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


345

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

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

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


4
次のように、使用する端末を指定するだけですssh user@server /bin/bash <<EOT…
Buzut

3
@Buzut:おそらくshellを意味しますが、はい、/bin/bash明示的に指定することは問題を回避する1つの方法です。
mklement0 2017年

1
@ mklement0確かに、それが私が意図したことです。これを修正するためのThx;)
Buzut 2017年

回答:


512

試すssh -t -t(またはssh -ttstdinが端末でなくても、疑似tty割り当てを強制する略して)ようにし。

参照:bashスクリプトによって実行されたSSHセッションの終了

sshマンページから:

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

21
ここで実行されるスクリプトで同様の問題が発生しています。-t -tを追加しましたが、新しいエラーが発生しています。「tcgetattr:デバイスの不適切なioctl」
MasterZ

5
なぜssh -t -tありませんかssh -tt?私が知らない違いはありますか?
ジャック

3
@MasterZ同じことがここにあります。これに対する回答を得るのは良いことですInappropriate IOCtl for device
krb686 2015

11
@Jack -tt-t -t同等です。argsを個別に指定したり、一緒にスムーズにしたりすることは、個人的なスタイル/好みの問題になり、それについて言えば、どちらの方法でもそれを行うための有効な引数があります。しかし、実際には、それは単なる個人的な好みです。
JDS

5
「sshにローカルttyがない場合でも」とはどういう意味ですか?
CMCDragonkai 2016

192

また、オプション付き-Tマニュアル

疑似tty割り当てを無効にする


11
この回答には、より多くのポイントが必要です。正解であり、zancoの回答ほど長くはありません。「-t -tでTTYを割り当てる」という不条理は、完全にスキップできる場合、107ポイントを獲得します
nhed

2
賛成しただけ それは私にとって-t -tよりも効果的でした
Vincent Hiribarren '19年

15
「-t -t」は機能しますが、「
Ivan Balashov

3
@nhed:この-ttオプションは、実際にTTYを必要とし、OPのエラーメッセージを受け取っている私たちにとって最もよく見えます。この-T回答は、TTYが必要ない場合に適しています。
ErichBSchulz

3
@nhed-確かに-しかし、私のような他の人は、エラーメッセージのエラーをグーグルで検索してここに来たと確信しています。他のグーグルが2つの競合する回答からどのように選択すべきかを明確にすることは不合理ではないようです。またはそうでないかもしれません。わからない
ErichBSchulz

90

パーzancoの答え、あなたは、リモートコマンドを提供していないsshシェルはコマンドラインを解析する方法与えられ、。この問題を解決するには、sshコマンド呼び出しの構文を変更して、リモートコマンドが構文的に正しい複数行の文字列で構成されるようにします。

使用できるさまざまな構文があります。コマンドはにパイプすることができますので、例えば、bashそしてshあまりにも、そしておそらく他のシェル、最も簡単な解決策は、単に組み合わせることであるsshヒアドキュメントとシェル呼び出しを:

ssh user@server /bin/bash <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT

上記を実行せず /bin/bashに警告が発生することに注意してくださいPseudo-terminal will not be allocated because stdin is not a terminal。また、EOTは単一引用符で囲まれているためbash、ヒアドキュメントを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バッシュによって解析され、そしてあると解釈されるsshコマンド、引数が続くuser@serverに渡されるsshコマンド

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

  3. $( 実行されるコマンドを開始し、出力は周囲の補間された文字列によってキャプチャされます

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

  5. <<bashのヒアドキュメントを始める

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

  7. <<'EOT'で遭遇<newline>EOT<newline>し、nowdoc出力に追加されるコンテンツ

  8. EOTnowdocを終了します。その結果、nowdoc一時ファイルが作成され、呼び出しcatコマンドに渡されます。catnowdocを出力し、出力をキャプチャーされた補間文字列に渡します

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

  10. "キャプチャされた補間文字列を終了します。補間された文字列の内容は、ssh単一のコマンドライン引数として渡されます。これsshは、次のように実行されるリモートコマンドとして解釈されます。user@server

のような外部ツールの使用を回避する必要がありcat、1つではなく2つのステートメントを使用してもかまわない場合は、read組み込みのheredocを使用してSSHコマンドを生成します。

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}"

1年半後に+1!:)本当に明確な説明。よくできました
yaroslavTir 2014

1
私にとって(そして私は決して "DevOps"の人ではありません)、これがうまくいきました(私はJenkinsを使用しています)。「-t -t」と「-T」の提案も試しましたが、オープンストリームの問題と非実行の問題が発生しました。
Ryan Crews

2年後、本当に役に立った
ジャック・ニー

+1素晴らしい説明。しかし、最後のコードスニペット(IFS = '' *)をリモートコマンドを実行するための関数にした場合、$ 1をIFS = '' *にどのように渡しますか?それはまさしく1ドルの内容をまったく認識しているようです。
クリスチャンMatthiasAmbæk16年

1
@ mklement0もちろん、必要に応じて文字列をエスケープできますが、別のシェルスクリプトからコピーアンドペーストするだけのスクリプトステートメントを変更したり、そのためにスタックオーバーフローを変更したりする必要はありません。また、catソリューションの優雅さは、単一の(複合ではありますが)ステートメントで実現され、一時的な変数がシェル環境を汚染しないことです。
Dejay Clayton 2017

63

これは、同じエラーメッセージで発生していた関連問題を解決したためです。

問題:Windowsにcygwinをインストールしていて、次のエラーが発生しました。Pseudo-terminal will not be allocated because stdin is not a terminal

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


10
私にとっては、それはPATHの別のssh実装であることが$ which ssh/cygdrive/c/Program Files (x86)/Git/bin/ssh
わかりました

そして、opensshこれをインストールすることは、Windowsに行く方法かもしれません:superuser.com/a/301026/260710
Andreas Dietrich

この解決策は私にとってはうまくいきます。opensshをインストールした後、問題はなくなりました。
jdhao 2018年

34

警告メッセージPseudo-terminal will not be allocated because stdin is not a terminal.ssh、標準入力がヒアドキュメントからリダイレクトされている間、コマンドが指定されていないためです。引数として指定されたコマンドがないため、ssh最初に対話型ログインセッションが予想されます(リモートホストでのptyの割り当てが必要になります)が、ローカルのstdinがtty / ptyではないことを認識する必要があります。sshhereドキュメントからのstdinをリダイレクトするには、通常、コマンド(など/bin/sh)をへの引数として指定する必要がありsshます。そのような場合、デフォルトではリモートホストにptyは割り当てられません。

経由で実行するコマンドがないため sshtty / pty(vimまたはなどtop)の存在を必要とするでため、-tへの切り替えsshは不要です。使うだけssh -T user@server <<EOT ...またはまたはssh user@server /bin/bash <<EOT ...を警告は消えます。

<<EOFエスケープされていない場合、または一重引用符で囲まれた(つまり<<\EOTまたは<<'EOT')の場合、hereドキュメント内の変数は、実行前にローカルシェルによって展開されssh ...ます。結果は、here文書内の変数はリモートシェルでのみ定義されるため、空のままになります。

したがって、$REL_DIRローカルシェルからアクセス可能で、リモートシェルで定義する必要がある場合は$REL_DIRsshコマンドの前にhereドキュメントの外で定義する必要があります(以下のバージョン1)。または、<<\EOTまたは<<'EOT'が使用されている場合、sshコマンドの出力はREL_DIR場合、sshコマンドの出力は、stdoutへのコマンドの唯一の出力がecho "$REL_DIR"、エスケープ/単一引用符で囲まれたhereドキュメント(バージョン2下の)ます。

3番目のオプションは、ヒアドキュメントを変数に保存し、この変数をコマンド引数として渡すことです。 ssh -t user@server "$heredoc"バージョン3以下の)。

そして、最後に重要なことですが、リモートホスト上のディレクトリが正常に作成されたかどうかを確認することは悪い考えではありません(参照:sshを使用してリモートホストにファイルが存在するかどうかを確認してください)。

# version 1

unset DEP_ROOT REL_DIR
DEP_ROOT='/tmp'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR="${DEP_ROOT}/${datestamp}"

ssh localhost /bin/bash <<EOF
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
   echo "creating the root directory" 1>&2
   mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
#echo "$REL_DIR"
exit
EOF

scp -r ./dir1 user@server:"$REL_DIR"
scp -r ./dir2 user@server:"$REL_DIR"


# version 2

REL_DIR="$(
ssh localhost /bin/bash <<\EOF
DEP_ROOT='/tmp'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR="${DEP_ROOT}/${datestamp}"
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
   echo "creating the root directory" 1>&2
   mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
echo "$REL_DIR"
exit
EOF
)"

scp -r ./dir1 user@server:"$REL_DIR"
scp -r ./dir2 user@server:"$REL_DIR"


# version 3

heredoc="$(cat <<'EOF'
# -onlcr: prevent the terminal from converting bare line feeds to carriage return/line feed pairs
stty -echo -onlcr
DEP_ROOT='/tmp'
datestamp="$(date +%Y%m%d%H%M%S)"
REL_DIR="${DEP_ROOT}/${datestamp}"
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
   echo "creating the root directory" 1>&2
   mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
echo "$REL_DIR"
stty echo onlcr
exit
EOF
)"

REL_DIR="$(ssh -t localhost "$heredoc")"

scp -r ./dir1 user@server:"$REL_DIR"
scp -r ./dir2 user@server:"$REL_DIR"

5
このエラーの意味と、ヒアドキュメントを使用したときにエラーが表示される理由についての詳細な説明をありがとうございました。これは、回避するだけでなく、実際に理解するのに役立ちました。
Dan

29

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

tl; dr:

  • コマンドライン引数を使用して実行するコマンドを渡してください
    ssh jdoe@server '...'

    • '...' 文字列は複数行にまたがることができるため、ヒアドキュメントを使用しなくてもコードを読みやすくすることができます。
      ssh jdoe@server ' ... '
  • here-documentを使用する場合のように、stdinを介してコマンドを渡さないでください
    ssh jdoe@server <<'EOF' # Do NOT do this ... EOF

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

  • 疑似端末の問題は発生しません。
  • exitコマンドが処理された後、セッションは自動的に終了するため、コマンドの最後にステートメントを置く必要はありません

つまり、stdinを介してコマンドを渡すことは、sshの設計と矛盾するメカニズムであり、回避する必要のある問題を引き起こします。
あなたがもっと知りたいなら、読んでください。


オプションの背景情報:

sshターゲットサーバーで実行するコマンドを受け入れるメカニズムはコマンドライン引数です。最後のオペランド(オプション以外の引数)は、1つ以上のシェルコマンドを含む文字列を受け入れます。

  • デフォルトでは、これらのコマンドは非対話型シェルで(疑似)端末を使用せずに無人で実行され(オプション-Tが暗黙指定されます)、最後のコマンドが処理を完了すると、セッションは自動的に終了します。

  • コマンドで対話型プロンプトへの応答などのユーザー操作が必要な場合は、オプションを使用して、リモートセッションとの対話を可能にする疑似端末であるpty(疑似tty)の作成を明示的に要求でき-tます。例えば:

    • ssh -t jdoe@server 'read -p "Enter something: "; echo "Entered: [$REPLY]"'

    • 対話型readプロンプトはptyでのみ正しく機能するため、-tオプションが必要であることに注意してください。

    • ptyを使用すると、顕著な副作用があります。stdoutとstderrが結合され、どちらも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対話型シェルを作成します-stdinを介してコマンドを送信する場合を含みます。ここで問題が発生します。

  • 以下のためにインタラクティブシェル、ssh通常、デフォルトでPTY(疑似端末)を割り当てる以外そのSTDINは(実際の)端子に接続されていない場合。

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

    • この場合、ptyの作成-t要求することを目的とするオプションでも十分はありません。同じ警告が表示されます。

      • 不思議なことに、ptyの作成を強制するオプション2倍-tにする必要がありますssh -t -t ...または、本当に、本当にそれを意味してssh -tt ...いることを示します。

      • おそらく、この非常に慎重な手順を要求する根拠は、物事が期待どおりに機能しない可能性があることです。たとえば、macOS 10.12では、上記のコマンドに相当するもので、stdinを介してコマンドを提供し、を使用-ttして正しく機能しませreadプロンプトに応答した後、セッションがスタックします。
        ssh -tt jdoe@server <<<'read -p "Enter something: "; echo "Entered: [$REPLY]"'


万が一、引数として渡したいコマンドがシステムにとってコマンドラインを長くしすぎる場合(長さが近づくと、この記事をgetconf ARG_MAX参照してください))、最初にスクリプトの形式でリモートシステムにコードをコピーすることを検討して(たとえば、scp)そのスクリプトを実行するコマンドを送信します。

ピンチでは、を使用し、末尾のコマンドとともにstdin-Tを介してコマンドを提供しますが、インタラクティブな機能も必要な場合は、代わりにを使用しても機能しない可能性があることに注意してください。exit-tt-T


1
これは完全に機能し、-ttの方法は、sshを介して送信されたすべてのコマンドを端末に表示させることでした。
マークE

1
「ptyを強制的に作成するには、-tオプションを2倍にします。ssh-t -t ...またはssh -tt ...は、あなたが本当に、本当にそれを意味していることを示します。」-ハ、ハ-ありがとう、おもしろいけどまじめすぎる-頭がダブってわからないシングル1を入れても問題ない
DavidC

22

ハングの原因はわかりませんが、コマンドをインタラクティブなsshにリダイレクト(またはパイプ)することは、一般的に問題のレシピです。command-to-run-as-a-last-argumentスタイルを使用し、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クライアントはこれを拒否しています。リモートマシンからローカルエンドの実際の端末に特別な端末APIを渡す方法はありません。

-tとにかく何を達成しようとしていましたか?


1
-tオプションは、Psuedo端末の問題を修正する試みでした(機能しませんでした)。私はあなたの解決策を試しました、それは疑似末端のものを取り除きましたが、今それはただハングします...
マシュー

4
@ヘニング:インタラクティブsshへのリダイレクトやパイプのデメリットについて詳しく説明したり、リンクを提供してもらえますか?
Isaac Kleinman

少し遅れますが、ここでは疑似端末は必要ないため、-T代わりにオプションを使用してください。
フロリアンカステラーヌ

6

これらの回答の多くを読んだ後、私は自分の結果として得られた解決策を共有したいと思いました。追加したのは/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

追加

以前の解決策ではプロンプトに入力することができないため、リモートインタラクティブプロンプトが必要な場合(リモートで実行しているスクリプトがパスワードやその他の情報を要求する場合など)。

ssh -t user@machine "$(<run-command.sh)"

また、セッション全体をファイルに記録したい場合logfile.log

ssh -t user@machine "$(<run-command.sh)" | tee -a logfile.log

0

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

解決

  1. Mx Customize-variable(そしてEnterキーを押します)
  2. tramp-default-method(そしてEnterキーをもう一度押します)
  3. テキストフィールドにplinkを入力してから、バッファを適用して保存します
  4. リモートサーバーにアクセスしようとするときはいつでも、Cxf / user @ host:を使用してからパスワードを入力します。これで、WindowsのEmacsでリモートサーバーに正しく接続されました。

-3

ssh -t foob​​ar @ localhost yourscript.pl


2
OPも使用し-tますが、特定のシナリオでは十分ではないため、最初に質問をしました。
mklement0 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.