ssh bash -c退出状态不会传播

时间:2019-01-25 20:25:15

标签: bash ssh

根据man sshthis previous answer,ssh应该传播它在远程服务器上运行的任何进程的退出状态。我似乎发现了一个神秘的异常!

$ ssh myserver exit 34 ; echo $?
34

好...

$ ssh myserver 'exit 34' ; echo $?
34

好...

$ ssh myserver bash -c 'exit 34' ; echo $?
0

什么?!?

$ ssh myserver
ubuntu@myserver $ bash -c 'exit 34' ; echo $?
34

因此问题似乎不是孤立的ssh或bash -c,但它们的组合不能像我期望的那样工作。

我正在设计要在远程计算机上运行的脚本,该脚本需要采用在客户端计算的参数列表。为了论证,假设如果任何一个论据都不是远程服务器上的文件,则它失败:

ssh myserver bash -c '
    for arg ; do
        if [[ ! -f "$arg" ]] ; then
            exit 1
        fi
    done
' arg1 arg2 ...

如何运行这样的内容并有效检查其退货状态?上面的测试似乎表明我不能。

3 个答案:

答案 0 :(得分:3)

问题是报价丢失了。 ssh只是连接参数,它不会重新引用它们,因此您实际上在服务器上执行的命令是:

bash -c exit 34

-c选项仅接受一个参数,而不接受其余所有参数,因此它仅执行exit34被忽略。

如果这样做,您会看到类似的效果:

ssh myserver bash -c 'echo foo'

它将仅回显空白行,而不是foo

您可以通过为ssh提供一个参数来解决此问题:

ssh myserver "bash -c 'exit 34'"

或加倍引号:

ssh myserver bash -c "'exit 34'"

答案 1 :(得分:1)

如果您的问题是如何在ssh的命令行中传递命令时远程运行命令,而又不会陷入触发有问题的错误的句柄中,则printf '%q '可用于要求shell对命令进行引用代表您,构建一个字符串,然后将其传递给ssh:

printf -v cmd_str '%q ' bash -c '
    for arg ; do
        if [[ ! -f "$arg" ]] ; then
            exit 1
        fi
    done
' arg1 arg2 ...
ssh "$host" "$cmd_str"

但是,只有在远程用户的默认外壳程序也是bash的情况下(或者,如果您在本地使用ksh的printf %q,如果远程外壳程序是ksh,才能保证此方法正确运行) )。带外传递脚本文本要安全得多,就像在stdin上一样:

printf -v arg_str '%q ' arg1 arg2 ...
ssh "$host" "bash -s $arg_str" <<'EOF'
for arg; do
  if [[ ! -f "$arg" ]]; then
    exit 1
  fi
done
EOF

...其中我们仍然依靠printf %q来生成正确的输出,但仅用于参数,而不是脚本本身。

答案 2 :(得分:0)

尝试用引号引起来:

╰─➤  ssh server "bash -c 'exit 34' "; echo $?
34