Pseudo-terminal wordt niet toegewezen omdat stdin geen terminal is

Ik probeer een shellscript te schrijven dat enkele mappen op een externe server maakt en vervolgens scp gebruikt om bestanden van mijn lokale machine naar de externe computer te kopiëren. Dit is wat ik tot nu toe heb:

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

Telkens wanneer ik het uitvoer, krijg ik dit bericht:

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

En het script blijft gewoon voor altijd hangen.

Mijn openbare sleutel wordt vertrouwd op de server en ik kan alle opdrachten buiten het script prima uitvoeren. Enig idee?


Antwoord 1, autoriteit 100%

Probeer ssh -t -t(of kortweg ssh -tt) om pseudo-tty-toewijzing af te dwingen, zelfs als stdin geen terminal is.

Zie ook: SSH-sessie beëindigen uitgevoerd door bash-script

Van ssh-manpagina:

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

Antwoord 2, autoriteit 38%

Ook met optie -tuit handleiding

Schakel pseudo-tty-toewijzing uit


Antwoord 3, autoriteit 17%

Volgens zanco’s antwoord, geef je geen commando op afstand aan ssh, gezien de manier waarop de shell parseert de opdrachtregel. Om dit probleem op te lossen, wijzigt u de syntaxis van uw ssh-opdrachtaanroep zodat de externe opdracht bestaat uit een syntactisch correcte tekenreeks met meerdere regels.

Er zijn verschillende syntaxis die kunnen worden gebruikt. Omdat commando’s bijvoorbeeld kunnen worden doorgesluisd naar bashen sh, en waarschijnlijk ook andere shells, is de eenvoudigste oplossing om gewoon sshshell-aanroep te combineren met heredocs:

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

Merk op dat het uitvoeren van het bovenstaande zonder/bin/bashzal resulteren in de waarschuwing Pseudo-terminal will not be allocated because stdin is not a terminal. Merk ook op dat EOTtussen enkele aanhalingstekens staat, zodat bashde heredoc herkent als een nowdoc, waardoor interpolatie van lokale variabelen wordt uitgeschakeld, zodat de opdrachttekst wordt ongewijzigd doorgegeven aan ssh.

Als je een fan bent van pijpen, kun je het bovenstaande als volgt herschrijven:

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

Hetzelfde voorbehoud over /bin/bashis van toepassing op het bovenstaande.

Een andere geldige benadering is om de meerregelige opdracht op afstand als een enkele tekenreeks door te geven, met behulp van meerdere lagen bashvariabele interpolatie als volgt:

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

De bovenstaande oplossing lost dit probleem op de volgende manier op:

  1. ssh user@serverwordt geparseerd door bash en wordt geïnterpreteerd als de opdracht ssh, gevolgd door een argument user@serverdie moet worden doorgegeven aan de opdracht ssh

  2. "begint een geïnterpoleerde tekenreeks, die, wanneer voltooid, een argument zal bevatten dat moet worden doorgegeven aan de opdracht ssh, die in dit geval zal worden geïnterpreteerd door sshom de externe opdracht te zijn die moet worden uitgevoerd als user@server

  3. $(begint een uit te voeren opdracht, waarbij de uitvoer wordt vastgelegd door de omringende geïnterpoleerde string

  4. catis een commando om de inhoud van elk bestand dat volgt uit te voeren. De uitvoer van catwordt teruggestuurd naar de vastleggende geïnterpoleerde string

  5. <<begint een bash heredoc

  6. 'EOT'geeft aan dat de naam van de heredoc EOT is. De enkele aanhalingstekens 'rond EOT specificeren dat de heredoc moet worden geparseerd als een nowdoc, wat een speciale vorm van heredoc is waarin de inhoud niet wordt geïnterpoleerd door bash, maar liever letterlijk doorgegeven

  7. Alle inhoud die wordt aangetroffen tussen <<'EOT'en <newline>EOT<newline>wordt toegevoegd aan de nowdoc-uitvoer

  8. EOTbeëindigt de nowdoc, wat resulteert in een tijdelijk nowdoc-bestand dat wordt gemaakt en teruggestuurd naar het aanroepende cat-commando. catvoert de nowdoc uit en geeft de uitvoer terug aan de vastleggende geïnterpoleerde string

  9. )sluit het uit te voeren commando af

  10. "besluit de gevangen geïnterpoleerde string. De inhoud van de geïnterpoleerde string wordt teruggegeven aan sshals een enkel commandoregelargument, dat sshwordt geïnterpreteerd als de opdracht op afstand om uit te voeren als user@server

Als je het gebruik van externe tools zoals catwilt vermijden, en het niet erg vindt om twee statements te hebben in plaats van één, gebruik dan de ingebouwde readmet een heredoc om genereer het SSH-commando:

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

Antwoord 4, autoriteit 11%

Ik voeg dit antwoord toe omdat het een gerelateerd probleem heeft opgelost dat ik had met dezelfde foutmelding.

Probleem: ik had cygwin onder Windows geïnstalleerd en kreeg deze foutmelding: Pseudo-terminal will not be allocated because stdin is not a terminal

Oplossing: het bleek dat ik het openssh-clientprogramma en hulpprogramma’s niethad geïnstalleerd. Daarom gebruikte cygwin de Windows-implementatie van ssh, niet de cygwin-versie. De oplossing was om het openssh cygwin-pakket te installeren.


Antwoord 5, autoriteit 7%

Alle relevante informatie staat in de bestaande antwoorden, maar laat me een pragmatische samenvattingproberen:

tl;dr:

  • Geef de uit te voeren opdrachten door met een opdrachtregelargument:
    ssh jdoe@server '...'

    • '...'tekenreeksen kunnen meerdere regels beslaan, dus je kunt je code leesbaar houden, zelfs zonder het gebruik van een here-document:
      ssh jdoe@server ' ... '
  • Geef de commando’s NIET door via stdin, zoals het geval is wanneer je een hier-document:
    ssh jdoe@server <<'EOF' # Do NOT do this ... EOF

Het doorgeven van de commando’s als argumentwerkt zoals het is, en:

  • het probleem met de pseudo-terminal zal zich niet eens voordoen.
  • je hebt geen exit-statementnodig aan het einde van je commando’s, omdat de sessie automatisch wordt afgesloten nadat de commando’s zijn verwerkt.

Kortom: het doorgeven van opdrachten via stdinis een mechanisme dat op gespannen voet staat met het ontwerp van sshen problemen veroorzaakt waar dan omheen moet worden gewerkt.
Lees verder als je meer wilt weten.


Optionele achtergrondinformatie:

Het mechanisme van

sshvoor het accepteren van opdrachten om uit te voeren op de doelserver is een opdrachtregelargument: de laatste operand (niet- optie argument) accepteert een tekenreeks die een of meer shell-commando’s bevat.

  • Standaard draaien deze commando’s zonder toezicht, in een niet-interactieveshell, zonder het gebruik van een (pseudo) terminal (optie -tis geïmpliceerd), en de sessie wordt automatisch beëindigdwanneer de laatste opdracht is verwerkt.

  • In het geval dat uw opdrachten gebruikersinteractievereisen, zoals het reageren op een interactieve prompt, kunt u expliciet verzoeken om het maken van een pty (pseudo-tty), een pseudo-terminal, die interactie met de externe sessie mogelijk maakt, met behulp van de optie -t; bijv.:

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

    • Houd er rekening mee dat de interactieve read-prompt alleen correct werkt met een pty, dus de optie -tis nodig.

    • Het gebruik van een pty heeft een opmerkelijke bijwerking: stdout en stderr worden gecombineerden beide gerapporteerd via stdout; met andere woorden: je verliest het onderscheid tussen reguliere en foutoutput; bijv.:

      • 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

Als dit argument niet aanwezig is, maakt ssheen interactieveshell– ook wanneer je commando’s verstuurt via stdin, waar het probleem begint:

  • Voor een interactieveshell wijst sshnormaal gesproken standaard een pty (pseudo-terminal) toe, behalveals zijn stdin is niet verbonden met een (echte) terminal.

    • Het verzenden van commando’s via stdin betekent dat de stdin van sshniet langer verbonden is met een terminal, dus geenpty wordt aangemaakt, en sshwaarschuwtu dienovereenkomstig:
      Pseudo-terminal will not be allocated because stdin is not a terminal.

    • Zelfs de optie -t, waarvan het uitdrukkelijke doel is om verzoekente maken om een ​​pty te maken, is nietgenoeg in dit geval: u krijgt dezelfde waarschuwing.

      • Enigszins merkwaardig moet je dan verdubbelende -toptieom het aanmaken van een pty te forceren: ssh -t -t ...of ssh -tt ...laat zien dat je het echt, echt meent.

      • Misschien is de reden voor het vereisen van deze zeer bewuste stap dat dingen misschien niet werken zoals verwacht. Bijvoorbeeld, op macOS 10.12, werkt het schijnbare equivalent van het bovenstaande commando, het leveren van de commando’s via stdin en het gebruik van -tt, nietcorrect; de sessie loopt vast na het reageren op de readprompt:
        ssh -tt jdoe@server <<<'read -p "Enter something: "; echo "Entered: [$REPLY]"'


In het onwaarschijnlijke geval dat de opdrachten die u als argument wilt doorgeven, de opdrachtregel te lang maken voor uw systeem (als de lengte in de buurt komt van getconf ARG_MAX– zie dit artikel), overweeg dan eerst de code naar het externe systeem te kopiëren in de vorm van een script (met, bijv. scp), en stuur dan een commando om dat script uit te voeren.

Gebruik in een mum van tijd -t, en geef de commando’s via stdin, met een afsluitend exit-commando, maar houd er rekening mee dat als u hebben ook interactieve functies nodig, het gebruik van -ttin plaats van -twerkt mogelijk niet.


Antwoord 6, autoriteit 4%

Ik weet niet waar het vastlopen vandaan komt, maar het omleiden (of pipen) van commando’s naar een interactieve ssh is over het algemeen een recept voor problemen. Het is robuuster om de stijl commando-to-run-as-a-last-argument te gebruiken en het script op de ssh-opdrachtregel door te geven:

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'

(Alles in één gigantisch '-gescheiden meerregelig opdrachtregelargument).

Het pseudo-terminalbericht is vanwege uw -tdie ssh vraagt ​​om te proberen de omgeving die het op de externe machine draait, eruit te laten zien als een echte terminal voor de programma’s die daar draaien. Je ssh-client weigert dat te doen omdat zijn eigenstandaardinvoer geen terminal is, dus het heeft geen manier om de speciale terminal-API’s verder door te geven van de externe machine naar je eigenlijke terminal aan het lokale uiteinde.

Wat probeerde je eigenlijk te bereiken met -t?


Antwoord 7

Na het lezen van veel van deze antwoorden, dacht ik dat ik mijn resulterende oplossing zou delen. Alles wat ik heb toegevoegd is /bin/bashvóór de heredoc en het geeft de fout niet meer.

Gebruik dit:

ssh user@machine /bin/bash <<'ENDSSH'
   hostname
ENDSSH

In plaats van dit (geeft fout):

ssh user@machine <<'ENDSSH'
   hostname
ENDSSH

Of gebruik dit:

ssh user@machine /bin/bash < run-command.sh

In plaats van dit (geeft fout):

ssh user@machine < run-command.sh

EXTRA:

Als u nog steeds een interactieve prompt op afstand wilt, b.v. als het script dat u op afstand uitvoert, u om een ​​wachtwoord of andere informatie vraagt, omdat u met de vorige oplossingen niet in de prompts kunt typen.

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

En als je ook de hele sessie wilt loggen in een bestand logfile.log:

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

Antwoord 8

Ik had dezelfde fout onder Windows toen ik emacs 24.5.1 gebruikte om verbinding te maken met sommige bedrijfsservers via /ssh:user@host. Wat mijn probleem oploste, was het instellen van de variabele “tramp-default-method” op “plink” en wanneer ik verbinding maak met een server, laat ik het ssh-protocol weg. Je moet PuTTY’s plink.exe geïnstalleerd hebben om dit te laten werken.

Oplossing

  1. M-x customize-variable (en druk dan op Enter)
  2. tramp-default-methode (en druk dan nogmaals op Enter)
  3. Plaats in het tekstveld plink en vervolgens Toepassen en opslaan van de buffer
  4. Telkens wanneer ik toegang probeer te krijgen tot een externe server, gebruik ik nu C-x-f /user@host: en voer dan het wachtwoord in. De verbinding is nu correct gemaakt onder Emacs op Windows naar mijn externe server.

Antwoord 9

ssh -tfoobar@localhost uwscript.pl

Other episodes