使用 parallel 和引用文件名列表作为输入调用 shell 函数

calling shell function using parallel with list of quoted filenames as input

使用 Bash.

我有一个导出的 shell 函数,我想将其应用于许多文件。

通常我会使用 xargs,但像这样的语法(参见 here)太难用了。

...... | xargs -n 1 -P 10 -I {} bash -c 'echo_var "$@"' _ {}

在那次讨论中,parallel 有一个更简单的语法:

..... | parallel -P 10 echo_var {}

现在我 运行 遇到了以下问题:我要应用我的函数的文件列表是一行文件列表,每个文件都用 spaces 引用和分隔因此: "file 1" "file 2" "file 3"

如何将这个 space 分隔、引用、列表输入 parallel

我可以使用 echo 复制列表进行测试。

例如

echo '"file 1" "file 2" "file 3"'|parallel -d " " my_function {}

但我无法让它工作。

我该如何解决?

How can I fix it?

您必须选择一个唯一的分隔符。

echo 'file 1|file 2|file 3' | xargs -d "|" -n1 bash -c 'my_function "$@"' --
echo 'file 1^file 2^file 3' | parallel -d "^" my_function

最安全的是使用零字节作为分隔符:

echo -e 'file 1\x00file 2\x00file 3' | xargs -0 ' -n1 bash -c 'my_function "$@"' --
printf "%s[=11=]" 'file 1' 'file 2' 'file 3' | parallel -0 my_function

最好的方法是将元素存储在 bash 数组中,并使用零分隔流来处理它们:

files=("file 1" "file 2" "file 3")
printf "%s[=12=]" "${files[@]}" | xargs -0 -n1 bash -c 'my_function "$@"' --
printf "%s[=12=]" "${files[@]}" | parallel -0 my_function

请注意,空数组将 运行 函数不带任何参数。当输入为空时,有时首选使用 -r --no-run-if-empty 选项而不是 运行 函数。 --no-run-if-emptyparallel 支持,并且是 xargs 中的 gnu 扩展(BSD 上的 xargs 和 OSX 上没有 --no-run-if-empty)。

注意:xargs 默认解析 '"\。这就是为什么以下是可能的并且会起作用的原因:

echo '"file 1" "file 2" "file 3"' | xargs -n1 bash -c 'my_function "$@"' --
echo "'file 1' 'file 2' 'file 3'" | xargs -n1 bash -c 'my_function "$@"' --
echo 'file\ 1 file\ 2 file\ 3' | xargs -n1 bash -c 'my_function "$@"' --

它可能会导致一些奇怪的事情,所以请记住几乎总是为 xargs 指定 -d 选项:

$ # note \x replaced by single x
$ echo '\a\b\c' | xargs
\abc
$ # quotes are parsed and need to match
$ echo 'abc"def' | xargs
xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option
$ echo "abc'def" | xargs
xargs: unmatched single quote; by default quotes are special to xargs unless you use the -0 option

xargs 是随处可用的便携工具,而 parallel 是 GNU 程序,必须单独安装。

问题归结为值可以包含space,而space是值分隔符。所以我们需要一些可以将输入解析为包含 space 的单独值的东西。由于它们被 bash 引用,因此显而易见的选择是使用 bash 取消对值的引用。

您有多种选择:

(echo "file 1";
 echo "file  2";
 echo "file \"name\" $(3)") | parallel my_function

printf "%s\n" "file 1" "file  2" "file \"name\" $(3)" |
  parallel my_function

如果输入在变量中:

var='"file 1" "file  2" "file \"name\" $(3)"'
eval 'printf "%s\n" '"$var" |
  parallel my_function

或者您可以将变量转换为数组:

var='"file 1" "file  2" "file \"name\" $(3)"'
eval arr=("$var")

如果输入在数组中:

parallel my_function ::: "${arr[@]}"