“查找”和“ls”与 GNU 并行
Posted
技术标签:
【中文标题】“查找”和“ls”与 GNU 并行【英文标题】:"find" and "ls" with GNU parallel 【发布时间】:2011-11-28 10:59:54 【问题描述】:我正在尝试使用GNU parallel
将大量文件发布到网络服务器。在我的目录中,我有一些文件:
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
向我的脚本传递参数的方式有区别吗?还是我需要在该脚本中做一些额外的事情?
【问题讨论】:
您尝试使用 #!/bin/bash -x 运行,它会告诉您您的论点是否与您认为的不符。 发生这种情况时我总是很尴尬,但是当我第二天尝试重现这个问题(并按照建议使用 -x)时,我无法重现它并且一切正常。我每次都能成功使用 ls 或 find 。我想知道我是否以某种方式破坏了我的环境并且注销/登录清除了某些东西。 【参考方案1】:GNU parallel
是xargs
的变体。它们都有非常相似的界面,如果您在parallel
上寻求帮助,您可能会更幸运地查找有关xargs
的信息。
话虽如此,它们的操作方式都相当简单。使用它们的默认行为,两个程序都从 STDIN 读取输入,然后根据空格将输入分解为标记。然后将这些标记中的每一个作为参数传递给提供的程序。 xargs 的默认设置是将尽可能多的令牌传递给程序,然后在达到限制时启动一个新进程。我不确定并行的默认设置是如何工作的。
这是一个例子:
> echo "foo bar \
baz" | xargs echo
foo bar baz
默认行为存在一些问题,因此通常会看到几种变化。
第一个问题是因为空格是用来分词的,任何有空格的文件都会导致parallel和xargs中断。一种解决方案是改为围绕 NULL 字符进行标记。 find
甚至提供了一个选项让这件事变得简单:
> echo "Success!" > bad\ filename
> find . "bad\ filename" -print0 | xargs -0 cat
Success!
-print0
选项告诉find
使用 NULL 字符而不是空格分隔文件。-0
选项告诉 xargs
使用 NULL 字符来标记每个参数。
请注意,parallel
比 xargs
稍微好一点,因为它的默认行为是仅围绕换行符进行标记化,因此不需要更改默认行为。
另一个常见问题是您可能想要控制如何将参数传递给xargs
或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
考虑到xargs
和parallel
的工作方式,您应该希望能够看到您的命令存在的问题。 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 只会搜索当前目录。上述ls
命令的等效find
版本如下:
> find -maxdepth 1 -name '*.xml'
这只会搜索当前目录。
【讨论】:
【参考方案2】:由于它适用于 find
,您可能想查看 GNU Parallel 正在运行什么命令(使用 -v 或 --dryrun),然后尝试手动运行失败的命令。
ls *.xml | parallel --dryrun -j2 script.sh
find -maxdepth 1 -name '*.xml' | parallel --dryrun -j2 script.sh
【讨论】:
【参考方案3】:我没有使用过parallel
,但ls
和find . -name '*.xml'
之间存在差异。 ls
将列出 所有 文件和目录,而 find . -name '*.xml'
将仅列出以 .xml 结尾的文件(和目录)。
正如 Paul Rubel 所建议的,只需在脚本中打印 $1 的值即可进行检查。此外,您可能需要考虑使用 -type f
选项仅在 find
中过滤文件的输入。
希望这会有所帮助!
【讨论】:
【参考方案4】:整洁。
我以前从未使用过并行。看起来,虽然有两个。 一个是 Gnu Parrallel,安装在我的系统上的是 Tollef Fog Heen 在手册页中列为作者。
正如保罗所说,你应该使用 设置-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 确实提供了不同的输入,它在名称之前添加了相对路径。 也许这就是你的脚本搞砸的原因?
【讨论】:
以上是关于“查找”和“ls”与 GNU 并行的主要内容,如果未能解决你的问题,请参考以下文章