リモートホストで実行されているDockerコンテナの環境を取得したい場合があります。これを行うには、ホストにログインします。
ssh [email protected]
次に、次のコマンドを実行します。
sudo docker exec -it `sudo docker ps | grep mycontainername | awk '{print $1;}'` env
これを1つのコマンドで実行したいと思います(3回以上実行し、自動化したいです。.:-))。
これは機能します:
ssh -t [email protected] sudo docker ps | grep mycontainername
しかし、私がこれを行うとき
ssh -t [email protected] sudo docker exec -it `sudo docker ps | grep mycontainername | awk '{print $1;}'` env
私は得る
"docker exec" requires at least 2 arguments.
See 'docker exec --help'.
Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
Run a command in a running container
Connection to 123.456.789.10 closed.
誰かが私がこれを実行する方法を知っていますか?
Bashには、その後の構文解析に影響を与えるキーワードがいくつかあります[[
。しかしssh
、それらの1つではなく、通常のコマンドです。この意味は:
ssh …
通常、行全体がローカルシェルによって解析されます。以下のような文字|
、;
、*
、"
、$
またはスペース平均殻に何か、彼らはに取得することはできませんssh
あなたは(一部の例外を除いて、例えば唯一のそれらを引用したり脱出しない限り、$
別の単語としては特別なものではありません)。これは、構文解析と解釈の最初のレベルです。ssh
シェルがその仕事をした後に引数(または他の通常のコマンド)が何であれ、それらは単なる引数、文字列です。それらを解釈するのは今やツールの仕事です。これは第2レベルです。ssh
コマンドライン引数の一部(ゼロ、1つ以上)の場合、サーバー側で実行されるコマンドとして解釈されます。一般にssh
、多くの引数からコマンドを作成できます。その効果は、サーバーで次のようなものを呼び出したかのようです。
"$SHELL" -c "$command_line_built_by_ssh"
(これとまったく同じだとは言いませんが、何が起こるかを理解するのに十分近いです。シェルで呼び出されたかのように書いたので、見覚えがありますが、実際にはまだシェルがありません。いいえ$command_line…
、変数としては、この回答の目的で文字列を参照するためにこの名前を使用しています。)
次に$SHELL
、サーバー$command_line…
上で独自に解析します。これは第3レベルです。
失敗したコマンド
ssh -t [email protected] sudo docker exec -it `sudo docker ps | grep mycontainername | awk '{print $1;}'` env
が 123.456.789.10
有効なIPアドレスではないため、失敗しました 。
OK、123.456.789.10
プレースホルダーだと理解していますが、それでも有効ではありません。:)
コマンドはsudo docker ps | grep mycontainername | awk '{print $1;}'
ローカルで実行されたため失敗しました。出力はおそらく空でした。その後$command_line_built_by_ssh
、あなたが望んでいたものからはほど遠いものでした。
ローカルでssh … | grep mycontainername
実行するgrep
ことに注意してください(これに気付いている場合と気付いていない場合があります)。
リモートシェルがどのようになるかを制御するには$command_line_built_by_ssh
、以前に発生した解析と解釈を理解し、予測し、首謀する必要があります。あなたはそう、あなたの地元のコマンドを作る必要がある後ローカルシェルとssh
それが正確になり、それを消化し$command_line…
、リモート側で実行したいです。
結果がに到達する前に、実際にローカルシェルを拡張したり、何かを置き換えたりする場合は、非常に複雑になる可能性がありますssh
。必要な逐語的な文字列がすでにあるため、ケースはより単純です$command_line_built_by_ssh
。文字列は次のとおりです。
sudo docker exec -it $(sudo docker ps | grep mycontainername | awk '{print $1;}') env
ノート:
$()
、バックティックではなく、の形式でコマンド置換を使用しました。を好む理由$()
があります。docker
かどうかはわかりません$(…)
。一般的に、引用しないことはほとんどの場合悪いことです。置換によって複数の単語が返される(つまり、複数の行が入力されるawk
)とどうなるかを自問してください。これは別の問題であり(この場合は問題がある場合)、この回答では取り上げません。ローカルシェルによってすべてが展開/解釈されないように保護するには、展開\
をトリガーできる、または解釈できるすべての文字を適切に引用またはエスケープする必要があります。この場合引用$
、(
、)
、|
、;
、{
、}
、'
および(多分または必要に応じて)スペース。
私は、どのようにssh … some command
機能するかという理由で、「多分、またはオプションでスペースを引用またはエスケープする」と言いました。サーバー上で実行されるコードとして解釈される2つ以上の引数が見つかった場合、それらを連結し、間に単一のスペースを追加します。これが$command_line_built_by_ssh
構築方法です。リモートシェルのコードのように見えるスペースを引用もエスケープもしない場合、ローカルシェルは単語を分割するときにスペース(およびタブ)を消費し、スペースssh
を追加します。タブまたは複数の連続するスペースがある場合、結果は希望どおりにならない場合があります。例えば:
ssh user@server echo a b
ssh
取得user@server
、echo
、a
、b
。リモートコマンドは次のようになりますecho a b
し、echo
そこになりますa
、b
。印刷しますa b
。
次にこれ:
ssh user@server 'echo a b'
ssh
を取得user@server
しecho a b
ます。リモートコマンドは次のようになりますecho a b
し、echo
そこになりますa
、b
。印刷しますa b
。
そして最後にこれ:
ssh user@server 'echo "a b"'
ssh
を取得user@server
しecho "a b"
ます。リモートコマンドがありecho "a b"
、echo
そこにがありますa b
。印刷しますa b
。
結論として、ローカルシェルのコンテキストで引用し、リモートシェルのコンテキストで個別に引用する必要があります。シェルによって物事を拡張することになると、外側の引用符が重要になることに注意してください。
このすべての情報をまとめると(それでも、すべてをローカルシェルによって拡張/解釈されないように保護したい場合)、次のようにアドバイスします。
\
一つだけの文字を保護します。ほとんどの場合、すべてを保護するために多くの円記号が必要になります。多くの場合、1組の引用符で同じ結果を得ることができます。'
)、彼らはしかし、すべてを保護することができます'
。一方、二重引用符("
)を保護することができ'
なく$
も"
(また\
時には、また!
時にはバッシュで)、しない限り$
、そのようなも同様にエスケープされている(つまり、エスケープと引用された、つまりは二重引用符でエスケープ;以外!
である 面倒)。ssh
ます。これにより、次の手順が実行されます。
'
を'"'"'
またはで置き換え'\''
ます(それぞれに個別に選択できます'
)。ssh …
前に追加します。逐語的なコマンドは次のとおりです。
sudo docker exec -it $(sudo docker ps | grep mycontainername | awk '{print $1;}') env
手順の結果は次のとおりです。
ssh -t user@server 'sudo docker exec -it $(sudo docker ps | grep mycontainername | awk '\''{print $1;}'\'') env'
# single-quoted ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^
# escaped ^ ^
この手順では、可能な限り短い文字列が得られない場合があることに注意してください。洞察があれば、文字列を「最適化」できる場合があります。しかし、手順は非常に簡単で、完全に信頼できます。ローカルシェルによってすべてが拡張/解釈されないように保護したいことがわかっている場合は、手順自体にそれ以上の洞察は必要ありません。
実際、手順は自動化できます。文字列に引用符を追加し、既存の引用符を適切に保持できるツールがあります。Bash自体はそのようなツールです。私のこの答えは、Bashのキーストロークでこれを行う方法を提供します。あなたのケースに合わせて調整できる可能な解決策は次のとおりです。
ローカルシェルで、次のカスタム関数とバインディングを定義します。
_prepend_ssh() { READLINE_LINE="ssh ${READLINE_LINE@Q}"; READLINE_POINT=4; }
bind -x '"\C-x\C-h":_prepend_ssh'
ローカルシェルで、リモートシェルで実行するコマンドを入力(または貼り付け)します。実行しないでください。コマンドは、リモートシェルに配置したいものとまったく同じである必要があります。
ssh
、前に追加し、直後にカーソルを置きます。-t user@server
)を追加(型)します。便宜上、カーソルはすでにこれを行うのに適切な位置にあります。逐語的なコマンドをリモートシェルに渡す別の方法があります。場合によっては、を介してそれらをパイプできますssh
。リモートコマンドラインは次のようになっていると仮定しましょう。
echo "$PATH"; date
上記のように進め、一重引用符を追加して、次のようにローカルで実行できます。
ssh user@server 'echo "$PATH"; date'
例は単純ですが、一般的に引用符を追加するのは必ずしも簡単ではありません。または、次のようにコマンドをパイプすることもできecho
ます(簡単にするために;printf
より良い):
echo 'echo "$PATH"; date' | ssh user@server bash
それでもこれらの一重引用符が必要です。ただし、ファイルにコマンドがある場合は、次のようにします。
<file ssh user@server bash
または、ファイルがなくても(ヒアドキュメント):
ssh user@server bash <<'EOF'
echo "$PATH"
date
EOF
(の引用符はローカルで展開され<<'EOF'
ないように注意してください$PATH
。)
利点:
echo … ; date
これを示すために分割しました)。bash
かzsh
、またはをpython
)。短所:
exec "$SHELL"
(行は次のようになりますssh … 'exec "$SHELL"' <<'EOF'
)。ssh
端末ではないため、使用できません-t
(これが、元のコマンドを例として使用しなかった理由です)。bash
、標準入力を介してリモートインタープリター(例では)に到達します。考えられる問題:
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加