Shell脚本中使用SSH命令时的退出状态

26
在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 $?”获取退出状态。

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

感谢任何帮助和代码。

4个回答

39

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

$ ssh localhost exit 10
$ echo $?
10

当你的ssh命令退出后,你可以简单地检查$?。你需要确保不掩盖你的返回值。例如,你的ssh命令结束时为:

因此,在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

1
你是说我需要单独使用ssh运行每个命令,以便我可以提取它们的退出状态吗?我正在尝试在同一个ssh会话中运行至少5个命令。 - Kashif Usmani
嗨,@larsks。你能解释一下 ssh localhost exit 10 是什么意思吗?这是指执行带有显式状态码10的ssh localhost命令吗?在这里,exit是ssh或Linux命令exit的一个选项吗? - Gab是好人
请记住,ssh命令的基本语法是ssh <主机名> <命令>。因此,在上面的答案中,exit 10是传递给远程shell的命令。 - larsks
@larsks 的回答有些过时了,但问题仍然存在:如果您正在执行10个或更多命令而不仅仅是一个命令,那么最佳实践是什么,以便捕获可能发生的错误并提前退出? - trainoasis
将您的命令放入脚本中,然后 ssh remotehost bash < your_script.sh,如果出现错误,请确保您的脚本以适当的错误代码退出。在这种情况下,set -e 可能很有用,它会导致脚本在脚本中的任何命令失败时中止并显示错误。虽然如果您在许多主机上运行许多命令,那么现在可能是您应该考虑使用 Ansible 或类似工具的时候了。 - larsks

2
您可以简单地将退出状态分配给变量,如下所示:
variable=$?

在你尝试检查命令后的那一刻执行。不要在此之前使用echo $?,否则$?的新值将成为echo的退出码(通常为0)。

0
一个有趣的方法是使用反引号将每个ssh命令集的整个输出存储在本地变量中,甚至可以使用特殊字符(例如“:”)进行分隔,如下所示:
export MYVAR=`ssh $HOST 'echo -n ${HOSTNAME}\:;pwd'`

在这之后,你可以使用 awk 来将 MYVAR 分割成你的结果,并继续进行 bash 测试。

0

也许可以在另一端准备日志文件并将其管道传输到标准输出,像这样:

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
}

请看倒数第二行的示例(向下滚动)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接