¿Por qué el comando `cd` no funciona a través de SSH?

41

Intenté hacer una copia de seguridad de algunos archivos a través de SSH, pero en lugar de obtener tarlos que quería, obtuve mi carpeta de inicio. Hice algunas pruebas adicionales y se reduce a esto:

ssh root@server /bin/sh -c "cd /boot && ls -l"

Que para mi sorpresa enumera los archivos en /rootno /boot. Pero si ejecuto todo el /bin/shcomando desde un terminal, se cdimprime correctamente los /bootarchivos.

¿Que esta pasando aqui?

Ambroz Bizjak
fuente
1
A menos que este sea un ejemplo, ¿por qué no lo usa ssh root@server /bin/sh -c "ls -l /boot"?
recatada
Es gracioso que lo hayas preguntado. Traté de hacer eso antes de comentar y todavía no obtuve el listado del directorio.
unxnut
2
@demure porque realmente quería tar, y tar / boot incluirá la ruta de arranque en el resultado, que no quiero
Ambroz Bizjak

Respuestas:

35

ssh no le permite especificar un comando con precisión, como lo ha hecho, como una serie de argumentos para pasar a execvp en el host remoto. En su lugar, concatena todos los argumentos en una cadena y los ejecuta a través de un shell remoto. Esto se destaca como una falla de diseño importante en ssh en mi opinión ... es una herramienta de Unix que se comporta bien en la mayoría de los casos, pero cuando llega el momento de especificar un comando, elige usar una sola cadena monolítica en lugar de un argumento, como ¡Fue diseñado para MSDOS o algo así!

Como ssh pasará su comando como una sola cadena a sh -c, no necesita proporcionar el suyo sh -c. Cuando lo haces, el resultado es

sh -c '/bin/sh -c cd /boot && ls -l'

con la cita original perdida. Entonces los comandos separados por el &&son:

`/bin/sh -c cd /boot`
`ls -l`

El primero de ellos ejecuta un shell con el texto de comando "cd" y $0= "boot". El comando "cd" se completa con éxito, $0es irrelevante y el /bin/sh -cindica éxito, entonces ls -lsucede.


fuente
2
Si bien estoy de acuerdo en que es desafortunado que se sshcomporte así, si no fuera así, tendría que llamarse sexec, no ssh. sshes la versión segura de rsh(que se comportó igual en ese sentido) y rlogin( rshllamada rlogincuando no se pasa una línea de comando). Es lamentable que no se implemente rexectan bien.
Stéphane Chazelas
@StephaneChazelas ¿alguna vez hubo un comando rexec? Es solo una función de biblioteca C, que yo sepa. Buen punto, sin embargo, sobre rsh. Ser un reemplazo directo para rsh fue la clave para una adopción rápida en los primeros días de ssh, cuando todos tenían toneladas de scripts que ya usaban rsh.
Definitivamente lo usé en el pasado. Mirando ahora, debe haber estado en HPUX ya que no muchos otros Unices parecen tenerlo, debo admitir que tenía la impresión de que estaba más extendido.
Stéphane Chazelas
Gracias. Esto realmente ayudó, especialmente. ya que estoy haciendo un túnel con ssh. Tuve que hacer ssh -t machine1 'ssh -t machine2 "echo \" 1 && 2 \ ""' para obtener '1 && 2' como salida. Eso mató unas pocas horas.
ghayes
Si desea pasar un comando con precisión, por ejemplo, desde "$ @", puede usar esto: "`printf "%q " "$@"`"con bash printfpara citar una cadena o cadenas con escapes de shell.
Sam Watkins,
36

Es un problema de citas. Ssh ya ejecuta el comando que le pasa en un shell. Cuando pasa múltiples parámetros, se concatenan con un espacio intermedio para construir una cadena. Entonces, el comando remoto que está ejecutando de forma remota es /bin/sh -c cd /boot && ls -l(sin comillas, porque las comillas en su comando fueron interpretadas por el shell local).

/bin/sh -c cd /bootcarreras /bin/shy le dice que se ejecute el comando cdy también para conjunto $0a /boot. Una vez hecho esto, se sshdejecuta el shell principal (el lanzado por ) ls -l.

En su caso, simplemente elimine el sh -cque es completamente inútil a menos que su shell remoto (como se indica en /etc/passwdotra base de datos de contraseñas) no comprenda este comando.

ssh root@server "cd /boot && ls -l"

Si necesita invocar un shell diferente, debe citar el comando remoto para protegerlo de la expansión del shell remoto invocado por sshd. Por ejemplo, si su shell de inicio de sesión es dash y desea ejecutar un comando bash:

ssh root@server 'bash -c "cd ~bob && ls -l"'
Gilles 'SO- deja de ser malvado'
fuente
8

Creo que tiene más que ver con cómo el shell analiza las opciones. Por ejemplo, esto funciona:

$ ssh root@server /bin/sh -c '"cd /boot && ls -l"'

Esto tiene el mismo problema que su comando:

$ ssh root@server /bin/sh -c 'cd /boot && ls -l'

Si habilita el -vcambio a sshpuede ver lo que está sucediendo:

1er comando:

debug1: comando de envío: / bin / sh -c "cd / boot && ls -l"

2do comando:

debug1: comando de envío: / bin / sh -c cd / boot && ls -l

Por lo general, al enviar comandos a través de ssh, debe prestar especial atención a las comillas y ajustar las comillas dentro de las comillas, ya que las diversas capas las eliminan. Tampoco te molestes en enviar /bin/sh.

Puede hacer algo muy útil una vez que comprenda la cita de sshlo siguiente. Esto ejecutará el comando en el servidor remoto pero recopilará los resultados en un archivo local en el sistema donde ejecutó el sshcomando:

$ ssh root@server 'free -m' > /tmp/memory.status

o esto, donde alquila un directorio en un servidor remoto y lo crea en el sistema local:

$ ssh remotehost 'tar zcvf - SOURCEDIR' | cat > DESTFILE.tar.gz

Referencias

slm
fuente
4

Por lo general, no tiene que especificar qué shell usar. Esto funciona:

ssh user@host "cd /boot && pwd"

Pero si su shell predeterminado es problemático, asegúrese de citar todo el comando, incluida la invocación del shell. Cualquiera de los siguientes trabajos

ssh user@host '/bin/sh -c "cd /boot && pwd"'
ssh user@host "/bin/sh -c \"cd /boot && pwd\""
tylerl
fuente
Tenga en cuenta que si bien cd /boot && pwdfunciona en todos los shells de las familias principales (Bourne, csh, rc), /bin/sh -c "cd /boot && pwd"no funcionaría si el shell remoto es de la rcfamilia donde "no es especial.
Stéphane Chazelas