SSHはif条件でどのように機能しますか?


8

ifファイルを計算し、最新の3つのファイルを除くすべてを削除するステートメントがあります。しかし、このコマンドをリモートで実行したいと思います。条件と組み合わせるにsshはどうすればよいifですか?

私はこれを試しましたが、成功しませんでした。

#!/bin/bash

ssh -t  test@192.168.94.139 "cd /var/www/test.com/backup ;

if [ $(ls  | wc -l) -lt 3  ]
then
    echo "Less"
else [ $(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]
    echo "deleted"
fi"

私が得たエラー:

ls:* .tgzにアクセスできません:そのようなファイルまたはディレクトリはありません


次に、これをどのようにカスタマイズできますか?
Janith

@ user68186なぜですか?コマンドは引用符で囲みます。
オービットのライトネスレース

2
$( )コマンドの一部は、コマンドを開始する前にローカルシェルによって実行されsshます。$( )スタンドアロンの場合も、"s で囲まれた場合も同じです。ただし、s $( )内にある場合は'、ローカルシェルによって実行されません。
kasperd

回答:


3

注:ここでの質問には、実際には2つの層があります。1つは、「SSH経由でアクセス可能なリモートサーバーで重要なタスクを実行したい」です。もう1つは、「複雑な文字列をコマンドに渡そうとしているのですが、引数が意図したものと異なってしまう」です。私は、高レベルのアプローチを解決するために使用されるアプローチが「適切」(便利で、エラーが発生しにくい、安全など)であるかどうかについては議論せずに、低レベルの質問に答えています。他の回答とコメントに示されているように、そうではない可能性があります。

コマンドラインはほとんど正しいです。あなたは少し引用を変更する必要があります。

主な問題は、二重引用符で囲まれた文字列がローカルシェルによって展開されるため、$(...)ローカルシステムでパーツが評価されることです。それらをリモートシステムに渡すには、スクリプトを一重引用符で囲む必要があります。

また、引用符が埋め込まれています。元のスクリプトには、2つのechoの引数があります。外側の引用符を単一引用符に変更すると、awkスクリプトになります。これらは効果的に引用符を省略します。これはechosを気にしませんが、大なり記号が出力のリダイレクトになるため、awkスクリプトを混乱させます。したがって、外側の引用符を一重引用符に変更した後、二重引用符に変更します。

これは引用が修正されたスクリプトです。スクリプトに他の問題がある可能性があります。構文を修正しました。

#!/bin/bash

ssh -t  test@192.168.94.139 'cd /var/www/test.com/backup ;

if [ $(ls  | wc -l) -lt 3  ]
then
  echo "Less"
else [ $(ls -t *.tgz|awk "NR >3"|xargs rm -f) ]
  echo "deleted"
fi'

/var/www/test.com/backup「BACKUPDEST」という変数に割り当てたいのですが、cd $BACKUPDESTその方法を教えていただけますか?
Janith

1
どこに値を割り当てますBACKUPDESTか?これがリモート側で発生する場合は、通常どおりスクリプトに含めてください。ローカルで設定する場合(たとえば、ローカルスクリプトで計算するか、コマンドライン引数として渡す場合)、最初の行を次のように変更できます。たとえば"cd $BACKUPDEST"' ;、シェルは二重引用符で囲まれた部分を展開し、単一引用符で囲まれたままにします。一部はそのままで、2つを連結し、その結果を最後の引数としてに渡しますssh
pt314 2018

ああ、それを作る "cd '$BACKUPDEST'"' ;その変数のパスに変(スペースなど)が含まれている場合に備えてください。繰り返しますが、この方法で実行できると言っているのではなく、この方法で実行できると言っているだけです。
pt314 2018

10

はい、複雑なスクリプトを実行することができます ssh

#!/bin/bash -e

ssh_cmd="$(cat <<-EOF
    cd /var/www/test.com/backup;

    if [ \$(ls  | wc -l) -lt 3  ]; then
        echo "less"
    elif [ \$(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]; then
        echo "deleted"
    else
        echo "something else"
    fi
EOF
)"

ssh -t test@192.168.94.139 "$ssh_cmd"

この例では、bash hereドキュメントを使用してコマンド文字列を生成しています。いずれの場合も、変数の引用とエスケープが難しいため、sshを介してスクリプトを渡すとエラーが発生しやすくなります(コマンドの前にバックスラッシュを付けてください)。スクリプトが複雑になりすぎる場合は、スクリプトを経由してコピーscpし、ターゲットホストで実行することをお勧めします。

私はスクリプトを修正しようとしませんでしたが、リモートホストでのカウントと削除がどのように機能するかの例を次に示します。

#!/bin/bash -e

tmp_dir="$(mktemp -d)"

ssh_cmd="$(cat <<-EOF
    cd "$tmp_dir"

    cnt_tgz=(\$(find . -type f -name "*.tgz"))
    if [[ \${#cnt_tgz[@]} -lt 3 ]]; then
        echo "less"
    else
        rm "\${cnt_tgz[@]}"
        echo "deleted"
    fi
EOF
)"

touch "$tmp_dir/1.tgz"
ssh -t localhost "$ssh_cmd"
touch "$tmp_dir/2.tgz" "$tmp_dir/3.tgz"
ssh -t localhost "$ssh_cmd"

ls -t *.tgz以来、機能しませんグロブ、ローカルシステム上で起こっています。また、やディレクトリlsなどのエントリも返されるため、ファイルのカウントに使用することはお勧めできません。...


エラーが発生しました。 syntax error near unexpected token elif'elif [ $(ls -t |awk 'NR >3'|xargs rm -f) ]; then'
Janith

;最初のifステートメントに欠落がありました。しかし、私はあなたのスクリプトを修正しませんでした!さらに多くの問題があります。たとえば、ls *.tgz
Simon Sudler

1
「グロビングはローカルシステムでのみ発生しているため、ls -t * .tgzは機能しません。」-適切にエスケープすると、リモートシステムで発生します。元のバージョンは$(...)二重引用符で囲まれた文字列の中に裸だったので、ローカルシェルがそれを評価しました。\$(...)最初の例で示したようにそれをエスケープするか、スクリプト全体を一重引用符で囲むと、ローカルシェルがそれを展開できなくなり、リモートシステムに送信され、リモートシェルによって展開され、スクリプトが機能します。意図した通り。
pt314 2018

これは、ヒアドキュメントの代わりに一重引用符を使用することを強くお勧めする状況です。挿入する変数がいくつかある場合でも、一重引用符で囲まれた文字列を使用printfして変数を展開する方が得策です。それでも、すべてのシェル変数をエスケープするよりも管理が簡単です。また、catヒアドキュメントを変数にキャプチャするためだけに使用する必要がなくなります。
Daniel Pryden 2018

4

この複雑な引用文全体は、使用せずにスクリプトを使用するのに十分な証拠だと思います。複数のssh接続を避けたい場合は 、スクリプトを他のホストにパイプして、1つのコマンドで実行できるようにします。

ローカルファイル、と言うmyscript.sh

cd /var/www/test.com/backup;

if [ $(ls  | wc -l) -lt 3  ]; then
    echo "less"
elif [ $(ls -t *.tgz|awk 'NR >3'|xargs rm -f) ]; then
    echo "deleted"
else
    echo "something else"
fi

次に:

cat myscript.sh | \
    ssh -t test@192.168.94.139 \
    "cat - > /tmp/ms.sh && sh /tmp/ms.sh && rm /tmp/ms.sh"

または(猫の無用な使用を避ける):

ssh -t test@192.168.94.139 \
    "cat - > /tmp/ms.sh && sh /tmp/ms.sh && rm /tmp/ms.sh" \
    < myscript.sh

これはローカルスクリプトmyscript.shをリモート側にパイプし、そこで(一時的な)ファイル/tmp/ms.shにリダイレクトされて実行され、最後に削除されます。

注:元のスクリプトにエラーがないかチェックしませんでしたが、アイデアを示したかっただけです。エラーが発生しやすい引用符は不要で、スクリプト内のすべてのコマンドはリモート側で実行されます。


スクリプトを一時ファイルに保存する必要もありませんssh user@host bash <myscript.sh。適切なシェルにパイプするだけです。
pt314 2018

2
ただし、このリダイレクトは、スクリプト自体が標準入力から読み取ろうとしない場合にのみ機能することに注意してください。
chepner 2018


はい、リダイレクトには制限があります。また、予測可能な名前の一時ファイルの書き込み(および実行)に関連する一連のセキュリティ問題も回避できます。一時的なスクリプトを使用する場合は(選択または必要にかかわらず)、mktempまたは同様の安全なものを使用して作成します。
pt314 2018

3

スクリプトをリモートインスタンスに配置してsshを介して実行することをお勧めしますが、これを希望どおりに実行する方法を以下に示します。

#!/bin/bash

HOST='test@192.168.94.139'
REMOTE_PATH='/var/www/test.com/backup'

COMMAND1="ssh \"$HOST\" 'ls \"$REMOTE_PATH\" | wc -l'"
COMMAND2="ssh \"$HOST\" 'ls -t \"$REMOTE_PATH\"/*.tgz'"
COMMAND3="xargs ssh \"$HOST\" rm -rf"

if [[ $(eval "$COMMAND1") -le 3 ]]
then
    echo "Less"
else
    eval "$COMMAND2" | awk 'NR > 3' | eval "$COMMAND3" && echo "Deleted"
fi

ノート:

  • 条件式-ltはに置き換えられ-leます。
  • eval -引数を連結してコマンドを作成します。
  • 式の\"中にこれらの引用符が本当に必要かどうかはわかりませんが、追加$COMMAND{1..3}することにしました。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.