【问题标题】:shell script ssh command exit statusshell 脚本 ssh 命令退出状态
【发布时间】:2013-03-01 17:19:56
【问题描述】:

在 shell 脚本的循环中,我连接到各种服务器并运行一些命令。例如

#!/bin/bash
FILENAME=$1
cat $FILENAME | while read HOST
do
   0</dev/null ssh $HOST 'echo password| sudo -S 
   echo $HOST 
   echo $?      
   pwd
   echo $?'
done

这里我正在运行“echo $HOST”和“pwd”命令,并且通过“echo $?”获得退出状态。

我的问题是我希望能够将远程运行的命令的退出状态存储在某个变量中,然后(根据命令是否成功)将日志写入本地文件。

感谢任何帮助和代码。

【问题讨论】:

    标签: shell ssh


    【解决方案1】:

    ssh 将使用远程命令的退出代码退出。例如:

    $ ssh localhost exit 10
    $ echo $?
    10
    

    所以在您的ssh 命令退出后,您可以简单地检查$?。您需要确保不掩盖您的返回值。例如,您的 ssh 命令以以下方式结束:

    echo $?
    

    这将始终返回 0。您可能想要的是更像这样的东西:

    while read HOST; do
      echo $HOST
      if ssh $HOST 'somecommand' < /dev/null; then
        echo SUCCESS
      else
        echo FAIL
    done
    

    你也可以这样写:

    while read HOST; do
      echo $HOST
      if ssh $HOST 'somecommand' < /dev/null
      if [ $? -eq 0 ]; then
        echo SUCCESS
      else
        echo FAIL
    done
    

    【讨论】:

    • 你是说我需要单独 ssh 来运行每个命令,以便提取它们的退出状态?我试图在我的真实代码中的同一个 ssh 会话中运行至少 5 个命令。
    • 嗨,@larsks。你能解释一下ssh localhost exit 10 是什么意思吗?这意味着使用明确的 10 作为状态码执行 ssh localhost?这里,exit 是 ssh 或 linux 命令 exit? 的一个选项
    • 记住ssh命令的基本语法是ssh &lt;hostname&gt; &lt;command&gt;。所以在上面的答案中,exit 10 是传递给远程 shell 的命令。
    • @larsks 是一个相当古老的答案,但仍然:如果您正在执行 10 多个命令而不仅仅是一个命令怎么办?捕获可能发生的错误并提前退出的最佳做法是什么?
    • 将您的命令粘贴到脚本中,然后 ssh remotehost bash &lt; your_script.sh,也许,如果出现错误,请确保您的脚本以适当的错误代码退出。 set -e 在这种情况下可能很有用,如果脚本中的任何命令失败,它会导致脚本中止并出现错误。尽管如果您在大量主机上运行大量命令,那么您可能是时候研究像 Ansible 或类似的工具了。
    【解决方案2】:

    一种有趣的方法是使用反引号在局部变量中检索每个 ssh 命令集的全部输出,甚至用特殊字符(为简单起见说“:”)分隔,例如:

    export MYVAR=`ssh $HOST 'echo -n ${HOSTNAME}\:;pwd'`
    

    在此之后,您可以使用 awkMYVAR 拆分为您的结果并继续 bash 测试。

    【讨论】:

      【解决方案3】:

      也许在另一端准备日志文件并将其通过管道传输到标准输出,如下所示:

      ssh -n user@example.com 'x() { local ret; "$@" >&2; ret=$?; echo "[`date +%Y%m%d-%H%M%S` $ret] $*"; return $ret; };
      x true
      x false
      x sh -c "exit 77";'  > local-logfile 
      

      基本上,只需使用此 x 包装器为要调用的远程所有内容添加前缀即可。它也适用于条件,因为它不会改变命令的退出代码。

      您可以轻松循环此命令。

      此示例将类似以下内容写入日志:

      [20141218-174611 0] true
      [20141218-174611 1] false
      [20141218-174611 77] sh -c exit 77
      

      当然,您可以使其更易于解析或根据您的喜好调整日志文件的外观。注意远程程序未被捕获的普通stdout被写入stderr(参见x()中的重定向)。

      如果您需要一个配方来捕获和准备日志文件的命令输出,这里是来自https://gist.github.com/hilbix/c53d525f113df77e323d 的此类捕获器的副本 - 但是是的,这是一个更大的样板,用于“在当前上下文中运行某些东西” shell,后处理 stdout+stderr 而不会干扰返回码”:

      # Redirect lines of stdin/stdout to some other function
      # outfn and errfn get following arguments
      # "cmd args.." "one line full of output"
      : catch outfn errfn cmd args..
      catch()
      {
      local ret o1 o2 tmp
      tmp=$(mktemp "catch_XXXXXXX.tmp")
      mkfifo "$tmp.out"
      mkfifo "$tmp.err"
      pipestdinto "$1" "${*:3}" <"$tmp.out" &
      o1=$!
      pipestdinto "$2" "${*:3}" <"$tmp.err" &
      o2=$!
      "${@:3}" >"$tmp.out" 2>"$tmp.err"
      ret=$?
      rm -f "$tmp.out" "$tmp.err" "$tmp"
      wait $o1
      wait $o2
      return $ret
      }
      
      : pipestdinto cmd args..
      pipestdinto()
      {
      local x
      while read -r x; do "$@" "$x" </dev/null; done
      }
      
      STAMP()
      {
      date +%Y%m%d-%H%M%S
      }
      
      # example output function
      NOTE()
      {
      echo "NOTE `STAMP`: $*"
      }
      
      ERR()
      {
      echo "ERR `STAMP`: $*" >&2
      }
      
      catch_example()
      {
      # Example use
      catch NOTE ERR find /proc -ls
      }
      

      示例见倒数第二行(向下滚动)

      【讨论】:

        【解决方案4】:

        您可以将退出状态分配给一个变量,就像这样做一样简单:

        variable=$?
        

        在您尝试检查的命令之后。不要在前面echo $?,否则$?的新值将是echo的退出码(通常为0)。

        【讨论】:

          猜你喜欢
          • 2017-08-15
          • 1970-01-01
          • 2017-03-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多