Comment exécuter un script bash local sur des machines distantes via ssh?

51

Je cherche un moyen de transférer la configuration d'une machine centrale à plusieurs machines distantes sans avoir à installer quoi que ce soit sur les machines distantes.

L'objectif est de faire quelque chose comme vous le feriez avec des outils tels que cfengine, mais sur un ensemble de machines sur lesquelles aucun agent n'est configuré. Cela pourrait en fait être une bonne technique d'installation cfagentsur un ensemble de machines distantes existantes.

Trémoloqui
la source
Similaire sur SO: stackoverflow.com/q/305035/435605
AlikElzin-kilaka
1
La question actuelle a 23 votes positifs, alors que le doublon sur SO en a 55: P
MoJo

Réponses:

55

Vous pouvez transmettre un script et le faire exécuter de manière éphémère en le connectant puis en exécutant un shell.

par exemple

echo "ls -l; echo 'Hello World'" | ssh me@myserver /bin/bash

Naturellement, la "ls -l; echo 'Hello World'"pièce pourrait être remplacée par un script bash stocké dans un fichier sur la machine locale.

par exemple

cat script.sh | ssh me@myserver /bin/bash

À votre santé!

Trémoloqui
la source
2
Comment puis-je exécuter cette exécution en tant que sudo sur le système distant. Si j'étais connecté sur le serveur distant, je l'utilisais généralement en tant que sudo -u testuser script.sh
Sharjeel
Et si le script que j'appelle implique des interactions utilisateur ???
Abhimanyu Srivastava
20

Il y a plusieurs façons de le faire.

1:

ssh user@remote_server 'bash -s' < localfile

2:

cat localfile  | ssh user@remote_server

3:

ssh user@remote_server "$(< localfile)"

le numéro 3 est ma manière préférée, il permet des commandes interactives, par exemple su -S service nginx restart

(# 1 utilisera le reste du script comme entrée pour la question du mot de passe lorsque vous l'utiliserez su -S.)

Paul Verschoor
la source
4
En ce qui concerne l'exécution d'un script local sur une machine distante - Existe-t-il un moyen d'envoyer une variable sous forme d'argument à la machine distante? avec le script, je veux envoyer une variable (ayant plusieurs lignes) en argument à la machine distante. Le script a ensuite l'intention d'utiliser la variable.
13

Je recommanderais Fabric en python à cette fin:

#!/usr/bin/python
# ~/fabfile.py

from fabric_api import *

env.hosts = ['host1', 'host2']
def deploy_script():
    put('your_script.sh', 'your_script.sh', mode=0755)
    sudo('./your_script.sh')

# from shell
$ fab deploy_script

Vous devriez pouvoir utiliser ce qui précède pour commencer. Consultez l'excellente documentation de Fabric pour faire le reste. En tant qu’additif, il est tout à fait possible d’écrire votre script entièrement dans Fabric - aucune copie n’est nécessaire, mais il convient de noter que pour modifier le script sur toutes les machines, il vous suffit de modifier la copie locale et de le redéployer. En outre, avec un peu plus que l’utilisation de base de l’API, vous pouvez modifier le script en fonction de l’hôte sur lequel il est actuellement exécuté et / ou d’autres variables. C'est une sorte d'Expect pythonique.

Sam Halicke
la source
Je ne pense pas que cela réponde exactement à la question, mais j'aime bien l'idée et je pourrais voir le tissu comme un outil utile.
Trémoloqui
1
@tremoloqui Fabric est un wrapper python autour de ssh - rien ne doit être installé sur les machines cibles, enregistrez le script en cours de transfert. Qui, s'ils sont réécrits sous forme d'une série de commandes Fabric (à l'aide de runet sudo), n'est même pas nécessaire.
Izkata
5

C'est exactement ce à quoi Ansible est utilisé. Il n'y a pas d'agent, il suffit de créer un fichier texte appelé:

/etc/ansible/hosts

avec un contenu qui ressemble à quelque chose comme:

[webhosts]
web[1-8]

Cela spécifierait que les machines "web1, web2 ... web8" sont dans le groupe "hôtes Web". Ensuite, vous pouvez faire des choses comme:

ansible webhosts -m service -a "name=apache2 state=restarted" --sudo

pour redémarrer le service apache2 sur toutes vos machines, en utilisant sudo.

Vous pouvez faire des commandes à la volée comme:

ansible webhosts -m shell -a "df -h"

ou vous pouvez exécuter un script local sur la machine distante:

ansible webhosts -m script -a "./script.sh"

ou vous pouvez créer un playbook (consultez la documentation pour plus de détails) avec une configuration complète à laquelle vos serveurs doivent se conformer et le déployer avec:

ansible-playbook webplaybook.yml

En gros, vous pouvez commencer à l'utiliser comme un outil de ligne de commande pour exécuter des commandes sur plusieurs serveurs et à étendre son utilisation en un outil de configuration complet à votre guise.

seumasmac
la source
3
J'aime autant Ansible que son voisin, mais s'il pose une question sur un script, Ansible a un très bon module de script :ansible webhosts -m script script.sh
vendredi
1
Toutes les autres réponses impliquent un script bash, mais ce n'est pas ce qu'il a spécifiquement demandé. Il vient de dire de pousser la configuration sur des machines distantes. Mais bonne mention du module de script :)
seumasmac
S'il vous plaît voter cette réponse! C'est le moyen de le faire!
poussins
@ptman Je viens juste de remarquer que s'il ne mentionne pas de script dans la question, il le fait dans le titre! Pardon. J'ai mis à jour.
seumasmac
3

Comme expliqué dans cette réponse, vous pouvez utiliser heredoc :

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

Vous devez faire attention avec heredoc, car il envoie simplement du texte, mais il n'attend pas vraiment la réponse. Cela signifie qu'il n'attendra pas que vos commandes soient exécutées.

BЈовић
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 traitement par lots situé sur une linux/windowsmachine locale et que votre machine distante l’est Windows, elle se compose de plusieurs lignes utilisant **

plink root@MachineB -m local_script.bat

ça ne marchera pas.

Seule la première ligne du script sera exécutée. C'est probablement une limitation de plink.

Solution 1:

Pour exécuter un script de commandes multiligne (en particulier s'il est relativement simple et composé de quelques lignes):

Si votre script de lot d'origine est comme suit

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.batfichier comme suit : 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

Solution 2:

Si votre script de traitement par lots est relativement compliqué, il peut être préférable d’utiliser un script de traitement par lots qui encapsule la commande plink ainsi que suit, 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
0

Pourquoi ne pas simplement copier le script d'abord, puis l'exécuter?

scp your_script.sh the_server:
ssh the_server "chmod +x your_script.sh; ./your_script.sh"

Bien sûr, vous devez faire attention à ne pas le télécharger dans un endroit accessible en écriture, afin que personne d'autre ne puisse le tripoter avant de l'exécuter (éventuellement en tant que root).

Dave Vogt
la source
1
La raison pour laquelle je ne souhaite pas télécharger le script est qu'il n'a pas besoin d'être géré et qu'il comporte les risques que vous avez mentionnés. De plus, il me semble que c'est plus simple que le processus en plusieurs étapes de téléchargement, de traitement et (éventuellement) de suppression.
Trémoloqui
0

Réécrire le script d'une manière que chaque commande déjà est préfixé avec ssh et un nom d' hôte / IP ou une liste de tels est passé au script comme argument ( en supposant que vous avez passwordless / authentification clé ssh-agent mis en place). Certains travaux peuvent être nécessaires pour transmettre correctement les codes d’erreur / retour à partir des commandes à distance ....

rackandboneman
la source
0

Si le script n'est pas trop gros et que vous utilisez bash ou ksh ...

ssh vm24 -t bash -c "$(printf "%q" "$(< shell-test.sh )")"

Stdin et stdout fonctionnent correctement, mais le script est limité à la taille de l'argument (généralement environ 100 Ko). Les arguments du script peuvent fonctionner à la fin de la ligne, éventuellement en suivant un argument "-" supplémentaire. Le "-t" pour allouer un pty est optionnel.

Méfiez-vous: cela confond la finalisation de bash, ne touchez pas l'onglet.

utilisateur3710044
la source