【问题标题】:passing command line arguments from xargs从 xargs 传递命令行参数
【发布时间】:2014-06-11 01:30:15
【问题描述】:

我有一个执行以下操作的 shell 脚本:

INPUT_DIR 是一个包含 zip 文件的目录。 zip 文件看起来像 lograw1.zip、lograw2.zip 等。

我有一个名为 prog.py 的程序,它使用选项 -i 将输入作为 zip 文件并输出 tsv 文件。每个zip文件对应的tsv文件名称不同。

即 lograw1.zip 给出 logprocessed1.tsv 的输出

我有一个带有这个 for 循环的 shell 脚本,它运行良好。

for f in $INPUT_DIR/*.zip
  do
    filename=$(basename $f .zip)
    tsvfilename="$(basename "${f/raw/processed}" .zip).tsv"
    python /work/prog.py -i $f  $OUTPUT_DIR/$tsvfilename
  done

我想在 bash 脚本中使用 xargs 来读取 zip 文件并提交给我的 prog,对文件进行类似的重命名。如何使用 xargs 作为命令之间的参数发送? 提前致谢。

【问题讨论】:

  • 如果您的文件在其名称中包含空格,这所有都不能完美运行。使用更多引号!
  • ...更具体地说:basename $f .zip 必须是 basename "$f" .zip。 python /work/prog.py -i $f $OUTPUT_DIR/$tsvfilename 必须是 python /work/prog.py -i "$f" "$OUTPUT_DIR/$tsvfilename"。 $INPUT_DIR/*.zip 必须是 "$INPUT_DIR"/*.zip。

标签: bash shell sed command-line-arguments xargs


【解决方案1】:

xargs 通过从标准输入中获取文件名列表并使用从标准输入中检索到的所有文件运行命令来工作。如果标准输入的长度会溢出命令行缓冲区,xargs 将分割列表。

让我们做一些简单的事情:

ls | xargs /bin/echo

假设您的目录如下所示:

  • 富
  • 酒吧
  • barfoo
  • foobar
  • barbar
  • foofoo

ls 命令将输出:

foo bar barfoo foobar barbar foofoo

这将被传递给/bin/echo 并执行以下操作:

/bin/echo foo bar barfoo foobar barbar foofoo

现在,假设您的输入缓冲区只有 12 个字符长,将所有这些文件传递给 /bin/echo 将超出您的输入缓冲区。 xargs 将通过确保它不会向 /bin/echo 命令传递超过 10 个字符的数据来为您解决这个问题,并将一遍又一遍地重新执行 /bin/echo 命令,直到所有文件都传递给它:

/bin/echo foo bar
/bin/echo barfoo foobar
/bin/echo barber foofoo

您可以通过将-s10 参数传递给xargs 来测试这一点,这将限制xargs 将使用的字符数。你也可以试试-t,它会准确地回显xargs正在执行的内容。


本教程的原因是要了解,为了使xargs 工作,您的 shell 脚本中的每个命令都必须能够使用多个文件,但事实并非如此。 /work/prog.py 看起来一次只需要一个文件,basename 命令也是如此。

您必须修改脚本以利用 xargs 才能使其正常工作。可能通过使用for 循环来处理这个问题。

考虑将find 与您的shell 脚本一起使用,它可能会执行您想要的操作:

find . -name "*.zip" -exec script.sh {} \;

xargs 存在问题(默认情况下,它不处理带有时髦字符的文件名),并且有一个问题是它是否更快。毕竟:

rm *

仍然必须一次删除一个传递给它的文件,就像这个小脚本一样:

for file in *
do
    rm $file
done

在过去的 Unix 时代,启动一个新进程需要大量开销,因此如果您可以运行一次命令而不是多次,则可以节省您的时间。我不知道现在是否值得麻烦。

【讨论】:

  • xargs -n 1 command 会告诉 xargs 每次运行 command 时只使用一个参数。
  • @David W. 感谢 david,我可以使用 find 使其工作,但无法正确获取输出文件名。我要做的是:find testzips/ -name *zip |sed "s| (.*)|/work/prog.py -i \1 outputs/\1.out|"|xargs python
  • xargs 没有有有空白处理问题;它(或者,至少,广泛部署的变体)可以安全使用,这样做只需要小心。
  • @user1189851:根据您的sed 表达式,您可以直接执行替换后的命令,e 命令在sed 中。就像这样:find testzips/ -name *zip | sed 's=\(.*\)=python work/prog.py -i \1 outputs/\1.out=e'(注意末尾的e)。
  • ...真正正确的做法是将xargs -0 与NUL 分隔的字符串一起使用。可以肯定的是,sed 不能用于轻松创建这些字符串,但是一个 shell 循环(在一侧执行 read 而在另一侧执行 printf '%s\0' ...)可以。当然,如果您只是迭代 glob 结果,也不需要 read 结尾。
猜你喜欢
  • 2020-10-03
  • 2021-11-14
  • 1970-01-01
  • 2016-03-25
  • 1970-01-01
  • 2016-12-28
  • 1970-01-01
  • 1970-01-01
  • 2018-02-28
相关资源
最近更新 更多