it-swarm.com.de

Pseudo-Terminal wird nicht zugewiesen, da stdin kein Terminal ist

Ich versuche, ein Shell-Skript zu schreiben, das einige Verzeichnisse auf einem Remote-Server erstellt und dann scp verwendet, um Dateien von meinem lokalen Computer auf den Remote-Server zu kopieren. Folgendes habe ich bisher:

ssh -t [email protected]<<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 [email protected]:$REL_DIR
scp ./dir2 [email protected]:$REL_DIR

Wann immer ich es starte, erhalte ich diese Nachricht:

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

Und das Drehbuch hängt einfach für immer.

Mein öffentlicher Schlüssel ist auf dem Server vertrauenswürdig und ich kann alle Befehle auch außerhalb des Skripts ausführen. Irgendwelche Ideen?

300
Matthew

Versuchen Sie ssh -t -t (oder ssh -tt), um die Pseudotty-Zuweisung zu erzwingen, auch wenn stdin kein Terminal ist.

Siehe auch: Beenden der durch das Bash-Skript ausgeführten SSH-Sitzung

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

Auch mit der Option -T von manuell

Deaktivieren Sie die Pseudotty-Zuordnung

163
Emil Bojda

Per Antwort von zanco , Sie geben ssh keinen Remote-Befehl, vorausgesetzt, die Shell analysiert die Befehlszeile. Um dieses Problem zu beheben, ändern Sie die Syntax Ihres Befehls ssh so, dass der Remote-Befehl aus einer syntaktisch korrekten, mehrzeiligen Zeichenfolge besteht.

Es gibt eine Vielzahl von Syntaxen, die verwendet werden können. Da Befehle beispielsweise an bash und sh und wahrscheinlich auch an andere Shells weitergeleitet werden können, besteht die einfachste Lösung darin, nur ssh Shell-Aufrufe mit heredocs zu kombinieren:

ssh [email protected] /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 des obigen Befehls ohne /bin/bash zur Warnung Pseudo-terminal will not be allocated because stdin is not a terminal führt. Beachten Sie auch, dass EOT in einfache Anführungszeichen gesetzt ist, sodass bash den Heredoc als Nowdoc erkennt und den lokalen Status deaktiviert variable Interpolation, damit der Befehlstext unverändert an ssh übergeben wird.

Wenn Sie ein Fan von Pfeifen sind, können Sie das Obige wie folgt umschreiben:

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

Der gleiche Vorbehalt zu /bin/bash gilt für das oben Gesagte.

Ein anderer gültiger Ansatz ist die Übergabe des mehrzeiligen Remote-Befehls als einzelne Zeichenfolge unter Verwendung mehrerer Ebenen der Variableninterpolation bash wie folgt:

ssh [email protected] "$( 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 [email protected] wird von bash analysiert und als Befehl ssh interpretiert, gefolgt von einem Argument [email protected], das an den Befehl ssh übergeben wird

  2. " beginnt eine interpolierte Zeichenfolge, die nach Abschluss ein Argument enthält, das an den Befehl ssh übergeben wird. In diesem Fall interpretiert ssh den auszuführenden Fernbefehl als [email protected]

  3. $( startet einen auszuführenden Befehl, wobei die Ausgabe von der umgebenden interpolierten Zeichenfolge erfasst wird

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

  5. << startet eine Bash Heredoc

  6. 'EOT' gibt an, dass der Name des Heredocs EOT ist. Die einfachen Anführungszeichen ', die EOT umgeben, geben an, dass der Heredoc als nowdoc analysiert werden soll, bei dem es sich um eine spezielle Form von Heredoc handelt, in der der Inhalt enthalten ist Lassen Sie sich nicht von Bash interpolieren, sondern im wörtlichen Format weitergeben

  7. Alle Inhalte, die zwischen <<'EOT' und <newline>EOT<newline> auftreten, werden an die nowdoc-Ausgabe angehängt

  8. EOT beendet den Nowdoc, wodurch eine temporäre Nowdoc-Datei erstellt und an den aufrufenden Befehl cat zurückgegeben wird. cat gibt den Nowdoc aus und leitet die Ausgabe an den interpolierten Capturing-String zurück

  9. ) beendet den auszuführenden Befehl

  10. " schließt die interpolierte Zeichenfolge ab. Der Inhalt der interpolierten Zeichenfolge wird als einzelnes Befehlszeilenargument an ssh zurückgegeben, das ssh als Remote-Befehl interpretiert, der als [email protected] ausgeführt werden soll.

Wenn Sie die Verwendung externer Tools wie cat vermeiden müssen und es Ihnen nichts ausmacht, zwei Anweisungen anstelle von einer zu haben, verwenden Sie das in einen Heredoc integrierte read, um den SSH-Befehl zu generieren:

IFS='' read -r -d '' SSH_COMMAND <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT

ssh [email protected] "${SSH_COMMAND}"
76
Dejay Clayton

Ich füge diese Antwort hinzu, weil damit ein verwandtes Problem behoben wurde, bei dem ich dieselbe Fehlermeldung erhalten habe.

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

Resolution: Es stellte sich heraus, dass ich nicht das openssh-Client-Programm und die Hilfsprogramme installiert hatte. Aus diesem Grund verwendete cygwin die Windows-Implementierung von ssh, nicht die cygwin-Version. Die Lösung bestand darin, das openssh cygwin-Paket zu installieren.

57
Andrew Prock

Die Warnmeldung Pseudo-terminal will not be allocated because stdin is not a terminal. ist darauf zurückzuführen, dass für ssh kein Befehl angegeben ist, während stdin von einem Here-Dokument umgeleitet wird. Aufgrund des Fehlens eines angegebenen Befehls als Argument erwartet ssh zunächst eine interaktive Anmeldesitzung (für die die Zuweisung eines Pty auf dem Remote-Host erforderlich wäre), muss dann jedoch feststellen, dass die lokale Standard-ID kein Tty/Pty ist . Um die Standardeingabe von ssh aus einem Here-Dokument umzuleiten, muss normalerweise ein Befehl (z. B. /bin/sh) als Argument für ssh angegeben werden. In diesem Fall wird keine Pty auf der Fernbedienung zugewiesen Host standardmäßig.

Da es keine Befehle gibt, die über ssh ausgeführt werden können und die das Vorhandensein von tty/pty erfordern (z. B. vim oder top), wechselt der -t zu ssh ist überflüssig. Verwenden Sie einfach ssh -T [email protected] <<EOT ... oder ssh [email protected] /bin/bash <<EOT ... und die Warnung verschwindet.

Wenn <<EOF nicht mit Escape-Zeichen oder einfachen Anführungszeichen versehen ist (d. H. <<\EOT oder <<'EOT'), werden die Variablen in diesem Dokument von der lokalen Shell erweitert, bevor ssh ... ausgeführt wird. Dies hat zur Folge, dass die Variablen in diesem Dokument leer bleiben, da sie nur in der Remote-Shell definiert sind.

Wenn also $REL_DIR sowohl von der lokalen Shell aus zugänglich als auch in der Remote-Shell definiert sein soll, muss $REL_DIR außerhalb des Here-Dokuments vor dem ssh -Befehl definiert werden ( Version 1 unten); oder, wenn <<\EOT oder <<'EOT' verwendet wird, kann die Ausgabe des Befehls sshREL_DIR zugewiesen werden, wenn die einzige Ausgabe des Befehls ssh stdout ist wird von echo "$REL_DIR" innerhalb des Dokuments mit Escapezeichen/einfachen Anführungszeichen ( Version 2 unten) generiert.

Eine dritte Möglichkeit wäre, das Dokument hier in einer Variablen zu speichern und diese Variable dann als Befehlsargument an ssh -t [email protected] "$heredoc" ( Version 3 weiter unten zu übergeben ).

Und last but not least wäre es keine schlechte Idee, zu überprüfen, ob die Verzeichnisse auf dem Remote-Host erfolgreich erstellt wurden (siehe: Überprüfen Sie, ob die Datei auf dem Remote-Host mit ssh vorhanden ist ).

# 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 [email protected]:"$REL_DIR"
scp -r ./dir2 [email protected]:"$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 [email protected]:"$REL_DIR"
scp -r ./dir2 [email protected]:"$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 [email protected]:"$REL_DIR"
scp -r ./dir2 [email protected]:"$REL_DIR"
30
zanco

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

tl; dr:

  • Übergeben Sie die auszuführenden Befehle mit einem Befehlszeilenargument ​​:
    ssh [email protected] '...'

    • '...'-Zeichenfolgen können mehrere Zeilen umfassen, sodass Sie Ihren Code auch ohne Verwendung eines Here-Dokuments lesbar halten können:
      ssh [email protected] ' ... '
  • Übergeben Sie die Befehle NICHT über stdin, wie dies bei Verwendung eines here-document der Fall ist =:
    ssh [email protected] <<'EOF' # Do NOT do this ... EOF

Das Übergeben der Befehle als Argument funktioniert wie sie sind und:

  • Das Problem mit dem Pseudo-Terminal wird nicht einmal auftreten.
  • Sie benötigen am Ende Ihrer Befehle keine exit -Anweisung , da die Sitzung automatisch beendet wird, nachdem die Befehle verarbeitet wurden.

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


Optionale Hintergrundinformationen:

sshs Mechanismus zum Akzeptieren von Befehlen, die auf dem Zielserver ausgeführt werden sollen, ist Befehlszeilenargument ​​: der letzte Operand (Nicht-Options-Argument) akzeptiert eine Zeichenfolge, die einen oder mehrere Shell-Befehle enthält.

  • Standardmäßig werden diese Befehle in einer nicht interaktiven Shell ohne Verwendung eines (Pseudo-) Terminals (Option -T ist impliziert) und der Sitzung unbeaufsichtigt ausgeführt endet automatisch , wenn der letzte Befehl die Verarbeitung beendet.

  • Für den Fall, dass Ihre Befehle Benutzerinteraktion erfordern, z. B. das Reagieren auf eine interaktive Eingabeaufforderung, können Sie explizit die Erstellung eines pty (pseudo-tty) , eines Pseudoterminals, anfordern ermöglicht die Interaktion mit der Remote-Sitzung mit der Option -t; z.B.:

    • ssh -t [email protected] 'read -p "Enter something: "; echo "Entered: [$REPLY]"'

    • Beachten Sie, dass die interaktive Eingabeaufforderung read nur mit einer Pty ordnungsgemäß funktioniert. Daher ist die Option -t erforderlich.

    • Die Verwendung einer Pty hat einen bemerkenswerten Nebeneffekt: stdout und stderr sind kombiniert ​​und beide werden über stdout ​​gemeldet. Mit anderen Worten: Sie verlieren die Unterscheidung zwischen regulärer Ausgabe und Fehlerausgabe. z.B.:

Ohne dieses Argument erstellt ssh eine interaktive Shell - auch wenn Sie Befehle über - senden. stdin, hier beginnt der Ärger:

  • Für eine interaktive Shell weist ssh normalerweise ein Pty (Pseudo-Terminal) zu, außer, wenn sein Stdin nicht mit einem (realen) Terminal verbunden ist.

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

    • Auch die Option -t, deren ausdrücklicher Zweck darin besteht, Anfrage eine Pty zu erstellen, ist nicht ​​genug in diesem Fall: Sie erhalten die gleiche Warnung.

      • Etwas seltsamerweise müssen Sie dann double die Option -t aktivieren, um die Erstellung eines Pty zu erzwingen: ssh -t -t ... oder ssh -tt ... zeigt, dass Sie - wirklich, wirklich gemein es.

      • Vielleicht liegt der Grund für diesen sehr absichtlichen Schritt darin, dass die Dinge möglicherweise nicht wie erwartet funktionieren. Unter macOS 10.12 funktioniert beispielsweise das scheinbare Äquivalent des obigen Befehls, der die Befehle über stdin bereitstellt und -tt verwendet, nicht ​​ordnungsgemäß. Die Sitzung bleibt hängen, nachdem Sie auf die Eingabeaufforderung read geantwortet haben:
        ssh -tt [email protected] <<<'read -p "Enter something: "; echo "Entered: [$REPLY]"'


In dem unwahrscheinlichen Fall, dass die Befehlszeile, die Sie als Argument übergeben möchten, zu lang für Ihr System ist (wenn die Länge getconf ARG_MAX annähert - siehe dieser Artikel ), sollten Sie den Code in das ferne System kopieren zuerst in Form eines Skripts (z. B. mit scp) und dann einen Befehl zum Ausführen dieses Skripts senden.

Verwenden Sie zur Not -T und geben Sie die Befehle über stdin mit einem nachgestellten exit -Befehl ein. Beachten Sie jedoch, dass die Verwendung von -tt anstelle von -T möglicherweise nicht funktioniert, wenn Sie auch interaktive Funktionen benötigen .

24
mklement0

Ich weiß nicht, woher der Hang kommt, aber das Umleiten (oder Weiterleiten) von Befehlen in ein interaktives SSH ist im Allgemeinen ein Rezept für Probleme. Es ist robuster, den Befehl zum Ausführen als letztes Argument zu verwenden und das Skript an die ssh-Befehlszeile zu übergeben:

ssh [email protected] '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'

(Alles in einem großen, durch ' getrennten mehrzeiligen Befehlszeilenargument).

Die Pseudoterminalnachricht ist auf Ihren -t zurückzuführen, der ssh auffordert, die auf dem Remotecomputer ausgeführte Umgebung so zu gestalten, dass sie für die dort ausgeführten Programme wie ein tatsächliches Terminal aussieht. Ihr ssh-Client lehnt dies ab, da die Standardeingabe own kein Terminal ist und es daher nicht möglich ist, die speziellen Terminal-APIs vom Remote-Computer an Ihr tatsächliches Terminal am lokalen Ende weiterzuleiten.

Was wolltest du eigentlich mit -t erreichen?

21
Henning Makholm

Nachdem ich viele dieser Antworten gelesen hatte, dachte ich, ich würde meine resultierende Lösung teilen. Alles was ich hinzugefügt habe ist /bin/bash vor dem Heredoc und es gibt keinen Fehler mehr.

Benutze das:

ssh [email protected] /bin/bash <<'ENDSSH'
   hostname
ENDSSH

Stattdessen (gibt Fehler):

ssh [email protected] <<'ENDSSH'
   hostname
ENDSSH

Oder benutze dies:

ssh [email protected] /bin/bash < run-command.sh

Stattdessen (gibt Fehler):

ssh [email protected] < run-command.sh

EXTRA:

Wenn Sie weiterhin eine interaktive Remote-Eingabeaufforderung wünschen, z. Wenn das Skript, das Sie remote ausführen, Sie zur Eingabe eines Kennworts oder anderer Informationen auffordert, da Sie bei den vorherigen Lösungen keine Eingabeaufforderungen eingeben können.

ssh -t [email protected] "$(<run-command.sh)"

Und wenn Sie auch die gesamte Sitzung in einer Datei logfile.log protokollieren möchten:

ssh -t [email protected] "$(<run-command.sh)" | tee -a logfile.log
5
Wadih M.

Ich hatte den gleichen Fehler unter Windows mit Emacs 24.5.1, um über/ssh: user @ Host eine Verbindung zu einigen Unternehmensservern herzustellen. Was mein Problem löste, war das Setzen der Variablen "tramp-default-method" auf "plink" und wann immer ich mich mit einem Server verbinde, beende ich das ssh-Protokoll. Sie müssen PuTTYs plink.exe installiert haben, damit dies funktioniert.

Lösung

  1. M-x-Variable anpassen (und dann die Eingabetaste drücken)
  2. tramp-default-method (und drücke dann erneut die Eingabetaste)
  3. Setzen Sie in das Textfeld plink und dann Apply und Save the buffer
  4. Immer wenn ich versuche, auf einen Remote-Server zuzugreifen, verwende ich jetzt C-x-f/user @ Host: und gebe dann das Passwort ein. Die Verbindung wird nun unter Emacs unter Windows korrekt zu meinem Remote-Server hergestellt.
0
Miguel Rentes