web-dev-qa-db-ja.com

Stdinは端末ではないため、擬似端末は割り当てられません

リモートサーバー上にディレクトリをいくつか作成してから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.

そしてスクリプトは永久にハングします。

私の公開鍵はサーバー上で信頼されており、スクリプトの外部ですべてのコマンドを実行することができます。何か案は?

282
Matthew

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.
424
carok

manual からのオプション-T

擬似端末の割り当てを無効にする

152
Emil Bojda

zanco's answer 、シェルがコマンドラインを解析する方法を考えると、sshにリモートコマンドを提供していません。この問題を解決するには、sshコマンド呼び出しの構文を変更して、リモート・コマンドが構文的に正しい複数行のストリングで構成されるようにしてください。

使用できるさまざまな構文があります。たとえば、コマンドをbashsh、そしておそらく他のシェルにもパイプ接続できるため、最も簡単な解決策は、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
)"

上記の解決策は、次のようにしてこの問題を解決します。

  1. ssh user@serverはbashによって解析され、sshコマンドであると解釈され、その後にsshコマンドに渡される引数user@serverが続きます。

  2. "は補間された文字列を開始します。これは完了するとsshコマンドに渡される引数を含みます。この場合、sshによって解釈され、user@serverとして実行されるリモートコマンドになります。

  3. $(は、実行されるコマンドを開始し、その出力は周囲の補間文字列によって取得されます。

  4. catは、後に続くファイルの内容を出力するためのコマンドです。 catの出力は、キャプチャ用の補間文字列に返されます。

  5. <<がbashを始めるheredoc

  6. 'EOT'は、heredocの名前がEOTであることを指定します。 EOTを囲む一重引用符'は、heredocがnowdocとして解析されることを指定します。これは、内容がbashによって補間されず、リテラル形式で渡される特別な形式のheredocです。

  7. <<'EOT'<newline>EOT<newline>の間にあるコンテンツは、nowdocの出力に追加されます。

  8. EOTはnowdocを終了し、nowdocの一時ファイルが作成され、呼び出し元のcatコマンドに返されます。 catはnowdocを出力し、その出力をキャプチャ用補間文字列に返します。

  9. )は実行されるコマンドを終了します

  10. "は、キャプチャ用の補間文字列を終了します。補間された文字列の内容は、単一のコマンドライン引数としてsshに返されます。これは、sshuser@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}"
75
Dejay Clayton

これは私が同じエラーメッセージで抱えていた関連した問題を解決したからです。

問題 :私はWindowsにcygwinをインストールしていてこのエラーが出ました:Pseudo-terminal will not be allocated because stdin is not a terminal

解決策 :opensshクライアントプログラムとユーティリティをインストールしました できません 。そのためcygwinは、cygwinバージョンではなく、sshのWindows実装を使用していました。解決策は、openssh cygwinパッケージをインストールすることでした。

52
Andrew Prock

すべての関連情報は既存の回答にありますが、実用的な要約を試してみましょう

tl; dr:

引数としてコマンドを渡すと、そのまま機能します。

  • 擬似端末の問題は発生しません。
  • コマンドの最後に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

この引数がない場合、sshinteractiveShellを作成します。 stdinを介してコマンドを送信します。これがトラブルの始まりです。

  • interactiveシェルの場合、sshは通常、デフォルトでpty(疑似端末)を割り当てます。標準入力の場合、except (実際の)端末に接続されていません。

    • stdinを介してコマンドを送信すると、sshのstdinが端末に接続されなくなるため、noptyが作成され、sshwarnsそれに応じて
      Pseudo-terminal will not be allocated because stdin is not a terminal.

    • 明示的な目的がrequestptyの作成である-tオプションでも、not 十分この場合:同じ警告が表示されます。

      • やや不思議なことに、ptyの作成を強制するには、double-t optionssh -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を使用しても機能しない場合があります。

24
mklement0

私はハングがどこから来るのかわかりませんが、対話型の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で何を達成しようとしましたか?

21
Henning Makholm

これらの答えをたくさん読んだ後、私は自分の結果の解決策を共有すると思いました。私が追加したのは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
5
Wadih M.

Emacs 24.5.1を使用して/ ssh:user @ Hostを介して一部の会社のサーバーに接続するときに、Windowsで同じエラーが発生していました。私の問題を解決したのは、 "tramp-default-method"変数を "plink"に設定し、サーバーに接続するたびにsshプロトコルを省略することでした。これを機能させるには、PuTTYのplink.exeをインストールする必要があります。

解決策

  1. M-x customize-variable(そしてEnterキーを押します)
  2. tramp-default-method(そしてもう一度Enterを押す)
  3. テキストフィールドにplinkを入力してから、Applyをクリックしてバッファを保存します。
  4. リモートサーバーにアクセスしようとするたびに、C-x-f/user @ Host:を使用してからパスワードを入力します。これで、Windows上のEmacsの下で私のリモートサーバーに正しく接続されました。
0
Miguel Rentes