如何 ssh 并运行包含在 if else 中的多个命令?

如何 ssh 并运行包含在 if else 中的多个命令?

我想使用 ssh 从各种服务器捕获一些数据,并在某些条件下运行一些命令。

我不想做:

if ssh $host test -f /file; then
  # If file exists
  var=$(ssh $host long pipeline)
else
  # if it doesn't
  var=$(ssh $host another long pipeline)
fi

因为这会让这个过程变得更长。我想在远程机器上执行 if else 操作。


我尝试了几种方法,但没有运气。

var=$(ssh $host if [ -f /file ]\; then long pipeline1 \; else long pipeline2 \; fi)

基于此回答,它可以工作,但 pipeline1 的最后一个命令假定该命令else和 pipeline2 的其余部分作为其参数。

command: can't read else: No such file or directory
...
command: can't read fi: No such file or directory

然后我尝试了这个

var=$(ssh $host test -f /file \&\& pipeline1 \|\| pipeline2)

同样,pipeline1 的最后一个命令被||视为其参数。


我也在下面尝试过(基于),正在运行:

do_this () {
  if [ -f /file ]; then
    pipeline1
  else
    pipeline2
  fi
}
var=$(ssh $host "$(set); do_this")

然而它会打印不需要的错误消息,这些消息不会影响我的变量,但对我的脚本来说很难看。

bash: line 1: BASHOPTS: readonly variable
bash: line 8: BASH_VERSINFO: readonly variable
bash: line 24: EUID: readonly variable
bash: line 71: PPID: readonly variable
bash: line 82: SHELLOPTS: readonly variable
bash: line 92: UID: readonly variable

有什么建议么?


更新

我想我必须包括我的管道是什么,基本上它只是一堆文本处理:

cat file | grep "something" | sed 's/.*="\(.*\)"/\1/' | tr ' ' '-'

根据来自的回答杰奇瑟尔,简而言之,我必须用单引号括起我的命令。

var=$(ssh $host 'if [ -f /file ]; then cat file | grep "something" | sed 's/.*="\(.*\)"/\1/' | tr ' ' '-' ; else cat otherfile | ... ; fi'

我有tr: invalid option -- ';'tr视为;其参数。


它的工作原理是heredoc

var=$(ssh $host <<-EOF
  if [ -f file ]; then
    pipeline1
  else
    pipeline2
  fi
EOF
)

然而,由于我在 sed 中使用的正则表达式,它破坏了 vim 的着色。我heredoc暂时接受这个答案。


更新2:我相信我的问题不是重复的sshpass 中的多个命令,我的情况比较具体,而另一个线程则一般性地询问。

答案1

在:

ssh host code

ssh实际上运行一个 shell(目标用户的登录 shell)来解释您作为参数传递的代码。如果给出多个参数,ssh 会将它们与空格连接起来,并再次让用户的登录 shell 对其host进行解释。

一般来说,你想通过单个代码参数ssh并确保用单引号引起来,以确保本地 shell 不会进行扩展。

如果您知道远程用户的登录 shell 是类似 Bourne/POSIX 的,那么您所要做的就是:

var=$(ssh "$host" '
  if [ -f /file ]; then
    pipeline1
  else
    pipeline2
  fi'
)

如果要远程解释的代码必须包含单引号,则需要将它们插入为'\''(保留单引号,插入带引号(带反斜杠)单引号,重新输入单引号)。

如果您不能保证远程用户的 shell,并且不需要通过其标准输入将数据传递给远程命令(并且没有任何远程命令从其标准输入读取,除非将其重定向到其他内容)比 ssh 连接),你可以这样做:

ssh "$host" sh << 'EOF'
  if [ -f /file ]; then
    pipeline1
  else
    pipeline2
  fi
EOF

通过引用第一个EOF,我们确保本地 shell 不会在此处文档中进行扩展。我们显式调用sh以解释其标准输入上的代码,以便我们知道使用什么语法编写脚本。

这种方法还避免了必须转义单引号。

你的

cat file | grep "something" | sed 's/.*="\(.*\)"/\1/' | tr ' ' '-'

可以写成:

<file sed '/something/!d; s/.*="\(.*\)"/\1/; y/ /-/'

所以这给了我们:

ssh "$host" '
  file=/path/to/some/file
  otherfile=/path/to/some/other/file

  if [ -f "$file" ]; then
    <"$file" sed '\''/something/!d; s/.*="\(.*\)"/\1/; y/ /-/'\''
  else
    <"$otherfile" ...
  fi'

(如果远程用户的登录 shell 是 csh、tcsh、fish、rc、es、akanga,其语法与 Bourne/POSIX 类似语法不同,则该命令将不起作用)

或者:

ssh "$host" sh << 'EOF'
  file=/path/to/some/file
  otherfile=/path/to/some/other/file

  if [ -f "$file" ]; then
    <"$file" sed '/something/!d; s/.*="\(.*\)"/\1/; y/ /-/'
  else
    <"$otherfile" ...
  fi
EOF

答案2

几年前我做过类似的事情,这就是我的做法。

首先测试退出状态。

ssh -t remoteuser@lremotehost 'if [[ -e /etc/fstabs ]]; then exit 0; else exit 127; fi' >/dev/null 2>&1

检查退出状态。

echo $?

输出是127因为没有/etc/fstabs但是/etc/fstab

现在更改/etc/fstabs/etc/fstab注意没有尾随s

ssh -t remoteuser@lremotehost 'if [[ -e /etc/fstab ]]; then exit 0; else exit 127; fi' >/dev/null 2>&1

再次检查退出状态

echo $?

输出是因为远程机器中0有输出。/etc/fstab

然后只需将其放入变量中并检查退出状态并根据它执行脚本。还将退出状态保存在变量中。

var=$(ssh -t remoteuse@remotehost 'if [[ -e /etc/fstabs ]]; then exit 0; else exit 127; fi; exec bash -li' >/dev/null 2>&1); pid=$?

case $pid in
   0) echo 'good!';;
   *) echo 'bad!' >&2;;
esac

这就是我在这种情况下幸存下来的方式,我并不是说这是一个完美的解决方案,但你可以检查一下。顺便说一句,我已将 ssh 密钥转发到远程主机,因此当我通过 ssh 登录时不会进行密码检查。我还在两台机器上使用 bash 作为登录 shell,这就是为什么我exec bash -li最后有

相关内容