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
プロセスが長くなるからです。代わりに、リモートマシンでifelseを実行したいと思います。
私はいくつかのアプローチを試しましたが、運がありませんでした。
var=$(ssh $Host if [ -f /file ]\; then long pipeline1 \; else long pipeline2 \; fi)
これに基づいて answer は機能しますが、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の最後のコマンドは||
を引数と見なしました。
私も以下を試しました( this に基づいて)、これは機能しています:
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 ' ' '-'
Jetchisel からの回答によると、要するに、コマンドを一重引用符で囲む必要がありました。
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の複数のコマンド の重複ではないと思いますが、私の場合はより具体的ですが、他のスレッドは一般的にそれを尋ねます。
に:
ssh Host code
ssh
は、実際にシェル(ターゲットユーザーのログインシェル)を実行して、引数として渡したコードを解釈します。複数の引数が指定されている場合、sshはそれらをスペースで連結し、Host
上のユーザーのログインシェルにそれを解釈させます。
一般に、one単一コード引数をssh
に渡し、ローカルシェルによって展開が行われないように、単一引用符で引用されていることを確認します。
リモートユーザーのログインシェルがBourne/POSIXに似ていることがわかっている場合は、次のことを行うだけです。
var=$(ssh "$Host" '
if [ -f /file ]; then
pipeline1
else
pipeline2
fi'
)
リモートで解釈されるコードに一重引用符を含める必要がある場合は、それらを'\''
として挿入する必要があります(一重引用符を残し、引用符で囲まれた(円記号付き)一重引用符を挿入し、一重引用符を再入力します)。
リモートユーザーのシェルを保証できず、stdinを介してリモートコマンドにデータを渡す必要がない場合(および、他の何かにリダイレクトされない限り、リモートコマンドがstdinから読み取ることはありません) ssh接続よりも)、代わりに行うことができます:
ssh "$Host" sh << 'EOF'
if [ -f /file ]; then
pipeline1
else
pipeline2
fi
EOF
最初のEOF
を引用することにより、ローカルシェルによってヒアドキュメントで展開が行われないようにします。また、明示的にsh
を呼び出して、stdinのコードを解釈するため、スクリプトを記述する構文がわかります。
このアプローチでは、一重引用符をエスケープする必要もありません。
きみの
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'
(リモートユーザーのログインシェルが、たとえば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
私は数年前にこれに似た何かをしました、そしてこれが私がそれをした方法です。
まず、終了ステータスのテストを行います。
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を使用しているため、最後にexec bash -li
があります。