script - linux read variable from stdin
El pseudo-terminal no se asignará porque stdin no es un terminal (10)
Después de leer muchas de estas respuestas, pensé que compartiría mi solución resultante. Todo lo que agregué es /bin/bash
antes del heredoc y ya no da el error.
Utilizar esta:
ssh user@machine /bin/bash <<''ENDSSH''
hostname
ENDSSH
En lugar de esto (da error):
ssh user@machine <<''ENDSSH''
hostname
ENDSSH
O usa esto:
ssh user@machine /bin/bash < run-command.sh
En lugar de esto (da error):
ssh user@machine < run-command.sh
EXTRA :
Si aún desea una solicitud interactiva remota, por ejemplo, si el script que está ejecutando de forma remota le solicita una contraseña u otra información, ya que las soluciones anteriores no le permitirán escribir en las solicitudes.
ssh -t user@machine "$(<run-command.sh)"
Y si también desea registrar toda la sesión en un archivo logfile.log
:
ssh -t user@machine "$(<run-command.sh)" | tee -a logfile.log
Estoy intentando escribir un script de shell que crea algunos directorios en un servidor remoto y luego usa scp para copiar archivos de mi máquina local en el remoto. Esto es lo que tengo hasta ahora:
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
Cada vez que lo ejecuto recibo este mensaje:
Pseudo-terminal will not be allocated because stdin is not a terminal.
Y el guión simplemente cuelga para siempre.
Mi clave pública es de confianza en el servidor y puedo ejecutar todos los comandos fuera de la secuencia de comandos muy bien. ¿Algunas ideas?
El mensaje de advertencia Pseudo-terminal will not be allocated because stdin is not a terminal.
es debido al hecho de que no se especifica ningún comando para ssh
mientras que la entrada estándar se redirige desde un documento aquí. Debido a la falta de un comando especificado como argumento, ssh
primero espera una sesión de inicio de sesión interactiva (lo que requeriría la asignación de una pty en el host remoto) pero luego debe darse cuenta de que su stdin local no es tty / pty. La redirección de la entrada estándar de ssh
desde un documento aquí normalmente requiere que se especifique un comando (como /bin/sh
) como un argumento para ssh
, y en tal caso, no se asignará ningún pty en el host remoto de forma predeterminada.
Como no hay comandos que ejecutar a través de ssh
que requieren la presencia de un tty / pty (como vim
o top
), el interruptor -t
a ssh
es superfluo. Simplemente use ssh -T user@server <<EOT ...
o ssh user@server /bin/bash <<EOT ...
y la advertencia desaparecerá.
Si <<EOF
no se escapa o no tiene comillas simples (es decir, <</EOT
o <<''EOT''
), las variables dentro del documento aquí serán expandidas por el shell local antes de que se ejecute ssh ...
El efecto es que las variables dentro del documento aquí permanecerán vacías porque están definidas solo en el shell remoto.
Por lo tanto, si el shell local debería poder acceder a $REL_DIR
y definirlo en el shell remoto, se debe definir $REL_DIR
fuera del documento de aquí antes del comando ssh
( versión 1 a continuación); o, si se usa <</EOT
o <<''EOT''
, la salida del comando ssh
se puede asignar a REL_DIR
si la única salida del comando ssh
a stdout se genera mediante el echo "$REL_DIR"
dentro del único / escapado -cita aquí el documento ( versión 2 abajo).
Una tercera opción sería almacenar el documento aquí en una variable y luego pasar esta variable como un argumento de comando a ssh -t user@server "$heredoc"
( versión 3 a continuación).
Y, por último, pero no menos importante, no sería una mala idea comprobar si los directorios en el host remoto se crearon correctamente (ver: verificar si el archivo existe en el host remoto con 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"
Estaba teniendo el mismo error en Windows usando emacs 24.5.1 para conectarme a algunos servidores de la compañía a través de / ssh: user @ host. Lo que resolvió mi problema fue configurar la variable "tramp-default-method" en "plink" y cada vez que me conecto a un servidor omito el protocolo ssh. Necesitas tener instalado el plink.exe de PuTTY para que esto funcione.
Solución
- Mx personaliza-variable (y luego presiona Enter)
- tramp-default-method (y luego presiona Enter nuevamente)
- En el campo de texto, coloca plink y luego aplica y guarda el búfer.
- Cuando intento acceder a un servidor remoto, ahora uso Cxf / user @ host: y luego ingrese la contraseña. La conexión ahora se realiza correctamente bajo Emacs en Windows a mi servidor remoto.
Estoy agregando esta respuesta porque resolvió un problema relacionado que tenía con el mismo mensaje de error.
Problema : instalé cygwin en Windows y estaba recibiendo este error: el Pseudo-terminal will not be allocated because stdin is not a terminal
Resolución : Resulta que no había instalado el programa de cliente openssh y las utilidades. Debido a que cygwin estaba usando la implementación de ssh para Windows, no la versión de cygwin. La solución fue instalar el paquete openssh cygwin.
Intente ssh -t -t
(o ssh -tt
para abreviar) para forzar la asignación pseudo-tty incluso si stdin no es un terminal.
Vea también: Terminar sesión SSH ejecutada por script bash
Desde la página de manual de ssh:
-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.
No sé de dónde viene el bloqueo, pero redirigir (o canalizar) los comandos a un ssh interactivo es, en general, una receta para los problemas. Es más robusto usar el estilo de comando-a-ejecutar-como-último-argumento y pasar el script en la línea de comandos 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''
(Todo en un argumento de línea de comando de multilínea delimitado por gigante ''
).
El mensaje de pseudo-terminal se debe a que su -t
le pide a ssh que intente hacer que el entorno que se ejecuta en la máquina remota se vea como un terminal real para los programas que se ejecutan allí. Su cliente ssh se niega a hacerlo porque su propia entrada estándar no es un terminal, por lo que no tiene forma de pasar las API de terminales especiales desde la máquina remota a su terminal real en el extremo local.
¿Qué estabas tratando de lograr con -t
todos modos?
Según la respuesta de zanco , no está proporcionando un comando remoto a ssh
, dado que el shell analiza la línea de comandos. Para resolver este problema, cambie la sintaxis de la invocación de su comando ssh
para que el comando remoto se componga de una cadena multilínea sintácticamente correcta.
Hay una variedad de sintaxis que se pueden utilizar. Por ejemplo, dado que los comandos se pueden canalizar a bash
y sh
, y probablemente también a otros shells, la solución más sencilla es combinar la invocación de shell ssh
con heredocs:
ssh user@server /bin/bash <<''EOT''
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
Tenga en cuenta que ejecutar lo anterior sin /bin/bash
dará como resultado que el Pseudo-terminal will not be allocated because stdin is not a terminal
advertencia Pseudo-terminal will not be allocated because stdin is not a terminal
. También tenga en cuenta que EOT
está rodeado por comillas simples, por lo que bash
reconoce el heredoc como un nowdoc , desactivando la interpolación de variables locales para que el texto del comando se pase como está a ssh
.
Si eres fanático de las tuberías, puedes reescribir lo anterior de la siguiente manera:
cat <<''EOT'' | ssh user@server /bin/bash
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
La misma advertencia sobre /bin/bash
aplica a lo anterior.
Otro enfoque válido es pasar el comando remoto multilínea como una sola cadena, utilizando múltiples capas de interpolación de variables bash
siguiente manera:
ssh user@server "$( cat <<''EOT''
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
)"
La solución anterior soluciona este problema de la siguiente manera:
ssh user@server
se analiza mediante bash, y se interpreta como el comandossh
, seguido de un argumentouser@server
que se pasa al comandossh
"
comienza una cadena interpolada, que cuando se complete, comprenderá un argumento que se pasará al comandossh
, que en este caso será interpretado porssh
como el comando remoto que se ejecutará comouser@server
$(
comienza un comando para ejecutarse, con la salida capturada por la cadena interpolada circundantecat
es un comando para generar el contenido del archivo que sigue. La salida decat
pasará de nuevo a la cadena interpolada de captura<<
comienza un bash heredoc''EOT''
especifica que el nombre del heredoc es EOT. Las comillas simples que rodean a EOT especifican que el heredoc debe analizarse como un nowdoc , que es una forma especial de heredoc en la que los contenidos no se interpolan con bash, sino que se transmiten en formato literal.Cualquier contenido que se encuentre entre
<<''EOT''
y<newline>EOT<newline>
se agregará a la salida de nowdocEOT
termina el nowdoc, lo que da como resultado que se cree un archivo temporal nowdoc y se devuelva al comandocat
que realiza la llamada.cat
saca el nowdoc y devuelve la salida a la cadena interpolada de captura)
concluye el comando a ejecutar"
concluye la captura de la cadena interpolada. El contenido de la cadena interpolada se devolverá assh
como un único argumento de línea de comando, quessh
interpretará como el comando remoto que se ejecutará comouser@server
Si necesita evitar el uso de herramientas externas como cat
, y no le importa tener dos declaraciones en lugar de una, use la read
incorporada con un heredoc para generar el comando 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}"
También con opción -T
desde manual
Deshabilitar la asignación de pseudo-tty
Toda la información relevante está en las respuestas existentes, pero permítame intentar un resumen pragmático :
tl; dr:
Pase los comandos para ejecutar usando un argumento de línea de comandos :
ssh jdoe@server ''...''
-
''...''
cadenas''...''
pueden abarcar varias líneas, por lo que puede mantener su código legible incluso sin el uso de un documento aquí:
ssh jdoe@server '' ... ''
-
NO pase los comandos a través de stdin , como es el caso cuando usa un here-document :
ssh jdoe@server <<''EOF'' # Do NOT do this ... EOF
Pasar los comandos como un argumento funciona como está, y:
- El problema con el pseudo-terminal ni siquiera surgirá.
- no necesitará una instrucción de
exit
al final de sus comandos, porque la sesión se cerrará automáticamente después de que se hayan procesado los comandos.
En resumen: pasar comandos a través de stdin es un mecanismo que está en desacuerdo con el diseño de ssh
y causa problemas que luego se deben solucionar.
Sigue leyendo, si quieres saber más.
Información de fondo opcional:
El mecanismo de ssh
para aceptar comandos para ejecutarse en el servidor de destino es un argumento de línea de comandos : el operando final (argumento sin opción) acepta una cadena que contiene uno o más comandos de shell.
De forma predeterminada, estos comandos se ejecutan de forma desatendida, en un shell no interactivo , sin el uso de un (pseudo) terminal (opción
-T
está implícita), y la sesión finaliza automáticamente cuando el último comando termina de procesarse.En el caso de que sus comandos requieran la interacción del usuario , como responder a una solicitud interactiva, puede solicitar explícitamente la creación de un pty (pseudo-tty) , un pseudo terminal, que permita interactuar con la sesión remota, usando la opción
-t
; p.ej:ssh -t jdoe@server ''read -p "Enter something: "; echo "Entered: [$REPLY]"''
Tenga en cuenta que el indicador de
read
interactiva solo funciona correctamente con un pty, por lo que se necesita la opción-t
.El uso de un pty tiene un efecto secundario notable: stdout y stderr se combinan y ambos se informan a través de stdout ; en otras palabras: se pierde la distinción entre salida regular y error; p.ej:
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 ausencia de este argumento, ssh
crea un shell interactivo , incluido cuando envía comandos a través de la entrada estándar , que es donde comienza el problema:
Para un shell interactivo ,
ssh
normalmente asigna un pty (pseudo-terminal) de forma predeterminada, excepto si su stdin no está conectado a un terminal (real).Enviar comandos a través de stdin significa que el stdin de
ssh
ya no está conectado a un terminal, por lo que no se crea pty, yssh
le advierte en consecuencia :
Pseudo-terminal will not be allocated because stdin is not a terminal.
Incluso la opción
-t
, cuyo propósito expreso es solicitar la creación de una pty, no es suficiente en este caso : obtendrá la misma advertencia.Curiosamente, debes doblar la opción
-t
para forzar la creación de un pty:ssh -t -t ...
ossh -tt ...
muestra que realmente lo dices en serio .Quizás la razón para requerir este paso tan deliberado es que las cosas pueden no funcionar como se espera . Por ejemplo, en macOS 10.12, el equivalente aparente del comando anterior, proporcionar los comandos a través de stdin y usar
-tt
, no funciona correctamente; la sesión se atasca después de responder al indicador deread
:
ssh -tt jdoe@server <<<''read -p "Enter something: "; echo "Entered: [$REPLY]"''
En el improbable caso de que los comandos que desee pasar como argumento hagan que la línea de comandos sea demasiado larga para su sistema (si su longitud se aproxima a getconf ARG_MAX
- consulte este artículo ), considere copiar el código al sistema remoto en forma de un script primero (usando, por ejemplo, scp
), y luego envíe un comando para ejecutar ese script.
En caso de pinch, use -T
y proporcione los comandos a través de stdin , con un comando de exit
final, pero tenga en cuenta que si también necesita funciones interactivas, usar -tt
en lugar de -T
puede no funcionar.
ssh -t foobar @ localhost yourscript.pl