【问题标题】:xargs -n option to achieve parallelism and less processing timexargs -n 选项可实现并行性和更少的处理时间
【发布时间】:2018-04-03 15:03:29
【问题描述】:

我试图了解如何使用 xargs -n 选项循环遍历 csv 文件并运行 CURL 操作以将输出收集到文件以实现更快的处理时间。

示例:

我需要根据带有 URI(其中 1000 个)的 CSV 文件检查网页的运行状况。

URI.csv

signup
account
edit
close

我正在尝试并行检查它们的状态,使用:

猫 URI.csv | xargs -n1 -I {} /bin/bash -c 'curl -I http://localhost/{} &> /dev/null && echo "{},Online">>healthcheck.log || echo "{},Offline">>healthcheck.log '

我可以通过制作-n2 来加快处理速度吗?我知道我可以使用-P4 之类的东西来实现并行性,但是,我无法理解-n 如何用于我的用例。

【问题讨论】:

  • -n 单独不会做任何事情。并行性;为此,您可以将其与-P结合
  • 顺便说一句,在bash -c 参数中使用-I {} 然后{} 很容易导致shell 注入漏洞。永远不要那样做。 (想想如果你有一个包含$(rm -rf ~)的URI会发生什么)
  • 另外,foo && bar || baz 有时表现得像三元运算符,但它不是三元运算符——极端情况会杀死你。想一想如果foo 成功但bar 失败会发生什么——你最终可以运行baz 除了 bar

标签: bash xargs


【解决方案1】:

考虑以下代码:

xargs -d $'\n' -P2 -n2 \
  /bin/bash -c '
    for arg do
      if curl --fail -I "http://localhost/$arg" &>/dev/null; then
        printf "%s,Online\n" "$arg"
      else
        printf "%s,Offline\n" "$arg"
      fi
    done
  ' _ >>healthcheck.log <URI.csv
  • xargs -d $'\n' 告诉 GNU xargs 逐行操作,而不是将您的输入文件拆分为单词,尝试使用引号,或者使用比您可能实际想要的更复杂的解析。
  • xargs -P 2 指定一次运行两个进程。随意调整。
  • xargs -n 2 指定每个进程都有两个 URL 来运行。随意调整。
  • bash -c '...' _ arg1 arg2 运行脚本 ...$0 中的 _$1 中的 arg1$2 中的 arg2 等。因此,xargs 附加的参数成为位置参数for arg do 迭代的脚本。
  • foo &amp;&amp; bar || baz 的行为有点类似于if foo; then bar; else baz; fi,但并不完全相同。见BashPitfalls #22
  • 请注意,对于整个复合命令,我们只打开healthcheck.log 以写入一次,而不是每次我们想向其中写入一行时都重新打开文件。

【讨论】:

  • 感谢非常详细的回复,包括教我编写更好的 bash 脚本。它非常有用和赞赏。当我执行代码时,输​​出是在没有换行符的情况下写入的——我是否需要做任何事情才能在换行符中打印结果?谢谢!
  • 糟糕——我犯了在单引号内使用单引号的错误(用于保存整个脚本)。查看编辑,对格式字符串使用单引号(通常是一种不好的做法,因为格式字符串应该是恒定的,但对于这个来说足够安全)。
  • 感谢您耐心等待 shell 脚本开发新手:)。
  • 希望是最后一个问题——是否有可能将变量“传递”给 xargs?例如 -- localhost -- 我想将它设为 $host ,它会在脚本的开头进行初始化。
  • host=localhost xargs ... 将在命令执行期间导出环境变量 host。这并不特定于xargs——适用于任何命令。 (注意:请注意 foo=bar echo $foo 没有做你所期望的——那是因为 echo 不查看 foo 的环境,而是期望调用它的 shell 从局部变量执行扩展在调用之前)。或者你可以在你的脚本中初始化它之后export host;这也将使其可用。
【解决方案2】:

使用 GNU Parallel 它看起来像这样:

cat URI.csv |
  parallel -j100 'curl -I http://localhost/{} &> /dev/null && echo {}",Online" || echo {}",Offline"' >>healthcheck.log

或者更容易阅读:

#!/bin/bash

doit() {
  if curl -I http://localhost/"$1" &> /dev/null ; then
    echo "$1,Online"
  else
    echo "$1,Offline"
  fi
}
export -f doit
cat URI.csv | parallel -j100 doit >>healthcheck.log

-j100 调整为您希望并行运行的作业数。

通过使用 GNU Parallel,作业将并行运行,但 healthcheck.log 的输出将被序列化,您将永远不会看到竞争条件,即两个作业同时写入日志,这可能会弄乱日志文件。在这个例子中accountedit同时写道:

signup,Online
accouedit,Online
nt,Offline
close,Online

GNU Parallel 的输出永远不会发生这种情况。

【讨论】:

  • 如果您可以生成在直接情况下讨论的竞争条件,其中我们有一个带有单个 echoprintf 的循环(内容长度小于 4KB),默认行缓冲标准输出,我会很惊讶。
  • @CharlesDuffy 为什么要限制自己的内容长度小于 4KB?如果这是您的解决方案的限制,并且您知道这一点,那么也警告读者这一点不是正确的吗? (是的:您可以混合低于 4 KB:请参阅 gist.github.com/ole-tange/88ae153797748b3618e2433377e2870a
猜你喜欢
  • 2015-05-12
  • 2019-05-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
相关资源
最近更新 更多