我有以下文件结构:
- 一些目录
- 一些文件.txt
- 这里还有另一个文件.log
- 还有一个文件.mp3
- 另一个目录
- 与其他一些 file.txt
- 根级别文件.txt
- 根级别的另一个文件.ext
我现在想做的是运行一个小脚本,该脚本将另一个文件作为输入,其中包含某种类型的模式/替换对,以根据它们递归地重命名这些文件。这样每个“another”(不区分大小写)都会被“foo”替换,或者每个“some”被“bar”替换。
我已经尝试了很多迭代文件和读取所述输入文件的方法,但是没有任何效果,我最终成功地意外覆盖了我的测试脚本。但有很多ls
、while
、sed
或mv
正在使用中。
我自己无法解决的两件事是如何处理文件名中的空格以及如何不处理在先前的模式匹配中已重命名的文件。
也许你可以给我指出正确的方向?
答案1
TOP="`pwd -P`" \
find . -type d -exec sh -c '
for d
do
cd "$d" && \
find . ! -name . -prune -type f -exec sh -c '\''
while IFS=\; read -r pat repl
do
rename "s/$pat/$repl/g" "$@"
N=$#
for unmoved
do
if [ -f "$unmoved" ]
then
set X ${1+"$@"} "$unmoved"
shift
fi
done
shift "$N"
case $# in 0 ) break ;; esac
done < patterns.csv
'\'' x \{\} +
cd "$TOP"
done
' x {} +
- 仅设置
find
网络目录并吞下sh
它们。这最大限度地减少了 的调用次数sh
。 find
在每个目录中设置网络regular
文件,深度级别仅为 1,然后将它们放入sh
gulp 中。这最大限度地减少了rename
实用程序被调用的次数。- 设置一个
while
循环来读入各个pattern <-> replacement
对并将它们应用于所有regular
文件。 - 在过程中
rename
-英我们会记录rename
处理后文件是否仍然存在。如果我们发现一个文件仍然存在,那么这意味着,由于某种原因,它无法重命名,因此将在下一次迭代中尝试pat/repl
。 OTOH,如果文件已成功重命名,那么我们不会pat/repl
通过将其从命令行参数列表中删除来对该文件应用下一次迭代。
答案2
rPairs="/tmp/rename_pairs" \
find . -type f -exec sh -c '
while read -r old new; do
rename "s/$old/$new/i" "$@"
done < "$rPairs"
' x {} +
假设您的重命名对文件中没有非 ASCII 字符,并且该文件远离搜索路径。
答案3
在拉克什·夏尔马(Rakesh Sharma)的回答之后,我在进行了更多尝试并睡了一会儿之后找到了正确的方向。
最后我想出了以下脚本:
#!/bin/bash
while IFS=";" read pattern replacement
do
if [[ ! -z $pattern ]]
then
echo "Checking files for pattern '$pattern'."
find ./files -name "*$pattern*" -type f | while read fpath
do
fname=$(basename "$fpath")
dname=$(dirname "$fpath")
echo " Found file '$fname' in directory '$dname'. Renaming to '${fname/$pattern/$replacement}'."
mv -- "$fpath" "$dname/${fname/$pattern/$replacement}"
done
fi
done < patterns.csv
它读取文件并循环遍历其填充和变量pattern.csv
的行。在第二步中,找到目录中与当前模式匹配的所有文件。必须这样做以避免在第二个模式匹配时再次尝试重命名文件,因为这会失败。最后,它仅重命名文件本身,而不是使用 shell 参数替换来重命名包含该文件的目录。$pattern
$replacement
./files
不起作用的是替换不区分大小写的匹配项,但我可以忍受。
答案4
要记住的重要一点是,遍历目录树是一个缓慢的过程,因此只执行一次。我们首先要做的是find
仅查看树中的目录。对于每个目录,我们查找regular files
它们下面的所有目录(这里没有递归)。然后,我们对这些文件名应用重命名转换,同时记录它是否成功。如果成功,那么我们将跳出 while 循环,从而阻止下一个 patt/repl 应用于该文件。
tempd="`mktemp -d`" \
find . -type d -exec sh -c '
cd "$1" && \
for f in ./*
do
[ -f "$f" ] || continue
while IFS=\; read -r patt repl
do
case $f in
./*"$patt"* )
rename -v "s/$patt/$repl/g" "$f" 2>&1 | tee "$tempd/$f"
case $(< "$tempf/$f") in "$f renamed "* ) break ;; esac ;;
esac
done < /tmp/patterns.csv
done
' {} {} \;