14

我正在尝试使用GNU parallel将大量文件发布到 Web 服务器。在我的目录中,我有一些文件:

file1.xml
file2.xml

我有一个看起来像这样的 shell 脚本:

#! /usr/bin/env bash

CMD="curl -X POST -d@$1 http://server/path"

eval $CMD

脚本中还有一些其他内容,但这是最简单的示例。我尝试执行以下命令:

ls | parallel -j2 script.sh {}

这是GNU parallel页面显示为对目录中的文件进行操作的“正常”方式。这似乎将文件名传递到我的脚本中,但 curl 抱怨它无法加载传入的数据文件。但是,如果我这样做:

find . -name '*.xml' | parallel -j2 script.sh {}

它工作正常。向我的脚本传递参数的方式ls和传递方式之间有区别吗?find还是我需要在该脚本中做一些额外的事情?

4

4 回答 4

9

GNUparallelxargs. 它们都有非常相似的界面,如果您正在寻找有关 的帮助parallel,您可能会更幸运地查找有关 的信息xargs

话虽如此,它们的操作方式都相当简单。使用它们的默认行为,两个程序都从 STDIN 读取输入,然后根据空格将输入分解为标记。然后将这些标记中的每一个作为参数传递给提供的程序。xargs 的默认设置是将尽可能多的令牌传递给程序,然后在达到限制时启动一个新进程。我不确定并行的默认设置是如何工作的。

这是一个例子:

> echo "foo    bar \
  baz" | xargs echo
foo bar baz

默认行为存在一些问题,因此通常会看到几种变化。

第一个问题是,因为空格用于标记化,任何包含空格的文件都会导致并行和 xargs 中断。一种解决方案是改为围绕 NULL 字符进行标记。find甚至提供了一个选项来使这很容易做到:

> echo "Success!" > bad\ filename
> find . "bad\ filename" -print0 | xargs -0 cat
Success!

-print0选项告诉find使用 NULL 字符而不是空格分隔文件。
-0选项告诉xargs使用 NULL 字符来标记每个参数。

请注意,这parallelxargs它的默认行为是仅围绕换行符进行标记要好一些,因此不需要更改默认行为。

另一个常见问题是您可能想要控制如何将参数传递给xargsor parallel。如果您需要将参数传递给程序的特定位置,您可以使用{}来指定参数的放置位置。

> mkdir new_dir
> find -name *.xml | xargs mv {} new_dir

这会将当前目录和子目录中的所有文件移动到 new_dir 目录中。它实际上分为以下几部分:

> find -name *.xml | xargs echo mv {} new_dir
> mv foo.xml new_dir
> mv bar.xml new_dir
> mv baz.xml new_dir

因此,考虑到如何xargsparallel工作,您应该希望能够看到您的命令的问题。find . -name '*.xml'将生成要传递给script.sh程序的 xml 文件列表。

> find . -name '*.xml' | parallel -j2 echo script.sh {}
> script.sh foo.xml
> script.sh bar.xml
> script.sh baz.xml

但是,ls | parallel -j2 script.sh {}会生成当前目录中所有文件的列表以传递给 script.sh 程序。

> ls | parallel -j2 echo script.sh {}
> script.sh some_directory
> script.sh some_file
> script.sh foo.xml
> ...

该版本的更正确变体ls如下:

> ls *.xml | parallel -j2 script.sh {}

但是,这与 find 版本之间的重要区别在于 find 将在所有子目录中搜索文件,而 ls 将仅搜索当前目录。find上述命令的等效版本ls如下:

> find -maxdepth 1 -name '*.xml'

这只会搜索当前目录。

于 2011-09-30T20:22:52.747 回答
4

由于它适用于您,find您可能希望查看 GNU Parallel 正在运行什么命令(使用 -v 或 --dryrun),然后尝试手动运行失败的命令。

ls *.xml | parallel --dryrun -j2 script.sh
find -maxdepth 1 -name '*.xml' | parallel --dryrun -j2 script.sh
于 2011-10-02T15:13:04.000 回答
3

我没有使用过,但&parallel之间有不同。将列出所有文件和目录,其中 as将仅列出以.xml结尾的文件(和目录)。 正如 Paul Rubel 所建议的,只需在脚本中打印 $1 的值即可进行检查。此外,您可能需要考虑仅使用该选项过滤文件的输入。 希望这可以帮助!lsfind . -name '*.xml'lsfind . -name '*.xml'
find-type f

于 2011-09-30T13:49:41.323 回答
2

整洁的。

我以前从未使用过并行。看起来,虽然有两个。一个是 Gnu Parrallel,安装在我的系统上的那个在手册页中将 Tollef Fog Heen 列为作者。

正如保罗所说,你应该使用 set -x

此外,您上面提到的范式似乎不适用于我的并行,相反,我必须执行以下操作:

$ cat ../script.sh
+ cat ../script.sh
#!/bin/bash
echo $@
$ parallel -ij2 ../script.sh {} -- $(find -name '*.xml')
++ find -name '*.xml'
+ parallel -ij2 ../script.sh '{}' -- ./b.xml ./c.xml ./a.xml ./d.xml ./e.xml
./c.xml
./b.xml
./d.xml
./a.xml
./e.xml
$ parallel -ij2 ../script.sh {} -- $(ls *.xml)
++ ls --color=auto a.xml b.xml c.xml d.xml e.xml
+ parallel -ij2 ../script.sh '{}' -- a.xml b.xml c.xml d.xml e.xml
b.xml
a.xml
d.xml
c.xml
e.xml

find 确实提供了不同的输入,它在名称之前添加了相对路径。也许这就是弄乱你的脚本的原因?

于 2011-09-30T13:59:30.840 回答