我想通过 列出一堆文件名find
,通过一个实用程序(我们称之为util
)通过管道传输它们,该实用程序为每个输入名称输出一个新名称,然后将每个文件从旧名称重命名为新名称。
最基本的解决方案是这样的:
find . -print0 | while IFS= read -d '' -r old_name; do
new_name="$(echo "$file" | util)"
mv "$old_name" "$new_name"
done
这种方法的问题是速度util
太慢,无法单独启动每个文件名。因此,解决方案是util
仅启动一次并通过这个单一进程传输所有文件名:
find . -print0 >old_names
util <old_names >new_names
exec {old_fd}<old_names
exec {new_fd}<new_names
while IFS= read -d '' -r old_name <&$old_fd &&
IFS= read -d '' -r new_name <&$new_fd; do
mv "$old_name" "$new_name"
done
这只会启动util
一次,另一方面,它不再是管道:我们必须将所有文件列出到一个 tmp 文件中,util
在这个 tmp 文件上运行以获取另一个 tmp 文件,然后才真正开始重命名。 。
我尝试了以下方法以管道方式执行此操作:
mkfifo old_names new_names
find . -print0 | tee old_names | util >new_names &
exec {old_fd}<old_names
exec {new_fd}<new_names
while IFS= read -d '' -r old_name <&$old_fd &&
IFS= read -d '' -r new_name <&$new_fd; do
mv "$old_name" "$new_name"
done
不幸的是,这可能会死锁,具体取决于util
输入/输出缓冲的方式......
所以我的问题是:在 bash 中执行此操作的正确方法是什么?
答案1
不连接管道的解决方案:
find . -print > infiles
cat infiles | util > outfiles
parallel mv ::::+ infiles outfiles
优点:非常简单。缺点:重命名仅在util
完成后才开始。 2 个临时文件。
find . -print > infiles
cat infiles | util | parallel -j1 mv ::::+ infiles -
Pro:重命名将在util
开始命名时开始。缺点:1 个临时文件。
find . -print | util | parallel -j1 mv ::::+ <(find . -print) -
Pro:重命名将在util
开始命名时开始。缺点:不需要更改当前目录(否则两个find
s 可能不会给出相同的结果。)
答案2
根据转换 (util) 的复杂性,您也许可以使用重命名(Larry Wall 的重命名)。
或者
安排util
输出名称对:old-name
new-name
,然后
find . -print0 | util | xargs -n2 mv -T
或者
尝试这样的事情。
function a() {
echo -n "$1 "
echo "$1" | sed -e s/a/b/g
}
… | while read arg; do
a "$arg"
done