需要从文件中删除撇号。我尝试了几种方法,也来自堆栈交换。
我使用的是 Synology NAS,所以我没有 Python 或 Perl,而且我必须排除某些目录 (find -prune)。
以下 find 命令似乎在测试期间通常有效(使用 echo):
find . -depth \( -type d -name "@*" -o -name "*#recycle*" -o -name "*SynoResource*" -prune \) -o \( -name "*\'*" -execdir bash -c 'for f; do echo mv ${f/\\\'/\\\\'} `echo $f | sed 's/\\\'/X/g'`; done' _ {} + \)
对于像“911's.zip”这样的文件,将撇号 ' 替换为 X 会生成以下结果(使用上面的命令):
mv ./911's.zip ./911Xs.zip
当我删除“echo”以执行命令时,出现以下错误:
mv: target '911Xs.zip' is not a directory
在交互式 shell 中,我可以使用以下命令重命名该文件:
mv 911\'s.zip 911Xs.zip
当然,我尝试转义 ' (即使有几个 \,如${f/\'/X}
),但它不起作用。
任何想法?
多谢,
加里
答案1
几个问题:
- 您无法将
-prune
与结合起来,-depth
因为-depth
进程首先离开,因此-prune
在处理目录的全部内容之后,它来得太晚了,因此没有效果。 - 你的
-prune
放错地方了。你把它放在哪里,它只会适用于匹配的文件-name "*SynoResource*"
。相同,-type d
仅适用于-name "@*"
. - 您的参数扩展和命令替换缺少双引号,这会导致它们进行 split+glob。
echo
一般不能用于输出任意数据。- 不能有
'
内部单引号字符串。如果需要将'
字面意思传递给某个命令,则需要使用另一个 shell 引用运算符("..."
或反斜杠)并在单引号之外引用它。 '
对他们来说并不特殊mv
,sed
也find
没有必要逃避它。它仅对 shell 来说是特殊的,它是一个强引用运算符。- 您应该使用
$(...)
而不是不推荐使用的`...`
(当涉及反斜杠时,这也会增加更多的复杂性)。
所以在这里:
LC_ALL=C Q="'" find . -depth \
! -path '*/@*' \
! -path '*#recycle*' \
! -path '*SynoResource*' \
-name "*'*" -execdir bash -c '
for file do
mv -i -- "$file" "${file//$Q/X}"
done' bash {} +
在这里,由于-prune
不能与 一起使用-depth
,我们使用-path
(-wholename
在 GNU 中也称为find
)根据完整路径过滤掉这些排除目录中的文件。然而,这意味着它将find
进入那些从性能角度来看并不理想的目录。
使用-execdir
意味着您只重命名基本名称,这很好,但这也意味着您最终会在bash
每个包含带有'
s 的文件的目录中运行至少一个。或者,你可以这样做:
LC_ALL=C Q="'" find . -depth \
! -path '*/@*' \
! -path '*#recycle*' \
! -path '*SynoResource*' \
-name "*'*" -exec bash -c '
for file do
dir=${file%/*} base=${file##*/}
mv -i -- "$file" "$dir/${base//$Q/X}"
done' bash {} +
这使得它更高效,但在脚本运行时面对有人重命名文件和目录时不太安全。
对于空运行,您可以将该mv
命令替换为:
(PS4="Would run"; set -x; : mv -- "$file" "$dir/${base//$Q/X}")
echo
这比使用as更好,因为bash
会在跟踪输出中必要时使用引号,以使其明确。跟踪输出看起来就像是可用于运行相同命令的有效 shell 代码。
在那里,使用-exec
代替-execdir
也可以清楚地跟踪哪些文件将被重命名。
要使用但仍然首先处理目录深度,另一种方法是在输出上-prune
使用 GNU tac -s ''
(如果可用)(以及 GNU )来反转它:xargs
find -print0
LC_ALL=C find . -depth \
'(' -name '@*' -o \
-name '*#recycle*' -o \
-name '*SynoResource*' \
')' -type d -prune -o \
-name "*'*" -print0 |
tac -s '' |
Q="'" xargs -r0 bash -c '
for file do
dir=${file%/*} base=${file##*/}
mv -i -- "$file" "$dir/${base//$Q/X}"
done' bash {} +
没有 GNU tac
,xargs
但如果你的 bash 是 4.4 或更高版本,你也可以这样做:
LC_ALL=C find . -depth \
'(' -name '@*' -o \
-name '*#recycle*' -o \
-name '*SynoResource*' \
')' -type d -prune -o \
-name "*'*" -print0 |
Q="'" bash -c '
readarray -td "" files &&
for (( i = ${#files[@]} - 1; i >= 0; i-- )); do
file=${files[i]}
dir=${file%/*} base=${file##*/}
mv -i -- "$file" "$dir/${base//$Q/X}"
done' bash {} +
(请注意,我还没有测试过这些)。