我想使用 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
最后有