リモートサーバー上にディレクトリをいくつか作成してからscpを使用して自分のローカルマシンからリモートにファイルをコピーするシェルスクリプトを作成しようとしています。これが私がこれまでに持っているものです:
ssh -t user@server<<EOT
DEP_ROOT='/home/matthewr/releases'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR=$DEP_ROOT"/"$datestamp
if [ ! -d "$DEP_ROOT" ]; then
echo "creating the root directory"
mkdir $DEP_ROOT
fi
mkdir $REL_DIR
exit
EOT
scp ./dir1 user@server:$REL_DIR
scp ./dir2 user@server:$REL_DIR
実行するたびに、このメッセージが表示されます。
Pseudo-terminal will not be allocated because stdin is not a terminal.
そしてスクリプトは永久にハングします。
私の公開鍵はサーバー上で信頼されており、スクリプトの外部ですべてのコマンドを実行することができます。何か案は?
Stdinが端末でなくても、擬似tty割り当てを強制するためにssh -t -t
(または略してssh -tt
)を試してください。
以下も参照してください。 bashスクリプトによって実行されたSSHセッションの終了
Sshのmanページから:
-T Disable pseudo-tty allocation.
-t Force pseudo-tty allocation. This can be used to execute arbitrary
screen-based programs on a remote machine, which can be very useful,
e.g. when implementing menu services. Multiple -t options force tty
allocation, even if ssh has no local tty.
manual からのオプション-T
も
擬似端末の割り当てを無効にする
zanco's answer 、シェルがコマンドラインを解析する方法を考えると、ssh
にリモートコマンドを提供していません。この問題を解決するには、ssh
コマンド呼び出しの構文を変更して、リモート・コマンドが構文的に正しい複数行のストリングで構成されるようにしてください。
使用できるさまざまな構文があります。たとえば、コマンドをbash
とsh
、そしておそらく他のシェルにもパイプ接続できるため、最も簡単な解決策は、ssh
シェル呼び出しとheredocsを組み合わせることです。
ssh user@server /bin/bash <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
上記のwithout/bin/bash
を実行すると、警告Pseudo-terminal will not be allocated because stdin is not a terminal
が発生することに注意してください。また、EOT
は一重引用符で囲まれているため、bash
はheredocをnowdocとして認識し、コマンドテキストがそのままssh
に渡されるようにローカル変数の内挿をオフにします。
あなたがパイプのファンなら、あなたは次のように上記を書き換えることができます:
cat <<'EOT' | ssh user@server /bin/bash
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
/bin/bash
に関する同じ警告が上記にも当てはまります。
別の有効な方法は、複数行のbash
変数内挿を次のように使用して、複数行のリモートコマンドを単一の文字列として渡すことです。
ssh user@server "$( cat <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
)"
上記の解決策は、次のようにしてこの問題を解決します。
ssh user@server
はbashによって解析され、ssh
コマンドであると解釈され、その後にssh
コマンドに渡される引数user@server
が続きます。
"
は補間された文字列を開始します。これは完了するとssh
コマンドに渡される引数を含みます。この場合、ssh
によって解釈され、user@server
として実行されるリモートコマンドになります。
$(
は、実行されるコマンドを開始し、その出力は周囲の補間文字列によって取得されます。
cat
は、後に続くファイルの内容を出力するためのコマンドです。 cat
の出力は、キャプチャ用の補間文字列に返されます。
<<
がbashを始めるheredoc
'EOT'
は、heredocの名前がEOTであることを指定します。 EOTを囲む一重引用符'
は、heredocがnowdocとして解析されることを指定します。これは、内容がbashによって補間されず、リテラル形式で渡される特別な形式のheredocです。
<<'EOT'
と<newline>EOT<newline>
の間にあるコンテンツは、nowdocの出力に追加されます。
EOT
はnowdocを終了し、nowdocの一時ファイルが作成され、呼び出し元のcat
コマンドに返されます。 cat
はnowdocを出力し、その出力をキャプチャ用補間文字列に返します。
)
は実行されるコマンドを終了します
"
は、キャプチャ用の補間文字列を終了します。補間された文字列の内容は、単一のコマンドライン引数としてssh
に返されます。これは、ssh
がuser@server
として実行されるリモートコマンドとして解釈されます。
cat
のような外部ツールの使用を避け、1つではなく2つのステートメントを持つことを気にしないのであれば、SSHコマンドを生成するためにheredocに組み込まれたread
を使用してください。
IFS='' read -r -d '' SSH_COMMAND <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
ssh user@server "${SSH_COMMAND}"
これは私が同じエラーメッセージで抱えていた関連した問題を解決したからです。
問題 :私はWindowsにcygwinをインストールしていてこのエラーが出ました:Pseudo-terminal will not be allocated because stdin is not a terminal
解決策 :opensshクライアントプログラムとユーティリティをインストールしました できません 。そのためcygwinは、cygwinバージョンではなく、sshのWindows実装を使用していました。解決策は、openssh cygwinパッケージをインストールすることでした。
すべての関連情報は既存の回答にありますが、実用的な要約を試してみましょう:
tl; dr:
コマンドライン引数を使用して実行するコマンドを渡す:ssh jdoe@server '...'
'...'
文字列は複数行にわたることができるため、ヒアドキュメントを使用しなくてもコードを読みやすく保つことができます。ssh jdoe@server ' ... '
を使用する場合のように、stdin経由でコマンドを渡さないでくださいhere-document :ssh jdoe@server <<'EOF' # Do NOT do this ... EOF
引数としてコマンドを渡すと、そのまま機能します。
exit
ステートメントは必要ありません。これは、コマンドが処理された後にセッションが自動的に終了するためです。要するに、stdinを介してコマンドを渡すことは、ssh
の設計と対立するメカニズムであり、問題を回避する必要があります。
詳細を知りたい場合は、読んでください。
ssh
ターゲットサーバーで実行するコマンドを受け入れるためのメカニズムは、command-line argument:最後のオペランド(非オプション引数)は、1つ以上のシェルコマンドを含む文字列を受け入れます。
デフォルトでは、これらのコマンドは、非対話型シェルで、(疑似)端末を使用せずに無人で実行されます(オプション-T
が暗示されています)、最後のコマンドの処理が終了すると、セッションは自動的に終了します。
コマンドがユーザーインタラクションを必要とする場合(インタラクティブプロンプトへの応答など)、 pty(pseudo-tty) の作成を明示的に要求できます。 =、-t
オプションを使用して、リモートセッションとの対話を可能にする擬似端末。例えば。:
ssh -t jdoe@server 'read -p "Enter something: "; echo "Entered: [$REPLY]"'
インタラクティブread
プロンプトはptyでのみ正しく動作するため、-t
オプションが必要であることに注意してください。
Ptyを使用すると、顕著な副作用があります。stdoutとstderrはcombinedであり、両方ともstdoutで報告されます。つまり、通常の出力とエラー出力の区別が失われます。例えば。:
ssh jdoe@server 'echo out; echo err >&2' # OK - stdout and stderr separate
ssh -t jdoe@server 'echo out; echo err >&2' # !! stdout + stderr -> stdout
この引数がない場合、ssh
はinteractiveShellを作成します。 stdinを介してコマンドを送信します。これがトラブルの始まりです。
interactiveシェルの場合、ssh
は通常、デフォルトでpty(疑似端末)を割り当てます。標準入力の場合、except (実際の)端末に接続されていません。
stdinを介してコマンドを送信すると、ssh
のstdinが端末に接続されなくなるため、noptyが作成され、ssh
warnsそれに応じて:Pseudo-terminal will not be allocated because stdin is not a terminal.
明示的な目的がrequestptyの作成である-t
オプションでも、not 十分この場合:同じ警告が表示されます。
やや不思議なことに、ptyの作成を強制するには、double-t
option :ssh -t -t ...
またはssh -tt ...
は、あなたが本当にそれを意味することを示しています。
おそらく、この非常に慎重な手順を必要とする理由は、ものが期待どおりに機能しない可能性があることです。たとえば、macOS 10.12では、上記のコマンドと明らかに同等で、stdin経由でコマンドを提供し、-tt
を使用すると、notは正しく機能しません。 read
プロンプトに応答した後、セッションが停止します。ssh -tt jdoe@server <<<'read -p "Enter something: "; echo "Entered: [$REPLY]"'
まれに、引数として渡したいコマンドがシステムに対してコマンドラインを長くしすぎる場合(その長さがgetconf ARG_MAX
に近づく場合- この記事 を参照)、コードのコピーを検討してください最初にスクリプトの形式でリモートシステムに(たとえば、scp
を使用して)、次にそのスクリプトを実行するコマンドを送信します。
ピンチでは、-T
を使用し、stdinを介してコマンドを提供し、末尾にexit
コマンドを付けますが、インタラクティブ機能も必要な場合は、 -tt
の代わりに-T
を使用しても機能しない場合があります。
私はハングがどこから来るのかわかりませんが、対話型のsshにコマンドをリダイレクト(またはパイプ)することは一般的に問題のレシピです。最後の引数として実行するコマンドスタイルを使用し、sshコマンドラインでスクリプトを渡す方がより堅牢です。
ssh user@server 'DEP_ROOT="/home/matthewr/releases"
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR=$DEP_ROOT"/"$datestamp
if [ ! -d "$DEP_ROOT" ]; then
echo "creating the root directory"
mkdir $DEP_ROOT
fi
mkdir $REL_DIR'
(1つの巨大な'
で区切られた複数行のコマンドライン引数)。
疑似端末メッセージはあなたの-t
が原因で、sshがリモートマシン上で動作する環境をそこで動作するプログラムに対する実際の端末のように見せようとします。あなたのsshクライアントは、 own 標準入力が端末ではないのでそれを拒否しています。そのため、特別な端末APIをリモートマシンからローカルエンドの実際の端末に渡すことはできません。
とにかく-t
で何を達成しようとしましたか?
これらの答えをたくさん読んだ後、私は自分の結果の解決策を共有すると思いました。私が追加したのはheredocの前の/bin/bash
であり、それはもうエラーを与えません。
これを使って:
ssh user@machine /bin/bash <<'ENDSSH'
hostname
ENDSSH
これの代わりに(エラーを与えます):
ssh user@machine <<'ENDSSH'
hostname
ENDSSH
またはこれを使う:
ssh user@machine /bin/bash < run-command.sh
これの代わりに(エラーを与えます):
ssh user@machine < run-command.sh
_ extra _ :
それでもリモートのインタラクティブプロンプトが必要な場合は、以前のソリューションではプロンプトを入力できないため、リモートで実行しているスクリプトからパスワードやその他の情報の入力を求められた場合。
ssh -t user@machine "$(<run-command.sh)"
セッション全体をlogfile.log
ファイルにも記録したい場合は、次のようにします。
ssh -t user@machine "$(<run-command.sh)" | tee -a logfile.log
Emacs 24.5.1を使用して/ ssh:user @ Hostを介して一部の会社のサーバーに接続するときに、Windowsで同じエラーが発生していました。私の問題を解決したのは、 "tramp-default-method"変数を "plink"に設定し、サーバーに接続するたびにsshプロトコルを省略することでした。これを機能させるには、PuTTYのplink.exeをインストールする必要があります。
解決策