Comment utiliser SSH pour exécuter un script shell sur une machine distante?

1223

Je dois exécuter un script shell (windows / Linux) sur une machine distante.

J'ai SSH configuré sur la machine A et B. Mon script est sur la machine A qui exécutera une partie de mon code sur une machine distante, la machine B.

Les ordinateurs locaux et distants peuvent être des systèmes Windows ou Unix.

Existe-t-il un moyen de faire cela en utilisant plink / ssh?

alpha_989
la source
6
La même question se trouve déjà sur serverfault: serverfault.com/questions/215756/… Il n'y a donc probablement aucun intérêt à migrer cette question.
sleske
9
La question sur Server Fault n'a cependant pas autant de réponses. Peut-être que cette question devrait remplacer celle-là.
Big McLargeHuge
5
J'aime personnellement cette réponse: unix.stackexchange.com/questions/87405/…
mikevoermans
27
En outre, il devrait évidemment être sur le sujet car ssh est un outil de choix pour le développement de logiciels.
static_rtti
4
Les questions café et ssh ne partagent pas le même degré de hors-sujet sur SO. A voté pour la réouverture.
Vincent Cantin

Réponses:

1194

Si la machine A est une boîte Windows, vous pouvez utiliser Plink (partie de PuTTY ) avec le paramètre -m, et il exécutera le script local sur le serveur distant.

plink root@MachineB -m local_script.sh

Si la machine A est un système basé sur Unix, vous pouvez utiliser:

ssh root@MachineB 'bash -s' < local_script.sh

Vous ne devriez pas avoir à copier le script sur le serveur distant pour l'exécuter.

Jason R. Coombs
la source
11
y a-t-il un avantage à utiliser l' -soption? cette page de manuel me porte à croire qu'elle traitera les entrées standard une fois les options de traitement terminées, qu'elles -ssoient utilisées ou non.
aeroNotAuto
79
Pour un script qui nécessite sudo, exécutez ssh root@MachineB 'echo "rootpass" | sudo -Sv && bash -s' < local_script.sh.
bradley.ayers
6
@ bradley.ayers n'oubliez pas de démarrer la commande avec un «espace» pour ignorer l'historique (PS, vous devez avoir HISTCONTROL=ignoreboth or ignorespaceà le faire fonctionner)
derenio
8
@ bradley.ayers dans quelles situations auriez-vous besoin de sudo si vous vous connectez déjà en tant que root?
Brian Schlenker
21
@Agostino, vous pouvez ajouter des paramètres comme celui-ci: les ssh root@MachineB ARG1="arg1" ARG2="arg2" 'bash -s' < local_script.sh crédits vont entièrement à la réponse de @chubbsondubs ci-dessous.
Yves Van Broekhoven
635

C'est une vieille question, et la réponse de Jason fonctionne bien, mais je voudrais ajouter ceci:

ssh user@host <<'ENDSSH'
#commands to run on remote host
ENDSSH

Cela peut également être utilisé avec su et les commandes qui nécessitent une entrée utilisateur. (notez l' 'hérédoc échappé)

Edit: Étant donné que cette réponse continue de recevoir des bits de trafic, j'ajouterais encore plus d'informations à cette merveilleuse utilisation de heredoc:

Vous pouvez imbriquer des commandes avec cette syntaxe, et c'est la seule façon dont l'imbrication semble fonctionner (d'une manière saine)

ssh user@host <<'ENDSSH'
#commands to run on remote host
ssh user@host2 <<'END2'
# Another bunch of commands on another host
wall <<'ENDWALL'
Error: Out of cheese
ENDWALL
ftp ftp.secureftp-test.com <<'ENDFTP'
test
test
ls
ENDFTP
END2
ENDSSH

Vous pouvez réellement avoir une conversation avec certains services comme telnet, ftp, etc. Mais n'oubliez pas que heredoc envoie simplement le stdin sous forme de texte, il n'attend pas de réponse entre les lignes

Edit: je viens de découvrir que vous pouvez mettre en retrait l'intérieur avec des onglets si vous utilisez <<-END!

ssh user@host <<-'ENDSSH'
    #commands to run on remote host
    ssh user@host2 <<-'END2'
        # Another bunch of commands on another host
        wall <<-'ENDWALL'
            Error: Out of cheese
        ENDWALL
        ftp ftp.secureftp-test.com <<-'ENDFTP'
            test
            test
            ls
        ENDFTP
    END2
ENDSSH

(Je pense que cela devrait fonctionner)

Voir également http://tldp.org/LDP/abs/html/here-docs.html

Yarek T
la source
4
vous pouvez temporiser un peu en ajoutant des lignes telles que: # $ (sleep 5)
Olivier Dulac
50
notez qu'avec des guillemets simples autour du terminator ( <<'ENDSSH'), les chaînes ne seront pas développées, les variables ne seront pas évaluées. Vous pouvez également utiliser <<ENDSSHou <<"ENDSSH"si vous souhaitez une extension.
maackle
3
Expectpeut être utilisé lorsque vous avez besoin d'automatiser des commandes interactives comme FTP.
programaths
5
Notez que j'avais un Pseudo-terminal will not be allocated because stdin is not a terminal.message. Il faut utiliser ssh avec des -t -tparamètres pour éviter cela. Voir ce fil sur SO
Buzut
8
Si vous essayez d'utiliser la syntaxe << - 'END', assurez-vous que votre délimiteur de fin hérédoc est mis en retrait à l'aide de TAB, pas d'espaces. Notez que copier / coller à partir de stackexchange vous donnera des espaces. Remplacez-les par des onglets et la fonction de retrait devrait fonctionner.
fbicknel
249

N'oubliez pas non plus d'échapper les variables si vous souhaitez les récupérer sur l'hôte de destination.

Cela m'a rattrapé dans le passé.

Par exemple:

user@host> ssh user2@host2 "echo \$HOME"

imprime / home / user2

tandis que

user@host> ssh user2@host2 "echo $HOME"

imprime / accueil / utilisateur

Un autre exemple:

user@host> ssh user2@host2 "echo hello world | awk '{print \$1}'"

imprime "bonjour" correctement.

dogbane
la source
2
Cependant, soyez conscient des points suivants: ssh user2@host 'bash -s' echo $HOME /home/user2 exit
errant.info
1
Juste pour ajouter que dans les forboucles exécutées en sshsession, la variable de boucle ne doit pas être échappée.
AlexeyDaryin du
1
Dans de nombreuses situations, la meilleure façon de corriger votre dernier exemple serait d' ssh user2@host2 'echo hello world' | awk '{ print $1 }'exécuter localement le script Awk. Si la commande à distance produit une immense production, vous devez éviter de tout copier sur le serveur local, bien sûr. Soit dit en passant, les guillemets simples autour de la commande à distance évitent le besoin de s'échapper.
tripleee
151

Ceci est une extension de la réponse de YarekT pour combiner les commandes distantes en ligne avec le passage des variables ENV de la machine locale à l'hôte distant afin que vous puissiez paramétrer vos scripts du côté distant:

ssh user@host ARG1=$ARG1 ARG2=$ARG2 'bash -s' <<'ENDSSH'
  # commands to run on remote host
  echo $ARG1 $ARG2
ENDSSH

J'ai trouvé cela extrêmement utile en gardant le tout dans un script, donc c'est très lisible et maintenable.

Pourquoi cela fonctionne. ssh prend en charge la syntaxe suivante:

ssh user @ host remote_command

En bash, nous pouvons spécifier des variables d'environnement à définir avant d'exécuter une commande sur une seule ligne comme ceci:

ENV_VAR_1 = 'value1' ENV_VAR_2 = 'value2' bash -c 'echo $ ENV_VAR_1 $ ENV_VAR_2'

Cela facilite la définition des variables avant d'exécuter une commande. Dans ce cas, l'écho est notre commande que nous exécutons. Tout avant l'écho définit les variables d'environnement.

Nous combinons donc ces deux fonctionnalités et la réponse de YarekT pour obtenir:

ssh user @ host ARG1 = $ ARG1 ARG2 = $ ARG2 'bash -s' << 'ENDSSH' ...

Dans ce cas, nous définissons ARG1 et ARG2 sur des valeurs locales. Envoi de tout après user @ host en tant que remote_command. Lorsque la machine distante exécute les commandes ARG1 et ARG2, les valeurs locales sont définies, grâce à l'évaluation en ligne de commande locale, qui définit les variables d'environnement sur le serveur distant, puis exécute la commande bash -s en utilisant ces variables. Voila.

chubbsondubs
la source
1
Notez que si vous voulez passer des arguments comme -a, vous pouvez utiliser -. par exemple 'ssh user @ host - -a foo bar' bash -s '<script.sh'. Et les arguments peuvent également aller après la redirection, par exemple 'ssh user @ host' bash -s '<script.sh - -a foo bar'.
gaoithe
8
Si l'une des valeurs env var contient des espaces, utilisez:ssh user@host "ARG1=\"$ARG1\" ARG2=\"$ARG2\"" 'bash -s' <<'ENDSSH'...
TalkLittle
Comme je l'ai écrit dans un autre commentaire, le but même de l'utilisation -sest de pouvoir appliquer des arguments à des scripts provenant de stdin. Je veux dire, vous pouvez aussi bien l'omettre si vous n'allez pas l'utiliser. Si vous l'utilisez, il n'y a aucune raison d'utiliser des variables d'environnement:ssh user@host 'bash -s value1 value2' <<< 'echo "$@"'
JoL
104
<hostA_shell_prompt>$ ssh user@hostB "ls -la"

Cela vous demandera un mot de passe, sauf si vous avez copié la clé publique de votre utilisateur hostA dans le fichier authorized_keys sur le répertoire d'origine de l'utilisateur .ssh. Cela permettra une authentification sans mot de passe (si elle est acceptée comme méthode d'authentification dans la configuration du serveur ssh)

Vinko Vrsalovic
la source
3
Vous a voté. Ceci est une solution valable. Évidemment, les clés doivent être protégées, mais elles peuvent également être invalidées tout comme un mot de passe via le côté serveur.
willasaywhat
8
Je ne pense pas que cela réponde à la question. L'exemple montre comment exécuter une commande distante, mais pas comment exécuter un script local sur une machine distante.
Jason R. Coombs
Pas certain, mais ne pouvez-vous pas diriger votre script sur hostA pour qu'il s'exécute sur hostB en utilisant cette méthode?
nevets1219
27

J'ai commencé à utiliser Fabric pour des opérations plus sophistiquées. Fabric nécessite Python et quelques autres dépendances, mais uniquement sur la machine cliente. Le serveur doit uniquement être un serveur ssh. Je trouve que cet outil est beaucoup plus puissant que les scripts shell transmis à SSH et vaut bien la peine d'être configuré (en particulier si vous aimez la programmation en Python). Fabric gère l'exécution de scripts sur plusieurs hôtes (ou hôtes de certains rôles), facilite les opérations idempotentes (comme l'ajout d'une ligne à un script de configuration, mais pas s'il est déjà là), et permet la construction d'une logique plus complexe (comme le Python la langue peut fournir).

Jason R. Coombs
la source
11

Essayez de courir ssh user@remote sh ./script.unx.

Jeremy
la source
8
Cela ne fonctionne que si le script se trouve dans le répertoire par défaut (home) de la télécommande. Je pense que la question est de savoir comment exécuter un script stocké localement sur la télécommande.
metasim
1
ssh username @ ip "chmod + x script.sh" <br/> ssh username @ ip "chemin vers le fichier sh dans l'hôte distant"
mani deepak
10
cat ./script.sh | ssh <user>@<host>
cglotr
la source
1
Très intéressant ...
Noah Broyles
5

J'utilise celui-ci pour exécuter un script shell sur une machine distante (testé sur / bin / bash):

ssh deploy@host . /home/deploy/path/to/script.sh
Est Ma
la source
3
ssh user@hostname ".~/.bashrc;/cd path-to-file/;.filename.sh"

fortement recommandé de source le fichier d'environnement (.bashrc / .bashprofile / .profile). avant d'exécuter quelque chose dans l'hôte distant car les variables d'environnement des hôtes cible et source peuvent être différées.

Ankush Sahu
la source
Cela n'explique pas comment déplacer un script local vers l'hôte distant.
kirelagin
2

si vous voulez exécuter une commande comme celle-ci temp=`ls -a` echo $temp dans ``, cela provoquera des erreurs.

la commande ci-dessous résoudra ce problème ssh user@host ''' temp=`ls -a` echo $temp '''

Jinmiao Luo
la source
1

La réponse ici ( https://stackoverflow.com/a/2732991/4752883 ) fonctionne très bien si vous essayez d'exécuter un script sur une machine Linux distante à l'aide de plinkou ssh. Cela fonctionnera si le script a plusieurs lignes linux.

** Cependant, si vous essayez d'exécuter un script de commandes situé sur une linux/windowsmachine locale et que votre machine distante l'est Windows, et qu'il se compose de plusieurs lignes utilisant **

plink root@MachineB -m local_script.bat

ne fonctionnera pas.

Seule la première ligne du script sera exécutée. Il s'agit probablement d'une limitation de plink.

Solution 1:

Pour exécuter un script batch multiligne (surtout s'il est relativement simple, composé de quelques lignes):

Si votre script de commandes d'origine est le suivant

cd C:\Users\ipython_user\Desktop 
python filename.py

vous pouvez combiner les lignes en utilisant le séparateur "&&" comme suit dans votre local_script.bat fichier: https://stackoverflow.com/a/8055390/4752883 :

cd C:\Users\ipython_user\Desktop && python filename.py

Après cette modification, vous pouvez ensuite exécuter le script comme indiqué ici par @ JasonR.Coombs: https://stackoverflow.com/a/2732991/4752883 avec:

`plink root@MachineB -m local_script.bat`

Solution 2:

Si votre script batch est relativement compliqué, il peut être préférable d'utiliser un script batch qui encapsule la commande plink ainsi que comme indiqué ici par @Martin https://stackoverflow.com/a/32196999/4752883 :

rem Open tunnel in the background
start plink.exe -ssh [username]@[hostname] -L 3307:127.0.0.1:3306 -i "[SSH
key]" -N

rem Wait a second to let Plink establish the tunnel 
timeout /t 1

rem Run the task using the tunnel
"C:\Program Files\R\R-3.2.1\bin\x64\R.exe" CMD BATCH qidash.R

rem Kill the tunnel
taskkill /im plink.exe
alpha_989
la source
1

Ce script bash fait ssh dans une machine distante cible, et exécute une commande sur la machine distante, n'oubliez pas d'installer expect avant de l'exécuter (sur mac brew install expect)

#!/usr/bin/expect
set username "enterusenamehere"
set password "enterpasswordhere"
set hosts "enteripaddressofhosthere"
spawn ssh  $username@$hosts
expect "$username@$hosts's password:"
send -- "$password\n"
expect "$"
send -- "somecommand on target remote machine here\n"
sleep 5
expect "$"
send -- "exit\n"
Mohammed Rafeeq
la source
1
c'est génial si vous devez utiliser des mots de passe ... cependant, pour le bénéfice de quiconque regarde à la maison, toute commande ssh doit utiliser une paire de clés publiques + privées et non des mots de passe ... une fois en place, mettez à jour votre serveur ssh pour fermer complètement les mots de passe
Scott Stensland
-1

Vous pouvez utiliser runoverssh :

sudo apt install runoverssh
runoverssh -s localscript.sh user host1 host2 host3...

-s exécute un script local à distance


Indicateurs utiles:
-g utilisez un mot de passe global pour tous les hôtes (invite de mot de passe unique)
-nutilisez SSH au lieu de sshpass, utile pour l'authentification par clé publique

maintenant à
la source
-24

Tout d'abord, copiez le script sur la machine B à l'aide de scp

[utilisateur @ machineA] $ scp / chemin / vers / script utilisateur @ machineB: / home / utilisateur / chemin

Ensuite, lancez simplement le script

[utilisateur @ machineA] $ ssh utilisateur @ machineB "/ home / user / path / script"

Cela fonctionnera si vous avez donné l'autorisation exécutable au script.

Baishampayan Ghose
la source
salut j'ai appliqué la suggestion suggérée mais cela me donne l'erreur suivante [oracle @ node1 ~] $ ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh ssh: node2: ./ home / oracle / au / fs / conn.sh: nom ou service inconnu [oracle @ node1 ~] $
'ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh'? Mauvaise ligne de commande, le nom de la commande doit être séparé de la partie user @ host avec un espace, pas deux points.
bortzmeyer
5
Je vote en aval parce que c'est la principale affirmation selon laquelle il ne peut pas être exécuté sans le copier est incorrect.
Jason R. Coombs
Il aurait été utile que Jason ajoute pourquoi c'est incorrect au lieu de simplement énoncer le fait. Peu serviable.
Kris
[user @ machineA] $ ssh root @ MachineB 'bash -s' </ machinea / path / to / script
Oleksii Kyslytsyn