Как запустить локальный скрипт bash на удаленных машинах через ssh?

51

Я ищу способ перенести конфигурацию с одного центрального компьютера на несколько удаленных машин без необходимости устанавливать что-либо на удаленных машинах.

Цель состоит в том, чтобы сделать что-то похожее на инструменты, подобные тем cfengine, но на множестве машин, на которых не настроены агенты. На самом деле это может быть хорошей техникой настройки cfagentна множестве существующих удаленных машин.

tremoloqui
источник
Похоже на SO: stackoverflow.com/q/305035/435605
Алик Эльзин-килака
1
Фактические вопросы имеют 23 возражения, где дубликат на SO имеет 55: P
MoJo

Ответы:

55

Вы можете передать сценарий и заставить его исполниться эфемерно, отправив его и выполнив оболочку.

например

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

Естественно, эту "ls -l; echo 'Hello World'"часть можно заменить скриптом bash, который хранится в файле на локальном компьютере.

например

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

Ура!

tremoloqui
источник
2
Как сделать так, чтобы это выполнялось как sudo на удаленном system.eg Если бы я вошел на удаленный сервер, я обычно запускал бы это как sudo -u testuser script.sh
Sharjeel
Что если сценарий, который я вызываю, включает взаимодействие с пользователем ???
Абхиманью Шривастава
20

Есть несколько способов сделать это.

1:

ssh user@remote_server 'bash -s' < localfile

2:

cat localfile  | ssh user@remote_server

3:

ssh user@remote_server "$(< localfile)"

номер 3 - мой предпочтительный способ, он позволяет интерактивные команды, например su -S service nginx restart

(# 1 будет использовать остальную часть скрипта в качестве ввода для вопроса о пароле, когда вы используете su -S.)

Пол Вершур
источник
4
Что касается запуска локального сценария на удаленной машине - есть ли способ отправить переменную в качестве аргумента на удаленную машину? т.е. вместе со скриптом я хочу отправить переменную (имеющую несколько строк) в качестве аргумента на удаленную машину. Затем сценарий намеревается использовать переменную.
13

Я бы порекомендовал Python's Fabric для этой цели:

#!/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

Вы должны быть в состоянии использовать вышеупомянутое, чтобы начать. Обратитесь к отличной документации Fabric, чтобы сделать все остальное. Как дополнение, вы можете полностью написать свой скрипт в Fabric - копирование не требуется, однако следует отметить, что для изменения скрипта на всех машинах вам нужно будет только отредактировать локальную копию и заново развернуть ее. Более того, немного больше, чем просто базовое использование API, вы можете изменить сценарий в зависимости от того, на каком хосте он в данный момент работает и / или других переменных. Это своего рода питонический Ожидание.

Сэм Халике
источник
Я не думаю, что это точно отвечает на вопрос, но мне нравится идея, и я мог бы видеть Fabric как полезный инструмент.
тремолоки
1
@tremoloqui Fabric - это оболочка Python для ssh - на целевых машинах ничего не нужно устанавливать, кроме скрипта, который передается. Что, если переписать в виде серии команд Fabric (используя runи sudo), даже не нужно.
Изката
5

Это именно то, для чего используется Ansible. Там нет агента, вам просто нужно создать текстовый файл с именем:

/etc/ansible/hosts

с контентом, который выглядит примерно так:

[webhosts]
web[1-8]

Это будет означать, что машины "web1, web2 ... web8" находятся в группе "webhosts". Тогда вы можете делать такие вещи, как:

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

перезапустить службу apache2 на всех ваших машинах, используя sudo.

Вы можете делать на лету команды, такие как:

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

или вы можете запустить локальный скрипт на удаленной машине:

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

или вы можете создать playbook (подробности смотрите в документации) с полной конфигурацией, которой вы хотите, чтобы ваши серверы соответствовали, и развернуть ее:

ansible-playbook webplaybook.yml

По сути, вы можете начать использовать его в качестве инструмента командной строки для запуска команд на нескольких серверах и расширить его использование в полноценный инструмент настройки по своему усмотрению.

seumasmac
источник
3
Мне нравится ansible так же, как и следующему парню, но если он спрашивает о скрипте, то у ansible есть действительно хороший скрипт-модуль :ansible webhosts -m script script.sh
ptman
1
Все остальные ответы включают bash-скрипт, но это не то, что он специально просил. Он просто сказал, что отправляю конфигурацию на удаленные машины. Но хорошее упоминание о модуле сценария :)
seumasmac
Пожалуйста, проголосуйте за этот ответ! Это способ сделать это!
птенцы
@ptman Я только что заметил, что, хотя он не упоминает сценарий в вопросе, он упоминает в названии! Сожалею. Я обновил.
Сеумасмак
3

Как объясняется в этом ответе, вы можете использовать heredoc :

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

Вы должны быть осторожны с heredoc, потому что он просто отправляет текст, но на самом деле не ждет ответа. Это означает, что он не будет ждать выполнения ваших команд.

BЈовић
источник
1

Ответ здесь ( https://stackoverflow.com/a/2732991/4752883 ) прекрасно работает, если вы пытаетесь запустить скрипт на удаленной машине Linux с помощью plinkили ssh. Это будет работать, если скрипт имеет несколько строк linux.

** Однако, если вы пытаетесь запустить пакетный скрипт, расположенный на локальном linux/windowsкомпьютере, а ваш удаленный компьютер есть Windows, и он состоит из нескольких строк, используя **

plink root@MachineB -m local_script.bat

это не сработает.

Будет выполнена только первая строка скрипта. Это, вероятно, ограничение plink.

Решение 1:

Чтобы запустить многострочный пакетный скрипт (особенно, если он относительно простой и состоит из нескольких строк):

Если ваш оригинальный пакетный скрипт выглядит следующим образом

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

Вы можете объединить строки, используя разделитель «&&», как показано в вашем local_script.batфайле следующим образом https://stackoverflow.com/a/8055390/4752883 :

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

После этого изменения вы можете запустить скрипт, как указано здесь @ JasonR.Coombs: https://stackoverflow.com/a/2732991/4752883

Решение 2:

Если ваш пакетный сценарий относительно сложен, может быть лучше использовать пакетный сценарий, который инкапсулирует команду plink, а также следующие действия, указанные здесь @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
источник
0

Почему бы просто не скопировать сначала сценарий, а затем запустить его?

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

Конечно, вы должны быть осторожны, чтобы не загружать его в доступное для записи место, чтобы никто другой не мог возиться с ним до того, как вы запустите его (возможно, от имени root).

Дейв Фогт
источник
1
Причина, по которой я не хочу загружать скрипт, заключается в том, что им не нужно управлять и иметь риски, о которых вы упомянули. Кроме того, мне кажется, что это проще, чем многоэтапный процесс загрузки, обработки и (необязательно) удаления.
тремолоки
0

Перепишите сценарий таким образом , что каждая команда в нем уже будет с приставкой SSH и имя хоста / IP или список таких передается в скрипт в качестве аргумента (если у вас есть ключ аутентификации SSH-агент без пароля / настройки). Может потребоваться некоторая работа для правильной передачи кодов ошибок / возврата из удаленных команд ....

rackandboneman
источник
0

Если скрипт не слишком большой, и вы используете bash или ksh ...

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

И stdin, и stdout работают правильно, но сценарий ограничен размером аргумента (обычно около 100 КБ). Аргументы сценария могут работать в конце строки, возможно, после дополнительного аргумента "-". «-T» для выделения pty является необязательным.

Осторожно: это смущает завершение bash, не нажимайте вкладку.

user3710044
источник