Pseudo-terminal ne sera pas attribué parce que stdin n'est pas un terminal

j'essaie d'écrire un script shell qui crée des répertoires sur un serveur distant et utilise ensuite scp pour copier des fichiers de ma machine locale sur la télécommande. Voici ce que j'ai jusqu'à présent:

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

chaque fois que je l'exécute Je reçois ce message:

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

et le script est suspendu pour toujours.

ma clé publique est fiable sur le serveur et je peux exécuter toutes les commandes en dehors du script très bien. Des idées?

225
demandé sur Claudio 2011-08-19 02:38:40

10 réponses

essayez ssh -t -t (ou ssh -tt pour faire court) pour forcer l'allocation de pseudo-tty même si stdin n'est pas un terminal.

Voir aussi: la Résiliation de la session SSH exécuté par script bash

De ssh page de manuel:

-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.
368
répondu carok 2017-12-02 14:30:41

aussi avec l'option -T de manuel

Disable pseudo-tty allocation

126
répondu Emil Bojda 2018-09-04 22:02:24

Par zanco la réponse de , vous n'êtes pas fournir une commande à distance pour ssh , étant donné la façon dont le shell analyse la ligne de commande. Pour résoudre ce problème, changez la syntaxe de votre invocation de commande ssh de sorte que la commande à distance soit composée d'une chaîne multi-lignes syntaxiquement correcte.

Il existe une variété de syntaxes qui peuvent être utilisés. Par exemple, puisque les commandes peuvent être transmises dans bash et sh , et probablement d'autres coquillages aussi, la solution la plus simple est de simplement combiner ssh invocation shell avec des hérédocs:

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

notez que l'exécution du ci-dessus sans /bin/bash résultera en l'avertissement Pseudo-terminal will not be allocated because stdin is not a terminal . Notez également que EOT est entouré de guillemets simples, de sorte que bash reconnaît l'heredoc comme un nowdoc , désactivant l'interpolation des variables locales de sorte que le texte de commande sera passé comme-est à ssh .

si vous êtes un fan de pipes, vous pouvez réécrire ce qui précède comme suit:

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

la même mise en garde au sujet de /bin/bash s'applique à ce qui précède.

une autre approche valide consiste à passer la commande à distance multi-ligne en une seule chaîne, en utilisant plusieurs couches de bash interpolation variable comme suit:

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

la solution ci-dessus fixe ce problème de la manière suivante:

  1. ssh user@server est interprété par bash, et est interprété comme étant la commande ssh , suivi d'un argument user@server à passer à la commande ssh

  2. " commence une chaîne interpolée, qui une fois terminée, comprendra un argument à passer à la commande ssh , qui dans ce cas sera interprété par ssh pour être la commande à distance à exécuter en tant que user@server

  3. $( commence une commande à exécuter, la sortie étant capturée par la chaîne interpolée environnante

  4. cat est une commande pour afficher le contenu du fichier qui suit. La sortie de cat sera transmise de nouveau dans la chaîne interpolée de capture

  5. << commence un bash heredoc

  6. 'EOT' précise que le nom de l'hérédoc est EOT. Les citations simples ' entourant L'EOT précise que l'hérédoc doit être interprété comme un nowdoc , qui est une forme spéciale de l'heredoc dans lequel le contenu ne sont pas interpolés par bash, mais plutôt transmis dans le format littéral

  7. tout contenu rencontré entre <<'EOT' et <newline>EOT<newline> sera ajouté à la sortie nowdoc

  8. EOT met fin au nowdoc, ce qui entraîne la création d'un fichier temporaire nowdoc et le renvoie à la commande cat . cat sort le nowdoc et renvoie la sortie à la chaîne interpolée de capture

  9. ) conclut la commande à exécuter

  10. " conclut la chaîne interpolée de capture. Le contenu de la chaîne interpolée sera passé en arrière à ssh comme un argument de ligne de commande simple, qui ssh interprétera comme la commande à distance pour exécuter comme user@server

Si vous devez éviter d'utiliser des outils externes comme cat , et ne vous gênez pas d'avoir deux instructions au lieu d'une, Utilisez le read intégré avec un heredoc pour générer la commande 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}"
64
répondu Dejay Clayton 2017-05-23 12:26:33

j'ajoute cette réponse parce qu'elle a résolu un problème connexe que j'avais avec le même message d'erreur.

problème : j'avais installé cygwin sous Windows et j'ai eu cette erreur: Pseudo-terminal will not be allocated because stdin is not a terminal

résolution : il s'avère que j'avais et non installé le programme client openssh et les utilitaires. En raison de ce cygwin utilisait L'implémentation Windows de ssh, pas la version cygwin. La solution était d'installer le paquet openssh cygwin.

36
répondu Andrew Prock 2013-10-03 20:54:23

Le message d'avertissement Pseudo-terminal will not be allocated because stdin is not a terminal. est dû au fait qu'aucune commande n'est spécifiée pour ssh alors que stdin est redirigé à partir d'un document ici. En raison de l'absence d'une commande spécifiée comme un argument ssh attend d'abord une session de connexion interactive (qui exigerait l'allocation d'un pty sur l'hôte distant), mais doit ensuite se rendre compte que son stdin local n'est pas tty/pty. La redirection du stdin de ssh à partir d'un document ici nécessite normalement une commande (telle que /bin/sh ) à être spécifié comme argument pour ssh - et dans ce cas, aucun pty sera alloué sur l'hôte distant par défaut.

puisqu'il n'y a pas de commande à exécuter via ssh qui nécessite la présence d'un ATS/pty (tel que vim ou top ) le commutateur -t en ssh est superflu. Suffit d'utiliser ssh -T user@server <<EOT ... ou ssh user@server /bin/bash <<EOT ... et l'avertissement s'en aller.

si <<EOF n'est pas évadé ou cité (I. E. <<\EOT ou <<'EOT' ) variables à l'intérieur du présent document seront étendues par l'interpréteur de commandes local avant qu'il n'exécute ssh ... . L'effet est que les variables à l'intérieur du document ici resteront vides car elles ne sont définies que dans le shell distant.

donc, si $REL_DIR doit être à la fois accessible par le shell local et défini dans le shell distant, $REL_DIR doit être défini en dehors du document ici avant la commande ssh ( version 1 ci-dessous); ou, si <<\EOT ou <<'EOT' est utilisé, la sortie de la commande ssh peut être assignée à REL_DIR si la seule sortie de la commande ssh à stdout est générée par echo "$REL_DIR" à l'intérieur du document échappé/cité ici ( version 2 ci-dessous).

une troisième option serait de stocker le document ici dans une variable et puis passer ce variable comme argument de commande à ssh -t user@server "$heredoc" ( version 3 ci-dessous).

et, last but not least, ce ne serait pas une mauvaise idée de vérifier si les répertoires sur l'hôte distant ont été créés avec succès (voir: vérifier si le fichier existe sur l'hôte distant avec 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"
26
répondu zanco 2017-05-23 12:26:33

Je ne sais pas d'où vient le hang, mais rediriger (ou piping) les commandes dans un ssh interactif est en général une recette pour les problèmes. Il est plus robuste d'utiliser la commande-to-run-as-a-last-argument et de passer le script sur la ligne de commande 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'

(Tout en un géant ' délimité par des multiligne argument de ligne de commande).

le pseudo-terminal message est en raison de votre -t qui demande à ssh d'essayer de faites en sorte que l'environnement qu'il tourne sur la machine distante ressemble à un terminal réel pour les programmes qui y tournent. Votre client SSH refuse de le faire parce que son entrée standard propre n'est pas un terminal, il n'a donc aucun moyen de passer les API de terminal spéciales de la machine distante à votre terminal actuel à l'extrémité locale.

Qu'essayiez-vous de faire avec -t de toute façon?

20
répondu Henning Makholm 2011-08-18 22:46:42

toutes les informations pertinentes se trouvent dans les réponses existantes, mais laissez-moi tenter un résumé pragmatique :

tl; dr:

  • passez les commandes pour exécuter en utilisant un argument en ligne de commande :

    ssh jdoe@server '...'

    • '...' lignes, de sorte que vous pouvez garder votre code lisible même sans l'utilisation d'un ici-document:

      ssh jdoe@server ' ... '
  • ne passez pas les commandes via stdin , comme c'est le cas lorsque vous utilisez un ici-document :

    ssh jdoe@server <<'EOF' # Do NOT do this ... EOF

passer les commandes comme argument fonctionne que-est, et:

  • le problème avec le pseudo-terminal ne se posera même pas.
  • vous n'aurez pas besoin d'une exit déclaration à la fin de vos commandes, parce que la session se terminera automatiquement après que les commandes auront été traitées.

en bref: passer des commandes via stdin est un mécanisme qui est en contradiction avec La conception de ssh et provoque des problèmes qui doivent ensuite être travaillés autour.

Lire la suite, si vous voulez en savoir plus.


Informations générales facultatives:

ssh 's mécanisme pour accepter des commandes à exécuter sur le serveur cible est un argument en ligne de commande : l'opérande finale (argument non-option) accepte une chaîne contenant un ou plus de commandes shell.

  • par défaut, ces commandes tournent sans surveillance, dans un shell non-interactif , sans l'utilisation d'un terminal (pseudo) (l'option -T est implicite), et la session se termine automatiquement lorsque la dernière commande termine le traitement.

  • dans le cas où vos commandes nécessitent interaction avec l'utilisateur , comme répondre à une invite interactive, vous pouvez explicitement demander la création d'un pty (pseudo-tty) , un pseudo terminal, qui permet d'interagir avec la session distante, en utilisant l'option -t ; par exemple:

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

    • notez que l'invite interactive read ne fonctionne correctement qu'avec un pty, donc l'option -t est nécessaire.

    • L'utilisation d'un pty a un effet secondaire notable: stdout et stderr sont combiné et les deux rapportés via stdout ; en d'autres termes: vous perdez la distinction entre la sortie régulière et l'erreur; par exemple:

      • 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

en l'absence de cet argument, ssh crée un interactif shell - y compris lorsque vous envoyez des commandes via stdin , qui est où le problème commence:

  • Pour un interactif shell", ssh normalement alloue un pty (pseudo-terminal) par défaut, à l'exception de si son stdin n'est pas connecté à un terminal (réel).

    • envoyer des commandes via stdin signifie que ssh 's stdin n'est plus connecté à un terminal, donc no pty est créé, et ssh avertit vous en conséquence :

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

    • même le -t option, dont le but exprès est de demande création d'un pty, est pas assez dans ce cas" 1519450920 : vous recevrez le même avertissement.

      • un peu curieusement, vous devez alors double le -t option pour forcer la création d'un pty: ssh -t -t ... ou ssh -tt ... montre que vous vraiment, vraiment .

      • peut-être que la raison d'exiger cette mesure très délibérée est que les choses peuvent ne pas fonctionner comme prévu . Par exemple, sur macOS 10.12, l'équivalent apparent de la commande ci-dessus, fournissant les commandes via stdin et en utilisant -tt , ne pas fonctionne correctement; la session se bloque après avoir répondu à l'invite read :

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


dans le cas peu probable où les commandes que vous voulez passer comme argument rend la ligne de commande trop longue pour votre système (Si sa longueur approche getconf ARG_MAX - voir cet article ), envisager de copier le code sur le système distant sous la forme d'un script d'abord (en utilisant, par exemple, scp ), puis envoyer une commande pour exécuter ce script.

Dans un pincement, utiliser -T , et de fournir les commandes via stdin , avec un trailing "151940920 de la commande", mais notez que si vous avez aussi besoin de fonctionnalités interactives, à l'aide de -tt au lieu de -T peut ne pas fonctionner.

19
répondu mklement0 2017-05-25 12:46:25

après avoir lu beaucoup de ces réponses, j'ai pensé que je partagerais ma solution résultante. Tout ce que j'ai ajouté est /bin/bash avant l'heredoc et il ne donne plus l'erreur.

utilisez ceci:

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

au lieu de ceci (donne l'erreur):

ssh user@machine <<'ENDSSH'
   hostname
ENDSSH

ou utilisez ceci:

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

au lieu de ceci (donne l'erreur):

ssh user@machine < run-command.sh

EXTRA :

si vous voulez toujours une invite interactive à distance, par exemple si le script que vous utilisez vous invite à distance pour un mot de passe ou d'autres informations, parce que les solutions précédentes ne vous permettront pas de taper dans les invites.

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

et si vous voulez aussi enregistrer la session entière dans un fichier logfile.log :

ssh -t user@machine "$(<run-command.sh)" | tee -a logfile.log
5
répondu Wadih M. 2016-07-10 05:17:23

J'avais la même erreur sous Windows en utilisant emacs 24.5.1 pour me connecter à certains serveurs de la société par /ssh:user@host. Ce qui a résolu mon problème a été de définir la variable "tramp-default-method" à "plink" et chaque fois que je me connecte à un serveur Je ommit le protocole ssh. Tu as besoin d'avoir Putty's plink.exe installé pour que cela fonctionne.

Solution

  1. M-x customize-variable (et puis appuyez sur Entrée)
  2. tramp-par défaut-de la méthode (et puis appuyez sur "Entrée")
  3. sur le champ de texte mettre plink puis appliquer et sauvegarder le tampon
  4. chaque fois que j'essaie d'accéder à un serveur distant, j'utilise C-x-F /user@host: puis j'entre le mot de passe. La connexion est maintenant correctement effectuée sous Emacs sous Windows sur mon serveur distant.
0
répondu Miguel Rentes 2015-12-14 15:27:09

ssh - t foobar@localhost yourscript.pl

-2
répondu mxftw 2013-11-19 20:14:09