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 -t
uit 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 bash
en sh
, en waarschijnlijk ook andere shells, is de eenvoudigste oplossing om gewoon ssh
shell-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/bash
zal resulteren in de waarschuwing Pseudo-terminal will not be allocated because stdin is not a terminal
. Merk ook op dat EOT
tussen enkele aanhalingstekens staat, zodat bash
de 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/bash
is 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 bash
variabele 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:
-
ssh user@server
wordt geparseerd door bash en wordt geïnterpreteerd als de opdrachtssh
, gevolgd door een argumentuser@server
die moet worden doorgegeven aan de opdrachtssh
-
"
begint een geïnterpoleerde tekenreeks, die, wanneer voltooid, een argument zal bevatten dat moet worden doorgegeven aan de opdrachtssh
, die in dit geval zal worden geïnterpreteerd doorssh
om de externe opdracht te zijn die moet worden uitgevoerd alsuser@server
-
$(
begint een uit te voeren opdracht, waarbij de uitvoer wordt vastgelegd door de omringende geïnterpoleerde string -
cat
is een commando om de inhoud van elk bestand dat volgt uit te voeren. De uitvoer vancat
wordt teruggestuurd naar de vastleggende geïnterpoleerde string -
<<
begint een bash heredoc -
'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 -
Alle inhoud die wordt aangetroffen tussen
<<'EOT'
en<newline>EOT<newline>
wordt toegevoegd aan de nowdoc-uitvoer -
EOT
beëindigt de nowdoc, wat resulteert in een tijdelijk nowdoc-bestand dat wordt gemaakt en teruggestuurd naar het aanroependecat
-commando.cat
voert de nowdoc uit en geeft de uitvoer terug aan de vastleggende geïnterpoleerde string -
)
sluit het uit te voeren commando af -
"
besluit de gevangen geïnterpoleerde string. De inhoud van de geïnterpoleerde string wordt teruggegeven aanssh
als een enkel commandoregelargument, datssh
wordt geïnterpreteerd als de opdracht op afstand om uit te voeren alsuser@server
Als je het gebruik van externe tools zoals cat
wilt vermijden, en het niet erg vindt om twee statements te hebben in plaats van één, gebruik dan de ingebouwde read
met 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 ssh
en problemen veroorzaakt waar dan omheen moet worden gewerkt.
Lees verder als je meer wilt weten.
Optionele achtergrondinformatie:
Het mechanisme van
ssh
voor 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
-t
is 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-t
is 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 ssh
een interactieveshell– ook wanneer je commando’s verstuurt via stdin, waar het probleem begint:
-
Voor een interactieveshell wijst
ssh
normaal 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
ssh
niet langer verbonden is met een terminal, dus geenpty wordt aangemaakt, enssh
waarschuwtu 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
-t
optieom het aanmaken van een pty te forceren:ssh -t -t ...
ofssh -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 deread
prompt:
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 -tt
in plaats van -t
werkt 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 -t
die 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/bash
vóó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
- M-x customize-variable (en druk dan op Enter)
- tramp-default-methode (en druk dan nogmaals op Enter)
- Plaats in het tekstveld plink en vervolgens Toepassen en opslaan van de buffer
- 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