【问题标题】:xargs sh -c 'ssh ...' stopping execution when any connection times outxargs sh -c 'ssh ...' 在任何连接超时时停止执行
【发布时间】:2015-08-15 08:49:08
【问题描述】:

我在 bash 中写了一小段代码来在我的服务器上吐出 openssl 的版本 (checking if I'm protected against this most recent openssl vulnerability),但是如果 SSH 无法连接并超时,它会阻止脚本的其余部分执行。我知道足够的 bash 来解决,但我不太确定这里需要做些什么来强制它继续,也许会困住 SIGTERM 并从我离开的地方继续?我敢肯定有一个更简单的方法..

命令如下:

cat servers.txt | \
  xargs -I {} sh -c "echo {} && ssh -o ConnectTimeout=3 myusername@{} openssl version"

servers.txt 只是一个 IP 地址的大列表,每行一个

【问题讨论】:

  • @CharlesDuffy 不知道 xargs,谢谢!
  • 您应该永远{} 直接扩展为 sh -c 或类似的脚本。这不安全。您应该将 {} 作为参数传递给 shell,并在 shell 脚本中使用适当的位置参数。 xargs 的手册页表明,如果内部进程以退出代码 255 退出,它应该只停止整个执行链。我不希望ssh 这样做,但也许它在这里。是否只是将|| exit 1 添加到脚本“修复”问题?
  • (在不同的点上,sh 是 POSIX sh,而不是 bash——即使 /bin/sh 是到 /bin/bash 的链接,它在运行时也会关闭一堆功能方式;只有shshell 是适当的标签,而不是明确 使用bash)。
  • ...btw,捕获 SIGTERM 无济于事,因为停止的决定是 在 xargs 内部,而 xargs 不是外壳的一部分——它完全是外部的命令,因此 shell 内的任何信号处理程序都不会改变其行为。
  • @CharlesDuffy 说得通

标签: shell ssh xargs


【解决方案1】:

来自xargs 手册页:

xargs 实用程序立即退出(不再进行任何处理 输入)如果无法组装命令行,则无法调用实用程序, 实用程序的调用由信号终止,或 实用程序以 255 的值退出。

大概您的失败以 255 的状态退出。


就个人而言,我不会为此烦恼xargs;你没有做任何事情(比如并行化,或者在命令行上放置多个主机名),这对它特别有用。

while read -r name <&3; do
  echo "$name"; ssh -o ConnectTimeout=3 username@"$name" "openssl version" ||:
done 3<servers.txt

这使用文件描述符 3 来读取 servers.txt 文件,因此 stdin 保留其默认值。


顺便说一句,如果我正在编写您的代码以使用 xargs,我会这样做:

xargs sh -c 'for host; do ssh -o ConnectTimeout=3 myusername@"$host" "openssl version" </dev/null' _ <servers.txt

这样您就可以让 xargs 将 主机列表 传递给每个 shell 调用,而不是每个主机一个 shell。

【讨论】:

  • 我会将循环更改为for name in $(&lt;servers.txt); do ... done,考虑到该文件仅包含空格分隔的列表(否则 xargs 会严重失败)。这种偏好的原因:在 ssh 下运行的命令可能会从 servers.txt 中吞噬您的标准输入。
  • @anishsane,这是一个合法的错误,但教人们违反mywiki.wooledge.org/DontReadLinesWithFor 是我很警惕的——经常有人会记住一个没有警告的习语。最好从 /dev/null 显式重定向标准输入,或者将循环放在标准输入以外的 FD 上。
  • 我现在就是这样做的,是的。实际上很想回去并将输入的 FD 号码移到其他地方(#3?)。
【解决方案2】:

使用专为此类工作而设计的 GNU Parallel:

parallel -j0 --retries 3 --slf servers --nonall --timeout 1000% --tag openssl version

您需要在 'servers' 中的行前添加 'myusername@' 或在 ~/.ssh/config 中使用 'User myusername'。

【讨论】:

    【解决方案3】:

    基本上你需要 NOHUP

    "nohup 命令名 &" 或

    "nohup /path/to/command-name arg1 arg2 &"

    您还序列化到 servers.txt,我想您可能会喜欢 在内容上运行 foreach(),在后台运行每个。

    但是,这会产生如何获得结果的次要问题? (提示:附加到一个固定的命名 results.log)

    【讨论】:

    • 问题不是 xargs 在退出时运行的 SSH 连接,而是 xargs 退出时创建的 SSH 连接。
    猜你喜欢
    • 2013-08-10
    • 2018-11-07
    • 2021-12-31
    • 1970-01-01
    • 2013-05-04
    • 2013-01-21
    • 1970-01-01
    • 2013-04-04
    • 2012-05-01
    相关资源
    最近更新 更多