web-dev-qa-db-fra.com

Pourquoi la commande `cd` ne fonctionne-t-elle pas via SSH?

J'essayais de sauvegarder certains fichiers via SSH mais au lieu de tar 'ceux que je voulais, j'ai eu mon dossier de départ. J'ai fait quelques tests supplémentaires et cela se résume à ceci:

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

À ma grande surprise, qui répertorie les fichiers dans /root pas /boot. Mais si je lance tout le /bin/sh commande depuis un terminal correctement cds et imprime le /boot des dossiers.

Qu'est-ce qu'il se passe ici?

41
Ambroz Bizjak

ssh ne vous permet pas de spécifier une commande avec précision, comme vous l'avez fait, comme une série d'arguments à transmettre à execvp sur l'hôte distant. Au lieu de cela, il concatène tous les arguments dans une chaîne et les exécute via un shell distant. À mon avis, cela représente un défaut de conception majeur dans ssh ... c'est un outil Unix bien comporté à bien des égards, mais quand vient le temps de spécifier une commande, il a choisi d'utiliser une seule chaîne monolithique au lieu d'un argv, comme il a été conçu pour MSDOS ou quelque chose!

Puisque ssh transmettra votre commande sous la forme d'une chaîne unique à sh -c, vous n'avez pas besoin de fournir votre propre sh -c. Lorsque vous le faites, le résultat est

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

avec la citation originale perdue. Ainsi, les commandes séparées par le && sont:

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

Le premier de ceux-ci exécute un shell avec le texte de commande "cd" et $0 = "boot". La commande "cd" se termine avec succès, le $0 n'est pas pertinent et le /bin/sh -c indique le succès, puis le ls -l arrive.

36
user41515

C'est un problème de citation. Ssh exécute déjà la commande que vous lui transmettez dans un shell. Lorsque vous passez plusieurs paramètres, ils sont concaténés avec un espace entre les deux pour créer une chaîne. La commande à distance que vous exécutez à distance est donc /bin/sh -c cd /boot && ls -l (pas de guillemets, car les guillemets de votre commande ont été interprétés par le shell local).

/bin/sh -c cd /boot s'exécute /bin/sh et lui dit d'exécuter la commande cd et également de définir $0 à /boot. Une fois cela fait, le shell parent (celui lancé par sshd) exécute ls -l.

Dans votre cas, supprimez simplement le sh -c ce qui est complètement inutile à moins que votre shell distant (comme indiqué dans /etc/passwd ou autre base de données de mots de passe) ne comprend pas cette commande.

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

Si vous devez appeler un autre shell, vous devez citer la commande distante pour le protéger de l'expansion par le shell distant appelé par sshd. Par exemple, si votre shell de connexion est un tiret et que vous souhaitez exécuter une commande bash:

ssh root@server 'bash -c "cd ~bob && ls -l"'

Je pense que cela a plus à voir avec la façon dont les options sont analysées par le Shell. Par exemple, cela fonctionne:

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

Cela a le même problème que votre commande:

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

Si vous activez le -v passez à ssh vous pouvez voir ce qui se passe:

1ère commande:

debug1: Commande d'envoi:/bin/sh -c "cd/boot && ls -l"

2ème commande:

debug1: Commande d'envoi:/bin/sh -c cd/boot && ls -l

En règle générale, lors de l'envoi de commandes via ssh, vous devez faire particulièrement attention aux guillemets et les envelopper entre guillemets, car les différentes couches les enlèvent. Ne prenez pas la peine d'envoyer /bin/sh.

Vous pouvez faire une chose très utile une fois que vous comprenez les guillemets de ssh tels que les suivants. Cela exécutera la commande sur le serveur distant mais collectera les résultats dans un fichier localement sur le système sur lequel vous avez exécuté la commande ssh:

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

ou ceci, où vous tarez un répertoire sur un serveur distant et le créez sur le système local:

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

Références

8
slm

En règle générale, vous n'avez pas besoin de spécifier le shell à utiliser. Cela marche:

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

Mais si votre Shell par défaut est problématique, assurez-vous simplement de citer la commande entier, y compris l'invocation du Shell. L'une des œuvres suivantes

ssh user@Host '/bin/sh -c "cd /boot && pwd"'
ssh user@Host "/bin/sh -c \"cd /boot && pwd\""
5
tylerl