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から読み取りを行うと、スクリプトを取得します(または、スクリプト全体を既に読み取っており、何も残っていないため、何もありません)。
パトリック

1
これに関連した便利なリソース:backreference.org/2011/08/10/...
SLM

回答:


22

次のようなものを試してください:

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

-t力のtty割り当て、$(<your_script)ファイル全体を読み取り、この場合、コンテンツを1つの引数としてsshリモートユーザーのシェルによって実行されるに渡します。

スクリプトにパラメーターが必要な場合は、スクリプトの後にパラメーターを渡します。

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

私にとってはうまくいきますが、それが普遍的かどうかはわかりません。


3
それはまさに私が探していたものです、ありがとう!
アンソニーアナニッチ

良い解決策ですが、your script答えに使用した構文の利点を保持しながら、引数をに渡す方法はありますか?ssh -t yourserver "$(<your_script --some_arg)"だけを使用すると、になりbash: --some_arg: command not foundます。
サンパブロクパー

1
@sampablokuper:試してくださいssh ... $(<script) script_arg1 script_arg2 ...
マット

@Mat、ありがとう、WFM :)多分あなたの答えの本文にそれを追加しますか?
サンパブロクパー

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 マスター接続を設定することにより、接続確立時間を節約できます。
ジル 'SO-悪であるのをやめる'

@Gilles私はそれを提案することを考えていましたが、私の提案ではうまくいかないと思いました。1番目のssh接続がマスターとして機能する場合、終了後に閉じられ$OUT=$(ssh root@server rpm -qa | grep "$1");、2番目の接続には最初の接続と同じ時間がかかります。私が間違っている?
テルドン

1
@terdon ssh -M foo.example.com sleep 99999999またはのようなものをssh -M foo.example.com read <somefifoマスターとして実行し、完了したら明示的に(killまたはでecho done >somefifo)終了します。
ジル 'SO-悪であるのをやめる'

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が述べたように、リモートコピーは自動化できます。これはすべて1行の単なる例です。

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

2
どのように「機能」しましたか?bash -s < script.shOP と同じようにこのスクリプトを実行しましたか?答えに説明をリンクする代わりに含めることができますか?
テルドン

申し訳ありませんが、重要なものを追加するのを忘れてしまいました。また、スクリプトをリモートにコピーして、ローカルリダイレクトを回避しました。これを回答に追加して、明確にします。
X天

1
スクリプトをリモートにコピーすると、問題はなくなります。OPの問題は、リモートで実行されているローカルスクリプトにインタラクティブな入力を与えようとしていることです。もちろん、それをコピーして実行します。OPは、リモートサーバー、おそらくは多くのリモートサーバーに接続するときに、このスクリプトを実行したいと考えています。あなたがそれを自動化しない限り、私はそれをコピーすることが実用的であるとは思わない。
テルドン

0

私はこの問題の解決策を過去に数回検索しましたが、完全に満足できるものを見つけることはありませんでした。sshにパイピングすると、対話性が失われます。2つの接続(scp / ssh)は遅くなり、一時ファイルが横たわったままになる場合があります。そして、コマンドラインのスクリプト全体がしばしば地獄から逃げてしまいます。

最近、コマンドラインのバッファサイズが通常非常に大きいことに気付きました( 'getconf ARG_MAX>私が見たところ2MB)。そして、これにより、これをどのように使用し、エスケープの問題を軽減できるかについて考えました。

結果は次のとおりです。

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

またはヒアドキュメントと猫を使用して:

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

このアイデアを拡張してsshx、sshを介して引数がローカル入力ファイルでもある任意のスクリプト(BASHだけでなく)を実行できる完全に機能するBASHサンプルスクリプトを作成しました。こちらをご覧ください


興味深いですが、どのように、そして/またはいつこれがマットの答えよりも優れていますか?
スコット

1
マットの答えは良い最初のパス(私はよく使用します)ですが、"エスケープする必要のある文字など、任意のコンテンツのスクリプトを処理することはできません。また、BASHスクリプトに制限されています。私のソリューションは、インストールされているスクリプト言語のスクリプトコンテンツの一般的なケースです。さらに、sshxでは、引数ファイルのシリアル化をさらにデモンストレーションしますが、これは非常に便利です。
SourceSimian

(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"。a)$ 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'または類似したもの。引数が機能しないことに注意してください。(b)が実行されている場所:bash <(echo IyEvY...5EIgo= | base64 --decode) foo。(2)トランスポートエンコーディングとしてbase64を使用しました。
SourceSimian
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.