![在 Bash 中并行填充变量](https://linux22.com/image/216059/%E5%9C%A8%20Bash%20%E4%B8%AD%E5%B9%B6%E8%A1%8C%E5%A1%AB%E5%85%85%E5%8F%98%E9%87%8F.png)
find
我正在尝试利用并行化来加速访问多个硬盘驱动器上的文件的命令。不幸的是,要么忽略并行化,要么未填充变量。
found=""; IFS=$'\n'
for hdd in "${hdd_list[@]}"
do
found+=$'\n'$(find "$hdd" -name "*filter*" -type f &) # ignores parellelization
found+=$'\n'$(find "$hdd" -name "*filter*" -type f) & # doesn't fill variable
done
这是否可以在不诉诸临时文件的情况下实现?
答案1
命令替换等待从打开到命令内部的管道中获取所有数据,然后再继续。让 shell 在继续执行脚本的同时继续在后台读取数据将比必要的复杂得多。 (顺便说一句,这也意味着即使命令替换中的命令做了一些奇怪的事情,例如首先分叉并退出父级,当管道最终关闭时(如果确实如此),您仍然可以获得所有输出。)因此,将进程置于后台命令替换($(... &)
没有做任何有用的事情。
将整个分配放入后台 ( foo=... &
) 也不起作用,因为后台作业必须在单独的进程中运行,并且该后台进程无法更改主 shell 进程内存中的 shell 变量。
您可以安排将所有进程单独连接find
到管道,让它们并行运行和打印,但管道缓冲区只有这么大,这意味着您也需要同时读取所有进程。这在 shell 中很难做到,因为你没有select()
. (嗯,可能某些 shell 确实有它。)
但这一切都太复杂了,因为使用临时文件的简单解决方案就在那里。
如果您的find
实现很好,因为它只写入整行(条目),并且您不关心顺序,则可以将所有输出重定向到单个文件:
f=$(mktemp)
for hdd in "${hdd_list[@]}"; do
find "$hdd" ... &
done >> "$f"
# read "$f"
rm -f "$f"
但如果情况并非如此,或者您想确定,请为每个目录创建一个临时目录和一个输出文件find
:
d=$(mktemp -d)
i=1
for hdd in "${hdd_list[@]}"; do
find "$hdd" > "$d/out$i.tmp" &
i=$((i+1))
done
cat "$d"/*.tmp > "$d/all.out"
# read "$d/all.out"
rm -rf "$d"
当然,在第一个文件中,您可以跳过临时文件并直接从循环中读取。
由于您使用的是带有数组的 shell,因此您可能也希望将输出find
放入数组中。例如,readarray
在 Bash 中,这会将每一行放入不同的数组元素中:
readarray -t files < <(find ...)
答案2
parset
为此而构建:
parset hd find {} -name \""*filter*"\" -type f ::: "${hdd_list[@]}"
但是,它确实使用临时文件。它们已为您清理干净,因此您无需处理它们。
答案3
看来您可能不理解使用“&”的全部含义。
野兽的本性对于后台进程来说,你不能从父进程访问后台子进程内容...(无论变量是否被声明为全局变量)。
您遇到的问题与其他问题中概述的相同发帖。从本质上讲,临时文件是规避该问题的唯一方法。
答案4
特别是对于并行化find
,请查看fdfind
find
这是默认情况下多线程的改进版本。
如果您无法使用fdfind
,请尝试使用xargs
,它会并行运行命令。借用自这个问题,你可以尝试这样的事情(虽然我不是 100% 的,所以你可能需要在它起作用之前进行修改):
found="$( printf "%s\0" "${hdd_list[@]}" | xargs -0 -I {} find {} -name "*filter*" -type f )"
怎么运行的:
printf "%s\0" "${hdd_list[@]}"
:打印出以空字符作为分隔符的列表
| xargs -0 -I {} ...
:采用带有空分隔输入 ( -0
) 的 stdin 并调用后面的命令。它将找到 的所有实例{}
并将其替换为输入字段。