489 Stimmen

Das Pseudoterminal wird nicht zugewiesen, da stdin kein Terminal ist.

Ich versuche, ein Shell-Skript zu schreiben, das einige Verzeichnisse auf einem entfernten Server erstellt und dann scp verwendet, um Dateien von meinem lokalen Rechner auf den entfernten zu kopieren. Hier ist, was ich bis jetzt habe:

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

Jedes Mal, wenn ich es ausführe, erhalte ich diese Meldung:

Pseudo-terminal will not be allocated because stdin is not a terminal.

Und das Skript bleibt einfach ewig hängen.

Mein öffentlicher Schlüssel ist auf dem Server vertrauenswürdig, und ich kann alle Befehle außerhalb des Skripts problemlos ausführen. Irgendeine Idee?

666voto

carok Punkte 6222

トライ ssh -t -t (oder ssh -tt kurz), um eine Pseudo-Tty-Zuweisung zu erzwingen, auch wenn stdin kein Terminal ist.

Siehe auch: Beenden einer SSH-Sitzung, die von einem Bash-Skript ausgeführt wird

Aus der ssh-Manpage:

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

258voto

Emil Bojda Punkte 2429

Auch mit Option -T von Handbuch

Pseudo-Tty-Zuweisung deaktivieren

120voto

Dejay Clayton Punkte 3320

Per Antwort von zanco geben Sie keinen Fernbefehl an ssh wie die Shell die Befehlszeile analysiert. Um dieses Problem zu lösen, ändern Sie die Syntax Ihrer ssh Befehlsaufruf, so dass der Fernbefehl aus einer syntaktisch korrekten, mehrzeiligen Zeichenfolge besteht.

Es gibt eine Vielzahl von Syntaxen, die verwendet werden können. Da Befehle zum Beispiel in bash y sh und wahrscheinlich auch andere Muscheln, ist die einfachste Lösung, einfach zu kombinieren ssh Shell-Aufruf mit Heredocs:

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

Beachten Sie, dass die Ausführung der oben genannten ohne /bin/bash führt zu der Warnung Pseudo-terminal will not be allocated because stdin is not a terminal . Beachten Sie auch, dass EOT ist von einfachen Anführungszeichen umgeben, so dass bash erkennt den Heredoc als einen nowdoc und schaltet die Interpolation lokaler Variablen aus, so dass der Befehlstext unverändert an ssh .

Wenn Sie ein Fan von Pfeifen sind, können Sie den obigen Text wie folgt umschreiben:

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

Derselbe Vorbehalt gilt für /bin/bash gilt für die oben genannten Punkte.

Ein anderer gültiger Ansatz ist die Übergabe des mehrzeiligen Remote-Befehls als eine einzige Zeichenkette unter Verwendung mehrerer Schichten von bash variable Interpolation wie folgt:

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

Die obige Lösung behebt dieses Problem auf folgende Weise:

  1. ssh user@server wird von der Bash geparst und so interpretiert, dass es die ssh Befehl, gefolgt von einem Argument user@server die an den ssh Befehl

  2. " beginnt eine interpolierte Zeichenkette, die, wenn sie fertig ist, ein Argument darstellt, das an den ssh Befehl, der in diesem Fall interpretiert wird von ssh als den auszuführenden Fernbefehl als user@server

  3. $( beginnt einen auszuführenden Befehl, wobei die Ausgabe durch die umgebende interpolierte Zeichenkette erfasst wird

  4. cat ist ein Befehl zur Ausgabe des Inhalts der folgenden Datei. Die Ausgabe von cat wird in die erfassende interpolierte Zeichenkette zurückgegeben

  5. << beginnt einen Bash heredoc

  6. 'EOT' gibt an, dass der Name der Heredoc EOT ist. Die einfachen Anführungszeichen ' umgebende EOT gibt an, dass das Heredoc als nowdoc , eine spezielle Form von heredoc, bei der der Inhalt nicht von der Bash interpoliert wird, sondern als Literal weitergegeben wird

  7. Jeder Inhalt, der zwischen <<'EOT' y <newline>EOT<newline> wird an die nowdoc-Ausgabe angehängt

  8. EOT beendet nowdoc, was dazu führt, dass eine temporäre nowdoc-Datei erstellt und an den Aufrufer zurückgegeben wird cat Befehl. cat gibt den nowdoc aus und übergibt die Ausgabe zurück an den einfangenden interpolierten String

  9. ) schließt den auszuführenden Befehl ab

  10. " schließt die erfasste interpolierte Zeichenfolge ab. Der Inhalt der interpolierten Zeichenkette wird zurückgegeben an ssh als einzelnes Kommandozeilenargument, das ssh wird als der auszuführende Fernbefehl interpretiert als user@server

Wenn Sie die Verwendung externer Tools wie cat zu verwenden, und wenn es Ihnen nichts ausmacht, zwei Anweisungen anstelle einer zu haben, verwenden Sie die read mit einem heredoc eingebaut, um den SSH-Befehl zu erzeugen:

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

67voto

Andrew Prock Punkte 6523

Ich füge diese Antwort hinzu, weil sie ein verwandtes Problem gelöst hat, das ich mit der gleichen Fehlermeldung hatte.

Problem : Ich hatte cygwin unter Windows installiert und bekam diesen Fehler: Pseudo-terminal will not be allocated because stdin is not a terminal

Auflösung : Es stellte sich heraus, dass ich pas das openssh-Clientprogramm und die Dienstprogramme installiert. Aus diesem Grund verwendete cygwin die Windows-Implementierung von ssh, nicht die cygwin-Version. Die Lösung bestand darin, das openssh-Paket für cygwin zu installieren.

56voto

mklement0 Punkte 303096

Alle relevanten Informationen sind in den vorhandenen Antworten enthalten, aber lassen Sie mich versuchen, eine pragmatische Zusammenfassung :

tl;dr:

  • Übergeben Sie die auszuführenden Befehle mit einer Befehlszeilenargument :
    ssh jdoe@server '...'

    • '...' Strings können sich über mehrere Zeilen erstrecken so dass Sie Ihren Code auch ohne Here-Dokument lesbar halten können:
      ssh jdoe@server ' ... '
  • Übergeben Sie die Befehle NICHT über stdin wie es der Fall ist, wenn Sie eine hier-Dokument :
    ssh jdoe@server <<'EOF' # Do NOT do this ... EOF

Übergabe der Befehle als Argument funktioniert so wie es ist , und:

  • wird das Problem mit dem Pseudo-Terminal gar nicht erst auftreten.
  • brauchen Sie keine exit Anweisung am Ende Ihrer Befehle, denn die Sitzung wird automatisch beendet, nachdem die Befehle abgearbeitet wurden.

Kurz gesagt: Übergabe von Befehlen über stdin ist ein Mechanismus, der im Widerspruch steht zu ssh Design und verursacht Probleme, die dann umgangen werden müssen.
Lesen Sie weiter, wenn Sie mehr wissen wollen.


Optionale Hintergrundinformationen:

ssh Der Mechanismus zur Annahme von Befehlen, die auf dem Zielserver ausgeführt werden sollen, ist ein Befehlszeilenargument : Der letzte Operand (Nicht-Options-Argument) akzeptiert eine Zeichenkette, die einen oder mehrere Shell-Befehle enthält.

  • Standardmäßig werden diese Befehle unbeaufsichtigt ausgeführt werden, in einem nicht interaktiv Shell, ohne die Verwendung eines (Pseudo-)Terminals (Option -T ist impliziert), und die Sitzung endet automatisch wenn die Verarbeitung des letzten Befehls abgeschlossen ist.

  • Für den Fall, dass Ihre Befehle Folgendes erfordern Benutzerinteraktion (z. B. als Antwort auf eine interaktive Eingabeaufforderung) können Sie ausdrücklich die Erstellung einer pty (pseudo-tty) , ein Pseudo-Terminal, das die Interaktion mit der Fernsitzung ermöglicht, indem es die -t Option; z.B.:

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

    • Beachten Sie, dass die interaktive read Prompt funktioniert nur mit einem pty korrekt, so dass die -t ist erforderlich.

    • Die Verwendung eines pty hat einen bemerkenswerten Nebeneffekt: stdout und stderr sind kombiniert und beide berichtet über stdout mit anderen Worten: Sie verlieren die Unterscheidung zwischen regulärer und fehlerhafter Ausgabe; z.B.:

      • 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

In Ermangelung dieses Arguments, ssh erstellt eine interaktiv Shell - auch wenn Sie Befehle über stdin Und genau da fangen die Probleme an:

  • Für ein interaktiv Muschel, ssh weist normalerweise standardmäßig ein pty (Pseudoterminal) zu, außer wenn sein stdin nicht mit einem (echten) Terminal verbunden ist.

    • Das Senden von Befehlen über stdin bedeutet, dass ssh stdin ist nicht mehr mit einem Terminal verbunden, so dass keine pty wird erstellt, und ssh warnt Sie entsprechend :
      Pseudo-terminal will not be allocated because stdin is not a terminal.

    • Auch die -t Option, deren ausdrücklicher Zweck es ist Anfrage Gründung einer Pty, ist pas genug in diesem Fall : Sie werden die gleiche Warnung erhalten.

      • Kurioserweise müssen Sie dann doppelt die -t Option um die Erstellung eines Pty: ssh -t -t ... o ssh -tt ... zeigt, dass Sie es wirklich, wirklich ernst meinen .

      • Der Grund für die Notwendigkeit dieses sehr bewussten Schrittes ist vielleicht, dass die Dinge funktionieren vielleicht nicht wie erwartet . Unter macOS 10.12 zum Beispiel ist das scheinbare Äquivalent des obigen Befehls, die Bereitstellung der Befehle über stdin und die Verwendung von -tt hat pas richtig funktionieren; die Sitzung bleibt nach der Antwort auf die read sofort:
        ssh -tt jdoe@server <<<'read -p "Enter something: "; echo "Entered: [$REPLY]"'


Für den unwahrscheinlichen Fall, dass die Befehle, die Sie als Argument übergeben wollen, die Befehlszeile für Ihr System zu lang machen (wenn ihre Länge getconf ARG_MAX - siehe dieser Artikel ), sollten Sie in Erwägung ziehen, den Code zunächst in Form eines Skripts auf das entfernte System zu kopieren (z. B. mit, scp ), und senden Sie dann einen Befehl zur Ausführung dieses Skripts.

Im Notfall können Sie auch -T und geben die Befehle über stdin mit einem nachgestellten exit Befehl, aber beachten Sie, dass Sie, wenn Sie auch interaktive Funktionen benötigen, mit -tt anstelle von -T möglicherweise nicht funktionieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X