Comment exécuter une commande via SSH

Placez la commande après l'hôte, entre apostrophes : ssh user@server 'uptime | cut -c1-20'. Elle s'exécute sur le serveur, sa sortie vous revient, et ssh se termine avec le code de sortie de la commande, ou 255 si SSH lui-même a échoué. Les apostrophes empêchent votre shell local de modifier la commande. Ajoutez -n quand vous utilisez ssh dans une boucle.

Mis à jour le . OpenSSH 10.3 sur macOS vers OpenSSH 10.0 sur Debian 13 dans un conteneur jetable, avec bash des deux côtés.

Guillemets : quel shell lit quoi

Deux shells lisent votre commande : le vôtre d'abord, puis celui du serveur. Les guillemets décident lequel développe quoi.

NAME=local
ssh server "echo $NAME"       # local shell expands it, prints: local
ssh server 'echo $HOME $NAME' # server expands it, prints: /home/tester
                              # NAME does not exist on the server

ssh joint ses arguments avec des espaces simples et envoie une seule chaîne : les guillemets que seul votre shell local voit sont donc perdus :

$ ssh server echo "a  b"
a b
$ ssh server 'echo "a  b"'
a  b

Il en va de même pour les tubes. La première commande ci-dessous exécute wc sur le serveur, la seconde l'exécute sur votre machine :

ssh server 'ls /tmp | wc -l'
ssh server ls /tmp | wc -l

Pour transmettre une valeur locale qui contient des espaces ou des guillemets, laissez bash l'échapper :

F="a b'c"
ssh server "printf '%s\n' $(printf %q "$F")"

Plusieurs lignes : un heredoc

Mettez le marqueur du heredoc entre guillemets et rien n'est développé localement :

ssh server 'bash -s' <<'REMOTE'
echo "user: $(whoami)"
cd /tmp && pwd
REMOTE

Sans guillemets autour du marqueur, votre shell local développe d'abord les variables. Échappez celles destinées au serveur :

V=abc
ssh server 'bash -s' <<REMOTE
echo "local var: $V, remote: \$HOSTNAME"
REMOTE

Pour exécuter un script local sur le serveur avec des arguments :

ssh server 'bash -s -- one two' < script.sh

Codes de sortie

CommandeCode de sortie
ssh server 'exit 3'3
ssh server 'false; true'0, car seule la dernière commande compte
ssh server 'set -e; false; echo no'1, et l'exécution s'arrête à false
ssh server nosuchcmd127, commande introuvable sur le serveur
ssh -p 1 server true255, SSH n'a pas pu se connecter

Une commande distante peut aussi se terminer d'elle-même avec 255 : ce code ne fait donc que suggérer un échec de SSH. La sortie d'erreur reste séparée de la sortie standard, donc 2>/dev/null et > file fonctionnent comme d'habitude de votre côté.

Pourquoi ssh dans une boucle s'arrête après un hôte

ssh lit l'entrée standard et l'envoie au serveur : il avale donc le reste de la liste. Cette boucle ne s'est exécutée qu'une fois dans notre essai, avec trois lignes dans le fichier :

while read -r h; do ssh "$h" uptime; done < hosts

-n indique à ssh de ne pas lire l'entrée standard. Celle-ci s'est exécutée trois fois :

while read -r h; do ssh -n "$h" uptime; done < hosts

Avant une boucle sur des serveurs de production, lisez comment éviter les erreurs sur les serveurs de production.

Envoyer des données par l'entrée standard

Sans -n, l'entrée standard est utile. Les commandes distantes peuvent la lire :

printf 'hello\n' | ssh server 'tr a-z A-Z'
tar -cf - folder | ssh server 'tar -C /tmp -xf -'

Commandes qui ont besoin d'un terminal

Avec une commande, ssh n'alloue pas de terminal : tty affiche donc not a tty et sudo ne peut pas demander de mot de passe :

sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper

Ajoutez -t pour les programmes interactifs :

ssh -t server sudo systemctl restart nginx

Si votre propre entrée standard n'est pas un terminal, par exemple dans un script, -t affiche Pseudo-terminal will not be allocated because stdin is not a terminal. Utilisez -tt pour le forcer, et consultez le guide de cette erreur.

Tâches longues et commandes en arrière-plan

Une tâche en arrière-plan qui garde la sortie ouverte fait attendre ssh. La première commande ci-dessous a pris 2 secondes dans notre essai, et la seconde est revenue aussitôt :

ssh server 'sleep 2 &'
ssh server 'nohup sleep 2 >/dev/null 2>&1 &'

Pour un vrai travail qui doit survivre à la connexion, utilisez tmux sur le serveur. ssh -f server 'command' ne fait que passer votre ssh local en arrière-plan après la connexion.

L'environnement diffère de votre session de connexion

Une commande via SSH ne s'exécute pas dans un shell de connexion : des fichiers comme ~/.profile ne sont donc pas lus. Dans notre essai, shopt login_shell affichait off et PATH valait /usr/local/bin:/usr/bin:/bin:/usr/games. Si une commande fonctionne à la main mais indique command not found via SSH, utilisez son chemin complet.

Questions

Comment lancer sudo par SSH sans l'erreur de terminal ?

Ajoutez -t, comme dans ssh -t server sudo systemctl restart nginx, pour que sudo ait un terminal où demander le mot de passe. Dans les scripts, utilisez -tt ou configurez sudo pour cette commande.

Pourquoi une commande marche à la main mais pas par SSH ?

Une commande par SSH tourne dans un shell qui n'est pas de connexion, donc ~/.profile n'est pas lu et le PATH est plus court. Utilisez le chemin complet du programme.

Comment lancer plusieurs commandes en un seul appel SSH ?

Mettez-les dans une seule chaîne entre guillemets, comme ssh server 'cd /tmp && ls', ou envoyez un script avec ssh server 'bash -s' < script.sh.

Sources

Autres guides : Sessions et tmux

Tous les guides