私はファイル名のドットを置き換えてアンダースコアに置き換えようとするbashスクリプトを持っていますが、拡張子はそのままにしています(私はCentos 6を使用しています)。以下の出力からわかるように、置換するドットがある場合でもスクリプトは機能しますが、ドットのみが拡張子である場合、スクリプトはファイルを無視するのではなく、ファイルの名前を変更しようとします。誰かがこれをよりよく処理する方法を指摘できますか?助けてくれてありがとう。
私の(欠陥のある)スクリプト:
#!/bin/bash
for THISFILE in *
do
filename=${THISFILE%\.*}
extension=${THISFILE##*\.}
newname=${filename//./_}
echo "mv $THISFILE ${newname}.${extension}"
#mv $THISFILE ${newname}.${extension}
done
入力例:
1.3MN-Pin-Eurotunnel-Stw505.51.024-EGS-130x130.jpg
Wear-Plates.jpg
出力:
mv 1_3MN-Pin-Eurotunnel-Stw505_51_024-EGS1-130x130.jpg 1_3MN-Pin-Eurotunnel-Stw505_51_024-EGS1-130x130.jpg
mv Wear-Plates_jpg.Wear-Plates_jpg Wear-Plates_jpg.Wear-Plates_jpg
私はこのプログラムがあなたが望むことをすると信じています。私はそれをテストしました、そしてそれはいくつかの興味深いケース(全く拡張がないなど)で動作します:
#!/bin/bash
for fname in *; do
name="${fname%\.*}"
extension="${fname#$name}"
newname="${name//./_}"
newfname="$newname""$extension"
if [ "$fname" != "$newfname" ]; then
echo mv "$fname" "$newfname"
#mv "$fname" "$newfname"
fi
done
あなたが持っていた主な問題は、##
拡張が意図したとおりに行われていません。私は常にbashでのシェルパラメーターの展開を黒人芸術のようなものだと考えてきました。マニュアルの説明は完全に明確ではなく、拡張がどのように機能するかのサポート例がありません。それらはまたかなり不可解です。
個人的には、sed
で小さなスクリプトを書いて、名前を思い通りにいじったり、Perl
で小さなスクリプトをすべて書いたりしました。答えた他の人の一人がそのアプローチを取った。
もう1つ指摘したいのは、引用の使い方です。シェルスクリプトで何かをするたびに、引用には細心の注意を払う必要があることを思い出させます。シェルスクリプトの問題の大きな原因は、シェルが想定外のことを解釈することです。そして、引用規則は明白からほど遠いです。このシェルスクリプトには引用問題がないと思います。
for thisfile in *.*.*
を使用します(つまり、名前に2つ以上のドットがあるファイルをループします)。変数を引用符で囲み、--
を使用してオプションの終わりをmv -- "$thisfile" "$newname.$extension"
のようにマークしてください
Zshを使用。
autoload -U zmv
zmv '(*).(*)' '${1//./_}.$2'
これはどう:
Perl -e '
@files = grep {-f} glob "*";
@old_files = @files;
map {
s!(.*)\.!$1/!;
s!\.!_!g;
s!/!.!
} @files;
rename $old_files[$_] => $files[$_] for (0..$#files)
'
免責事項:最初にダミーディレクトリで試してください。私はテストしていません。
いくつかの良い回答がすでに利用可能になっているようですが、tr
とsed
を使用した別の回答があります:
#!/bin/bash
for file in *; do
newname=$(echo $file | tr '.' '_' | sed 's/\(.*\)_\([^_]*\)$/\1.\2/g')
[ "$newname" != "$file" ] && mv "$file" "$newname"
done
このバージョンでは、右側から始めて、保持したいドットの数を明示的に選択できます。
また、ドットに加えて他の文字を置換または削除します。置換文字は-
アンダースコアの代わりに、これは簡単に変更できます。
#!/bin/sh
# Rename files by replacing Unix-unfriendly characters.
usage () {
cat <<EOF
usage: $0 [OPTIONS] [--] [FILE [FILE...]]
Rename files by replacing Unix-unfriendly characters.
Options:
-p N preserve last N dots in filename, or keep all
dots if N < 0 (default: 1)
--help show this help and exit
EOF
}
error () {
printf "%s\n" "$1" 1>&2
}
delete_chars="()[]{}*?!^~%\\\<>&\$#|'\`\""
replace_chars=" _.,;-"
unixify_string () (
printf '%s\n' "$1" \
| tr -d "$delete_chars" \
| tr -s "$replace_chars" - \
| to_lower \
| sed 's/^-\(.\)/\1/; s/\(.\)-$/\1/'
)
to_lower () {
sed 's/.*/\L&/'
}
split () (
# split '.x.x.x.x' 0 -> '/x.x.x.x.x
# split '.x.x.x.x' 1 -> '/x.x.x.x/x
# split '.x.x.x.x' 2 -> '/x.x.x/x/x
# split '.x.x.x.x' -1 -> '/x/x/x/x/x
nf=$(printf '%s\n' "$1" | tr -d -C . | wc -c)
if [ $2 -lt 0 ]; then
keep=0
else
keep=$((nf-$2))
fi
IFS=. i=0 out= sep=
for part in $1; do
out="$out$sep$part"
if [ -z "$out" -o $i -ge $keep ]; then
sep=/
else
sep=.
fi
i=$(($i+1))
done
printf '%s\n' "$out"
)
unixify () (
IFS=/ out= sep=
for part in $(split "$1" $2); do
out="$out$sep$(unixify_string "$part")"
sep=.
done
printf '%s\n' "$out"
)
rename_maybe () (
dir="$(dirname "$1")"
name="$(basename "$1")"
newname="$(unixify "$name" $2)"
if [ "$newname" != "$name" ]; then
mv -i "$dir/$name" "$dir/$newname"
fi
)
# command line arguments
short_opts=p:
long_opts=help
args="$(LC_ALL=C getopt -n "$0" -s sh -o $short_opts -l $long_opts -- "$@")"
if [ $? -eq 0 ]; then
eval set -- "$args"
else
exit 1
fi
p=
while [ $# -gt 0 ]; do
case "$1" in
--help)
usage; exit 0 ;;
-p)
p="$2"; shift
if ! [ "$p" -eq "$p" ] 2> /dev/null; then
error "$0: option requires integer argument -- 'p'"
exit 1
fi ;;
--)
shift; break ;;
-*)
error "$0: illegal option -- '$1'"
exit 1 ;;
*)
break
esac
shift
done
# defaults
p=${p:-1}
# echo p=$p
# echo "$@"
# echo n=$#
# exit
if [ $# -lt 1 ]; then
error "$0: required non-option argument missing"
exit 1
fi
for file in "$@"; do
rename_maybe "$file" $p
done