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?


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.


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


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.


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.


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)


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).


11

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


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



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

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.


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 '''


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

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"

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


-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.


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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.