Bash:交互式远程提示


16

我有一个脚本可以连接到远程服务器,并检查是否安装了某些软件包:

ssh root@server 'bash -s' < myscript.sh

myscript.sh:

OUT=`rpm -qa | grep ntpdate`
if [ "$OUT" != "" ] ; then
    echo "ntpdate already installed"
else
    yum install $1
fi

这个例子可以简化。这是myscript2.sh同样的问题:

read -p "Package is not installed. Do you want to install it (y/n)?" choise

我的问题是bash无法以交互方式读取我的答案。

有没有一种方法可以远程执行本地脚本而不丢失提示用户的能力?


你能详细说明吗?不清楚您要的是什么?您的其他代码会有所帮助。
slm

1
仅供参考,之所以不起作用,是因为您正在通过STDIN传递脚本。因此,当bash从STDIN读取时,它将获取您的脚本(或者什么也没有,因为它已经读取了整个脚本并且什么都没有了)。
Patrick

1
:与此相关的有用资源backreference.org/2011/08/10/...
SLM

Answers:


22

尝试这样的事情:

$ ssh -t yourserver "$(<your_script)"

所述-t的力的tty分配,$(<your_script)读取整个文件,在这种情况下,通过将内容作为一个参数ssh,这将是由远程用户的壳来执行。

如果脚本需要参数,请在脚本之后传递它们:

$ ssh -t yourserver "$(<your_script)" arg1 arg2 ...

为我工作,但不确定它是否通用。


3
那正是我在寻找的东西,谢谢!
安东尼·阿纳尼奇

好的解决方案,但是有一种方法可以将参数传递给your script,同时保留您在答案中使用的语法的优势?使用ssh -t yourserver "$(<your_script --some_arg)"只会导致bash: --some_arg: command not found
sampablokuper

1
@sampablokuper:尝试ssh ... $(<script) script_arg1 script_arg2 ...
Mat

@Mat,谢谢,WFM :)也许将其添加到您的答案正文中?
sampablokuper

3

您的问题是ssh在远程计算机上启动了登录的非交互式外壳程序。显而易见的简单解决方案是将脚本复制到远程服务器并从那里运行它:

scp myscript.sh root@server:/tmp && ssh root@server /tmp/myscript.sh

如果出于某种原因不能复制,我将修改脚本以首先连接并检查是否$1已安装,然后根据需要重新连接并安装:

OUT=$(ssh root@server rpm -qa | grep "$1");
if [ "$OUT" != "" ] ; then
    echo "$1 already installed"
else
   read -p "Package $1 is not installed. Do you want to install it (y/n)?" choice
   if [ "$choice" -eq "y" ]; then
       ssh root@server yum install "$1"
   fi
fi

那就是我所担心的。与ssh的连接是最长的操作,而我试图避免这种情况。
安东尼·阿纳尼奇

@AnthonyAnanich您可以通过建立主连接来节省连接建立时间。
吉尔斯(Gilles)'“ SO-不要邪恶”

@Gilles我当时想提出建议,但我认为这与我的建议不符。如果第一个ssh连接充当主服务器,它将在$OUT=$(ssh root@server rpm -qa | grep "$1");退出后关闭,然后第二个连接将花费与第一个相同的时间。我错了吗?
terdon

1
@terdon运行类似ssh -M foo.example.com sleep 99999999ssh -M foo.example.com read <somefifo作为master的主机,并在完成后显式杀死它(使用killecho done >somefifo)。
吉尔斯(Gillles)“所以别再作恶了”,

0

这是一个很好的解释

所以我将脚本修改为

hostname
echo -n "Make your choice :"
read choice
echo "You typed " ${choice}
echo done

这没有用。

因此我将脚本移至远程,以避免在ssh上进行本地重定向。(我的命令在名为f的文件中)

cat f | ssh user@remotehost.com 'cat >remf'
ssh user@remotehost bash remf

这工作了。这是输出:

christian@clafujiu:~/tmp$ ssh localhost bash tmp/f
christian@localhost's password: 
Linux clafujiu 2.6.32-52-generic #114-Ubuntu SMP Wed Sep 11 19:00:15 UTC 2013 i686 GNU/Linux
Sun Nov 10 14:58:56 GMT 2013
Make your choice :abc
You typed  abc
done

正如@terdon提到的,最初的意图是远程运行本地脚本,因此可以自动执行远程副本,这只是一行示例。

REMID=`cat f |ssh user@remotehost 'cat > remf_$$; echo $$'` ;ssh root@redtoadservices.com "bash remf_${REMID} ; rm -v remf_${REMID}"

2
它是如何“工作”的?您是否bash -s < script.sh像OP一样运行此脚本?您能在答案中包含解释而不是链接吗?
terdon

糟糕,对不起,我忘了添加一些关键内容。我也将脚本复制到远程以避免本地重定向。我将其添加到我的答案中以使其清楚。
X天

1
如果将脚本复制到远程,则问题将消失。OP的问题在于,他正在尝试向远程运行的本地脚本提供交互式输入。当然,您可以将其复制过来。OP希望在连接到远程服务器(大概是许多远程服务器)时运行此脚本。我怀疑复制它是否可行,除非您将其自动化。
terdon

0

过去,我已经多次寻找该问题的解决方案,但是从未找到完全令人满意的解决方案。用ssh管道传输会失去交互性。两个连接(scp / ssh)速度较慢,并且可能会留下您的临时文件。而且命令行上的整个脚本通常以逃避地狱而告终。

最近,我遇到了命令行缓冲区大小通常很大的情况(在我看来,'getconf ARG_MAX> 2MB)。这让我开始思考如何使用它并缓解转义问题。

结果是:

ssh -t <host> /bin/bash "<(echo "$(cat my_script | base64 | tr -d '\n')" | base64 --decode)" <arg1> ...

或使用here文档和猫:

ssh -t <host> /bin/bash $'<(cat<<_ | base64 --decode\n'$(cat my_script | base64)$'\n_\n)' <arg1> ...

我已经扩展了这个想法,以生成一个sshx可以正常运行的BASH示例脚本,该脚本可以运行任意脚本(而不仅仅是BASH),其中参数也可以是ssh上的本地输入文件。看这里


有趣,但是这比Mat的答案好吗?
斯科特,

1
Mat的答案很不错(我经常使用),但是它不能处理任意内容的脚本,例如"需要转义的字符。而且它仅限于BASH脚本。我的解决方案是针对任何已安装脚本语言的任何脚本内容的一般情况。此外,在sshx中,我进一步演示了序列化参数文件,这非常漂亮。
SourceSimian '19

(1)您能否发布Mat的答案失败(并且您的工作正常)的脚本的MCVE?(2)看到“ echo $(stuff)”或“ echo`stuff`”有什么问题? 您的echo "$(cat my_script | base64)" | base64 --decode外观与(-ish)相当cat my_script | base64 | base64 --decode,看起来很像无操作。
斯科特,

嗨@斯科特。(1):考虑脚本:echo "ARG1=$1, USER=$USER, END"。一)$ ssh host "$(<my_bash)" foo- > ARG1=, USER=user, END foo。b)$ ssh host bash "<(echo $(cat my_bash|base64) | base64 --decode)" foo-> ARG1=foo, USER=user, END。这里(a)有效运行:bash -c $'#!/bin/bash\necho "ARG1=$1, USER=$USER, END" foo'或类似的东西。请注意,arg不起作用。(b)在哪里运行:bash <(echo IyEvY...5EIgo= | base64 --decode) foo。(2)我使用base64作为传输编码。
SourceSimian '19
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.