Як використовувати SSH для запуску сценарію оболонки на віддаленій машині?


1220

Мені потрібно запустити скрипт оболонки (Windows / Linux) на віддаленій машині.

У мене SSH налаштовано як на машині A, так і B. Мій сценарій на машині A, який запустить частину мого коду на віддаленій машині, машині B.

Місцеві та віддалені комп’ютери можуть бути як Windows, так і Unix.

Чи є спосіб запустити це за допомогою plink / ssh?


6
Це ж питання вже є на сервері за замовчуванням: serverfault.com/questions/215756/… Тому, мабуть, немає сенсу мігрувати це питання.
sleske

9
На питання про помилку сервера не існує стільки відповідей. Можливо, це питання повинно замінити це.
Big McLargeHuge

5
Мені подобається ця відповідь особисто: unix.stackexchange.com/questions/87405/…
mikevoermans

27
Крім того, це, очевидно, повинно бути на темі, оскільки ssh є основним інструментом для розробки програмного забезпечення.
static_rtti

4
Питання щодо кави та ssh не мають однакової ступеня позатематичності щодо SO. Проголосували за повторне відкриття.
Вінсент Кантін

Відповіді:


1192

Якщо машина A - це вікно Windows, ви можете використовувати Plink (частина PuTTY ) з параметром -m, і він виконає локальний скрипт на віддаленому сервері.

plink root@MachineB -m local_script.sh

Якщо машина A - система на базі Unix, ви можете використовувати:

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

Вам не потрібно було копіювати скрипт на віддалений сервер, щоб запустити його.


11
чи є перевага у використанні -sопції? ця сторінка підводить мене до думки, що вона буде обробляти стандартні дані, коли буде виконано параметри обробки, -sвикористовуються вони чи ні.
aeroNotAuto

78
Для потрібного сценарію sudoзапустіть ssh root@MachineB 'echo "rootpass" | sudo -Sv && bash -s' < local_script.sh.
bradley.ayers

6
@ bradley.ayers пам’ятають, що почати команду з «пробілу», щоб пропустити історію (PS потрібно мати, HISTCONTROL=ignoreboth or ignorespaceщоб вона працювала)
derenio

8
@ bradley.ayers У яких ситуаціях вам знадобиться судо, якщо ви вже входите як root?
Брайан Шленкер

21
@Agostino, ви можете додати такі параметри: ssh root@MachineB ARG1="arg1" ARG2="arg2" 'bash -s' < local_script.sh Кредити повністю переходять до відповіді @chubbsondubs нижче.
Ів Ван Брукховен

633

Це старе запитання, і відповідь Джейсона працює чудово, але я хотів би додати це:

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

Це також можна використовувати з su та командами, які потребують введення користувачем. (зверніть увагу на 'втеченого гередока)

Редагувати: Оскільки ця відповідь продовжує отримувати шматочки трафіку, я би додав ще більше інформації до цього чудового використання heredoc:

Ви можете вкладати команди за допомогою цього синтаксису, і це єдиний спосіб введення гнізду, здається, працює (здоровим чином)

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

Ви насправді можете вести розмову з деякими службами, такими як telnet, ftp тощо. Але пам’ятайте, що heredoc просто надсилає stdin у вигляді тексту, він не чекає відповіді між рядками

Редагувати: Я щойно з’ясував, що ви можете відрізати нутрощі за допомогою вкладок, якщо використовуєте <<-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

(Я думаю, що це має спрацювати)

Також див. Http://tldp.org/LDP/abs/html/here-docs.html


4
ви можете трохи темпотіти, додавши рядки, такі як: # $ (сон 5)
Олів'є Дулак,

50
зауважте, що за допомогою одиничних лапок навколо термінатора ( <<'ENDSSH') рядки не будуть розширюватися, змінні не оцінюватимуться. Ви також можете використовувати <<ENDSSHабо, <<"ENDSSH"якщо ви хочете розширення.
макет

3
Expectможе використовуватися, коли вам потрібно автоматизувати інтерактивні команди типу FTP.
програмисти

5
Зауважте, що у мене було Pseudo-terminal will not be allocated because stdin is not a terminal.повідомлення. -t -tЩоб уникнути цього, потрібно використовувати ssh з парами. Дивіться цю тему на SO
Buzut

8
Якщо ви намагаєтеся використовувати синтаксис << - 'END', переконайтеся, що роздільник для закінчення heredoc є відступом за допомогою TAB, а не пробілів. Зауважте, що копіювання / вставка з stackexchange надасть вам пробіли. Змініть їх на вкладки, і функція відступу повинна працювати.
fbicknel

249

Крім того, не забувайте уникати змінних, якщо ви хочете забрати їх у хоста призначення.

Це мене піймало в минулому.

Наприклад:

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

друкує / home / user2

поки

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

друкує / дома / користувача

Ще один приклад:

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

правильно друкує "привіт".


2
Однак пам’ятайте про наступне: ssh user2@host 'bash -s' echo $HOME /home/user2 exit
errant.info

1
Просто додамо, що в forциклі, що працює в sshсеансі, змінну циклу не можна уникати.
Олексій Дарін

1
У багатьох ситуаціях розумним способом виправити останній приклад було б ssh user2@host2 'echo hello world' | awk '{ print $1 }'тобто запустити сценарій Awk локально. Якщо віддалена команда створює величезну кількість результатів, звичайно, ви хочете уникати копіювання всього цього на локальний сервер. До речі, поодинокі лапки навколо віддаленої команди уникають необхідності будь-якого втечі.
трійка

151

Це розширення до відповіді YarekT для комбінування вбудованих віддалених команд з передачею змінних ENV з локальної машини на віддалений хост, щоб ви могли параметризувати свої сценарії на віддаленій стороні:

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

Я вважав це надзвичайно корисним, зберігаючи все це в одному сценарії, щоб воно було легко читабельним та ретельним.

Чому це працює. ssh підтримує такий синтаксис:

ssh user @ host remote_command

У bash ми можемо вказати змінні середовища для визначення перед запуском команди в одному рядку так:

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

Це дозволяє легко визначити змінні перед запуском команди. У цьому випадку відлуння - це наша команда, яку ми виконуємо. Все перед відлунням визначає змінні середовища.

Тож ми поєднуємо ці дві особливості та відповідь YarekT, щоб отримати:

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

У цьому випадку ми встановлюємо ARG1 та ARG2 на локальні значення. Надсилання все після user @ хосту як віддалений_команда. Коли віддалена машина виконує команди ARG1 і ARG2, встановлюються локальні значення, завдяки локальній оцінці командного рядка, яка визначає змінні середовища на віддаленому сервері, а потім виконує команду bash -s, використовуючи ці змінні. Вуаля.


1
Зауважте, що якщо ви хочете передавати аргументи типу -a, тоді ви можете використовувати -. наприклад, 'ssh user @ host - -a foo bar' bash -s '<script.sh'. І аргументи також можуть надходити після переадресації, наприклад, 'ssh user @ host' bash -s '<script.sh - -a foo bar'.
gaoithe

8
Якщо будь-яке значення env var містить пробіли, використовуйте:ssh user@host "ARG1=\"$ARG1\" ARG2=\"$ARG2\"" 'bash -s' <<'ENDSSH'...
TalkLittle

Як я вже писав в іншому коментарі, суть використання -sполягає в тому, щоб мати можливість застосовувати аргументи до скриптів, що надходять через stdin. Я маю на увазі, ви можете також пропустити це, якщо ви не збираєтесь його використовувати. Якщо ви все-таки користуєтесь ним, немає ніяких причин використовувати змінні середовища:ssh user@host 'bash -s value1 value2' <<< 'echo "$@"'
JoL

104
<hostA_shell_prompt>$ ssh user@hostB "ls -la"

Це дозволить вам ввести пароль, якщо ви не скопіювали відкритий ключ користувача хостаA у файл дозволених ключів у домашній каталог користувача .ssh. Це дозволить провести автентифікацію без паролів (якщо прийнято як метод автентифікації в конфігурації ssh-сервера)


3
Ви проголосували. Це правильне рішення. Очевидно, що ключі повинні бути захищені, але вони також можуть бути визнані недійсними, як і пароль через сторону сервера.
Вільно, що

8
Я не думаю, що це відповідає на питання. На прикладі показано, як запустити віддалену команду, а не як виконати локальний скрипт на віддаленій машині.
Джейсон Р. Кумбс

Не впевнено, але ви не можете передати свій скрипт на hostA, щоб запустити на хостіB за допомогою цього методу?
nevets1219

27

Я почав використовувати Fabric для більш складних операцій. Для тканини потрібні Python та пара інших залежностей, але лише від клієнтської машини. Сервер повинен бути лише ssh-сервером. Мені здається, що цей інструмент набагато потужніший, ніж сценарії оболонки, передані в SSH, і цілком вартий проблеми з налаштуванням (особливо, якщо вам подобається програмування в Python). Тканина обробляє сценарії, що виконують декілька хостів (або хостів певних ролей), допомагає полегшити ідентичні операції (наприклад, додавання рядка до конфігураційного сценарію, але не, якщо він вже є), а також дозволяє побудувати більш складну логіку (наприклад, Python мова може надати).


11

Спробуйте запустити ssh user@remote sh ./script.unx.


8
Це працює лише в тому випадку, якщо скрипт знаходиться в каталозі за замовчуванням (домашній) на пульті дистанційного керування. Я думаю, питання полягає в тому, як запустити скрипт, який зберігається локально на пульті.
метасим

1
ssh username @ ip "chmod + x script.sh" <br/> ssh ім'я користувача @ ip "шлях до файлу sh до віддаленого хоста"
mani deepak


8

Якщо припустити, що ви хочете зробити це автоматично з "локальної" машини, не вручну входячи в "віддалену" машину, вам слід заглянути в розширення TCL, відоме як "Очікувати", воно розроблене саме для подібної ситуації. Я також надав посилання на сценарій для входу / взаємодії через SSH.

https://www.nist.gov/services-resources/software/expect

http://bash.cyberciti.biz/security/expect-ssh-login-script/


5

Я використовую цей сценарій для запуску сценарію оболонки на віддаленій машині (перевірено на / bin / bash):

ssh deploy@host . /home/deploy/path/to/script.sh

3
ssh user@hostname ".~/.bashrc;/cd path-to-file/;.filename.sh"

настійно рекомендується джерело файлу середовища (.bashrc / .bashprofile / .profile). перед тим, як запустити щось у віддаленому хості, тому що змінні середовища цілі та джерела хостів можуть бути відкладеними.


Це не пояснює, як перемістити локальний скрипт на віддалений хост.
kirelagin

2

якщо ви хочете виконати таку команду, як ця temp=`ls -a` echo $temp команда в ``, викличе помилки.

нижче команда вирішить цю проблему ssh user@host ''' temp=`ls -a` echo $temp '''


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 за допомогою:

`plink root@MachineB -m local_script.bat`

Рішення 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

1

Цей скрипт bash робить ssh на цільовій віддаленій машині та виконує якусь команду на віддаленій машині, не забудьте встановити очікуваний перед його запуском (на 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
це чудово, якщо ви повинні використовувати паролі ... однак для вигоди тих, хто дивиться вдома будь-яку команду ssh, слід використовувати пару відкритих + приватних ключів, а не паролі ... раз на місці оновіть свій ssh-сервер, щоб повністю відключити паролі
Скотт Стенсланн

-1

Ви можете використовувати runoverssh :

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

-s запускає локальний сценарій віддалено


Корисні прапори:
-g використовуйте глобальний пароль для всіх хостів (один запит на пароль),
-nвикористовуйте SSH замість sshpass, корисний для аутентифікації з відкритим ключем


-24

Спочатку скопіюйте сценарій на Machine B за допомогою scp

[user @ machineA] $ scp / path / to / script user @ machineB: / home / user / path

Потім просто запустіть сценарій

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

Це спрацює, якщо ви дали дозвіл на виконання сценарію.


привіт, я застосував рекомендовану пропозицію, але це дало мені таку помилку [oracle @ node1 ~] $ ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh ssh: node2: ./ home / oracle / au / fs / conn.sh: ім’я чи послуга невідомі [oracle @ node1 ~] $

'ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh'? Неправильний командний рядок, ім'я команди має бути відокремлено від частини користувача @ host пробілом, а не двокрапкою.
борцмейєр

5
Я спростовую це, оскільки основне твердження, що його неможливо запустити без його копіювання, є невірним.
Джейсон Р. Кумбс

Було б корисно, додав Джейсон, чому це неправильно, а не просто констатувати факт. Непомітний.
Kris

[user @ machineA] $ ssh root @ MachineB 'bash -s' </ machinea / path / to / script
Олексій
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.