Bash:连接管道而不会出现死锁

Bash:连接管道而不会出现死锁

我想通过 列出一堆文件名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开始命名时开始。缺点:不需要更改当前目录(否则两个finds 可能不会给出相同的结果。)

答案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

相关内容