SSH no serveix només per obrir un terminal en un altre equip: també pots executar-hi una comanda o un script sencer directament des del teu terminal, recollir-ne la sortida i continuar treballant en local. És la base de molts scripts d’administració i de qualsevol automatització senzilla. En aquest article veuràs com fer-ho, des d’una comanda sola fins a diversos servidors alhora, i els detalls que fan que a vegades no funcioni com esperes.
Tots els exemples s’han provat amb OpenSSH. Per no haver d’escriure la contrasenya cada vegada, et convé fer servir claus SSH.
1. Executar una comanda
Si després del servidor hi poses una comanda, ssh l’executa, et mostra la sortida i tanca la connexió:
$ ssh joan@servidor 'hostname; uptime'
servidor
21:54:10 up 13 days, 10:52, 0 users, load average: 0.24, 0.15, 0.08
La sortida arriba al teu terminal, de manera que la pots redirigir o processar en local:
ssh joan@servidor 'cat /etc/os-release' > os-servidor.txt
ssh joan@servidor 'df -h' | grep '/dev/sd'
I ssh retorna el codi de sortida de la comanda remota, cosa que et permet fer servir-la en condicions:
$ ssh joan@servidor 'exit 3'; echo $?
3
if ssh joan@servidor 'systemctl is-active --quiet apache2'; then
echo "Apache funciona"
fi
Les cometes importen
Amb cometes simples, la comanda arriba tal qual al servidor i s’hi interpreten les variables. Amb cometes dobles, el teu shell substitueix les variables abans d’enviar-la:
$ ssh joan@servidor 'echo $HOME'
/home/joan
$ ssh joan@servidor "echo $HOME"
/home/oscar
En el segon cas, $HOME és el del teu equip. Fes servir cometes simples, tret que vulguis passar-hi justament un valor local.
2. Comandes que necessiten un terminal: sudo i programes interactius
Quan executes una comanda remota, ssh no assigna cap terminal per defecte. La majoria de comandes no el necessiten, però sudo sí, per demanar-te la contrasenya:
$ ssh joan@servidor 'sudo apt update'
sudo: a terminal is required to read the password; either use ssh's -t option or configure an askpass helper
sudo: a password is required
La solució és l’opció -t, que força un terminal:
ssh -t joan@servidor 'sudo apt update && sudo apt upgrade'
El mateix passa amb programes a pantalla completa com top, htop, vim o less. (La versió anterior d’aquest article proposava ssh servidor 'sudo apt-get update && sudo apt-get upgrade' sense -t: no funciona si sudo demana contrasenya.)
Per a automatitzacions sense ningú davant, -t no és la solució: cal que l’usuari pugui executar aquella comanda concreta amb sudo sense contrasenya (una regla específica a /etc/sudoers.d/), o fer servir directament un compte amb els permisos necessaris.
3. Diverses comandes
Per a dues o tres, separa-les amb ; (s’executen totes) o && (cada una només si l’anterior ha anat bé):
ssh joan@servidor 'cd /var/www/web && git pull && ls -l'
Per a més, un here document és més llegible:
ssh joan@servidor bash << 'EOF'
cd /tmp
echo "Sóc a $(pwd) com a $(whoami)"
EOF
Amb 'EOF' entre cometes, les variables i els $(...) s’avaluen al servidor, no al teu equip.
4. Executar un script local al servidor
No cal copiar l’script: bash -s llegeix les comandes de l’entrada estàndard, i li pots passar l’script amb <. Els arguments van després de --:
$ cat script.sh
#!/bin/bash
echo "Hola des de $(hostname), arguments: $1 $2"
df -h / | tail -1
$ ssh joan@servidor 'bash -s' -- prod 42 < script.sh
Hola des de servidor, arguments: prod 42
/dev/sda1 117G 21G 92G 19% /
Si l’script ha de quedar al servidor (per executar-lo més endavant o des de cron), copia’l i executa’l:
scp script.sh joan@servidor:~/
ssh joan@servidor 'bash ~/script.sh'
5. Interactuar amb la comanda remota
L’entrada estàndard del teu terminal arriba a la comanda remota, de manera que li pots passar dades amb una canonada:
$ echo valor | ssh joan@servidor 'read -p "Introdueix un valor: " r; echo "Has escrit: $r"'
Has escrit: valor
El missatge de read -p no surt perquè read només el mostra quan l’entrada és un terminal. Si vols interactuar-hi tu de debò, afegeix -t.
Això també serveix per enviar fitxers per la canonada, sense scp:
tar -czf - projecte/ | ssh joan@servidor 'tar -xzf - -C ~/'
ssh joan@servidor 'mysqldump --single-transaction botiga' > botiga.sql
6. Amb un altre usuari
L’usuari remot és el que poses davant de l’@ (o amb -l):
ssh admin@servidor 'whoami'
Si has d’entrar amb el teu usuari però executar la comanda com un altre, fes-ho amb sudo -u al servidor:
ssh -t joan@servidor 'sudo -u www-data php /var/www/app/cron.php'
7. El mateix a molts servidors
Amb una llista de servidors en un fitxer, un per línia:
while read -r h; do
echo "== $h"
ssh -n "joan@$h" 'uptime'
done < servidors.txt
L’opció -n és imprescindible en aquest tipus de bucle: sense ella, ssh es queda l’entrada estàndard (la resta del fitxer servidors.txt) i el bucle s’acaba després del primer servidor. És un error molt habitual i difícil de veure.
Si la llista és curta, un for també va bé:
for h in web1 web2 db1; do ssh "joan@$h" 'uptime'; done
Per fer-ho en paral·lel, posa cada ssh en segon pla i espera que acabin tots amb wait. Desa la sortida de cada servidor en un fitxer perquè no es barregin:
for h in web1 web2 db1; do
ssh -n "joan@$h" 'apt list --upgradable 2>/dev/null' > "pendents_$h.txt" &
done
wait
Per a més servidors hi ha eines fetes per a això:
parallel-ssh(paquetpssha Debian i Ubuntu):parallel-ssh -h servidors.txt -i uptime.- Ansible, quan la feina passa de comandes soltes a mantenir configuracions: només necessita SSH i Python als servidors.
8. Tasques llargues
Si llances una tasca llarga i la connexió es talla (o tanques el portàtil), la tasca s’atura. Tens diverses opcions:
nohup i segon pla. La comanda continua encara que tanquis la sessió, i ssh torna de seguida:
ssh joan@servidor 'nohup ./copia.sh > copia.log 2>&1 &'
Cal redirigir la sortida (aquí, a copia.log). Si no, ssh es queda esperant. No cal -t: la versió anterior d’aquest article l’hi posava, però no aporta res en aquest cas.
tmux o screen. Si vols poder tornar a la sessió més tard i veure com va:
ssh -t joan@servidor 'tmux new -A -s copia'
Llança-hi la tasca, desconnecta’t amb Ctrl+B i després D, i torna-hi més tard amb la mateixa comanda. Ho tens explicat a la guia de tmux.
Mantenir viva la connexió. Si el problema és que les connexions inactives es tallen (per un tallafoc o un router), afegeix ServerAliveInterval 60 a ~/.ssh/config o fes servir ssh -o ServerAliveInterval=60.
Compte amb…
- Contrasenyes i secrets a la línia de comandes. El que posis a la comanda remota pot quedar a l’historial del teu shell i ser visible a la llista de processos del servidor. Passa’ls per l’entrada estàndard o per fitxers amb permisos restringits.
rmi comandes destructives en bucles. Prova primer el bucle amb una comanda innòcua (hostname,echo) i comprova que la llista de servidors és la que creus.- Comandes diferents segons la distribució. Si administres Debian i Rocky Linux alhora,
aptidnfno són intercanviables: comprova-ho abans a/etc/os-release.
Articles relacionats
- La comanda ssh: totes les opcions del client.
- Accés ràpid amb ~/.ssh/config: per escriure
ssh weben lloc de l’adreça sencera. - Generar claus amb ssh-keygen.
- /bin/bash: guia completa: per escriure els scripts que executaràs.