このように名前の付いた一連のファイル名を変更したい
name (10).ext
name (11).ext
...
到着
name_10.ext
name_11.ext
...
この行の役割は次のとおりです。
$ for i in name\ \(* ; do echo "$i" | sed -e 's! (\([0-9]\{2\}\))!_\1!' ; done
name_10.ext
name_11.ext
...
しかし、これはそうではありません:
$ for i in name\ \(* ; do mv "$i" "$(echo "$i" | sed -e 's! (\([0-9]\{2\}\))!_\1!')" ; done
bash: !_\1!': event not found
なぜ?この状況を避ける方法は?
使用
$ bash --version
GNU bash, versione 4.3.48(1)-release (x86_64-pc-linux-gnu)
Ubuntu 18.04で。
そしてここエマルジョン!
!
内部単一引用符を考慮し、!
内部一重引用符、内部二重引用符と比較する簡単なケースを示します。コメントで指摘したように、Bashはこれら2つのケースで異なる動作をします。これはBashバージョンに関するものです4.3.48(1)
。問題はもう存在しないようです4.4.12(1)
(しかし、場合によってはBashのバージョンがわからないので、この1行の内容は避けることをお勧めします)。
Kusalanandaの回答で提案したようにsed
区切り記号を!
に変更すると#
、
$ for i in name\ \(* ; do mv "$i" "$(echo "$i" | sed -e 's# (\([0-9]\{2\}\))#_\1#')" ; done
この問題はまったく発生しません。
答え1
対話型シェルで使用すると、!
履歴拡張が有効になる可能性があります(スクリプトでは問題ありません)。
解決策は、式の外!
に別の区切り文字を使用することですsed
。
別の種類のbash
ループ:
for name in "name ("*").ext"; do
if [[ "$name" =~ (.*)" ("([^\)]+)").ext" ]]; then
newname="${BASH_REMATCH[1]}_${BASH_REMATCH[2]}.ext"
printf 'Would move %s to %s\n' "$name" "$newname"
# mv -i "$name" "$newname"
fi
done
答え2
Larry Wallのrename
コマンド(rename
Debianのパッケージ、prename
RedHatのパッケージ)を使用できます。このコマンドは(より簡単なIMHO)Perl正規表現構文を使用し、ループコードを書かずにargsファイルを繰り返します。
rename 's/ \(\d+\)/_$1/' name\ *
答え3
正しい範囲がわかっている場合は、次のようにファイル名を変更できます。
for i in {10..99}; do mv "name ($i).ext" "name_$i.ext"; done
またはPOSIXの専門家になりたい場合:
for i in `seq 10 99`; do mv "name ($i).ext" "name_$i.ext"; done