首页 > 解决方案 > SSH 命令输出中使用的 Grep 不返回 PIPESTATUS

问题描述

Grep 不会在脚本中返回管道状态,但会在 shell 中返回。

请参阅包含的脚本:

#!/usr/bin/env bash

function testSshConnection() {
  local SSH_CMD="ssh -o BatchMode=yes -o ConnectTimeout=3 $1@$2 2>&1 | grep \"Host key verification failed.\""
  echo "RUNNING: $SSH_CMD"
  $SSH_CMD
  PIPE_RESULT="${PIPESTATUS[0]} ${PIPESTATUS[1]}"
  echo "FUNCTION: PIPE_RESULT: ${PIPE_RESULT}"
  PIPE_RESULT_ARRAY=($PIPE_RESULT)
  PIPE0="${PIPE_RESULT_ARRAY[0]}"
  PIPE1="${PIPE_RESULT_ARRAY[1]}"
  local RC=${PIPE1:-$PIPE0}
  echo "Returning RC=$RC"
  return $RC
}

while ! testSshConnection root $1; do
  PIPE_RESULT="${PIPESTATUS[0]} ${PIPESTATUS[1]}"
  echo "WHILE: PIPE_RESULT: ${PIPE_RESULT}"  
  PIPE_RESULT_ARRAY=($PIPE_RESULT)
  PIPE0="${PIPE_RESULT_ARRAY[0]}"
  PIPE1="${PIPE_RESULT_ARRAY[1]}"
  WAIT=4
  echo "Waiting $SLEEP seconds for host $1 to be accessible on port 22"
  sleep $WAIT
done

exit

脚本输出:

~ $ /tmp/test.sh 46.101.7.220
RUNNING: ssh -o BatchMode=yes -o ConnectTimeout=3 root@46.101.7.220 2>&1 | grep "Host key verification failed."
Host key verification failed.
FUNCTION: PIPE_RESULT: 255
Returning RC=255
WHILE: PIPE_RESULT: 255
 Waiting 4 seconds for host 46.101.7.220 to be accessible on port 22
RUNNING: ssh -o BatchMode=yes -o ConnectTimeout=3 root@46.101.7.220 2>&1 | grep "Host key verification failed."
Host key verification failed.
FUNCTION: PIPE_RESULT: 255
Returning RC=255
WHILE: PIPE_RESULT: 255
...etc

在单独的 shell 中运行 ssh 命令的输出:

~ $ ssh -o BatchMode=yes -o ConnectTimeout=3 root@46.101.7.220 2>&1 | grep "Host key verification failed."
Host key verification failed.
~ $ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
255 0

为什么脚本中的 ssh 命令不为 grep 命令返回 ${PIPESTATUS[1]}?

标签: linuxbashshell

解决方案


您需要使用它eval来处理$SSH_CMD,就好像它是作为命令输入的一样。

eval "$SSH_CMD"

扩展变量后完成的唯一处理是分词和文件通配。它不处理转义符、管道、重定向、命令分隔符等。因此,当您执行该命令时,它相当于执行:

ssh -o BatchMode=yes -o ConnectTimeout=3 root@46.101.7.220 '2>&1' '|' grep '"Host' key verification 'failed."'

我已经引用了命令中包含 shell 元字符的部分,以表明在扩展变量后它们没有被特殊处理。

这样做的结果是命令“2>&1 | grep ... will be sent to the remote host. This will execute the commands2>&1 andgrep ...` 在服务器上,而不是在客户端上,管道也在服务器上完成。

对于一个更简单的例子,试试这个:

data="foo | wc"
echo $data

这将按字面意思打印foo | wc,它不会执行echo foo,然后将其通过管道传输到wc. 但如果你这样做:

eval "echo $data"

它会像你写的一样

echo foo | wc

有关详细信息,请参阅Bash 手册中的Shell 操作部分。处理像|>这样的元字符在此步骤中完成:

  1. 将标记解析为简单和复合命令(请参阅Shell 命令)。

扩展变量在下一步中完成:

  1. 执行各种 shell 扩展(请参阅Shell 扩展),将扩展的标记分解为文件名列表(请参阅文件名扩展)以及命令和参数。

由于在查找管道和重定向之后会发生变量扩展,因此这些操作不会在本地完成,它们将在 . 发送命令时在远程系统上完成ssh


推荐阅读