我想重命名一系列以此方式命名的文件
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 在这两种情况下的行为方式不同。这是关于 Bash 版本4.3.48(1)
;这个问题似乎不再存在4.4.12(1)
(但是建议避免这种单行,因为在某些情况下 Bash 版本可能未知)。
正如 Kusalananda 答案中所建议的,如果sed
分隔符!
替换为#
,
$ for i in name\ \(* ; do mv "$i" "$(echo "$i" | sed -e 's# (\([0-9]\{2\}\))#_\1#')" ; done
这个问题根本不会出现。
答案1
当在交互式 shell 中使用时,!
可能会启动历史扩展(在脚本中不是问题)。
解决方案是使用表达式!
中以外的另一个分隔符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 中的 package,prename
RedHat 中的 package):它使用(更简单的恕我直言)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