Как использовать SSH для запуска сценария оболочки на удаленной машине?

1223

Я должен запустить сценарий оболочки (Windows / Linux) на удаленной машине.

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

Локальные и удаленные компьютеры могут быть системами на базе Windows или Unix.

Есть ли способ запустить это с помощью plink / ssh?

alpha_989
источник
6
Тот же вопрос уже связан с serverfault: serverfault.com/questions/215756/… Так что, вероятно, нет смысла переносить этот вопрос.
Слёске
9
На вопрос о сбое сервера не так много ответов. Может быть, этот вопрос должен заменить этот.
Big McLargeHuge
5
Мне нравится этот ответ лично: unix.stackexchange.com/questions/87405/…
mikevoermans
27
Кроме того, это должно быть очевидно по теме, поскольку ssh является основным инструментом для разработки программного обеспечения.
static_rtti
4
Вопросы о кофе и ssh не имеют одинаковой степени не по теме на SO. Проголосовал за открытие.
Винсент

Ответы:

1194

Если компьютер 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
79
Для сценария, который требует 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 в каких ситуациях вам понадобится sudo, если вы уже вошли в систему как root?
Брайан Шленкер
21
@Agostino, вы можете добавить такие параметры: ssh root@MachineB ARG1="arg1" ARG2="arg2" 'bash -s' < local_script.sh Кредиты полностью переходят к ответу @chubbsondubs ниже.
Ив Ван Брукховен
635

Это старый вопрос, и ответ Джейсона работает нормально, но я хотел бы добавить это:

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.
programaths
5
Обратите внимание, что у меня было Pseudo-terminal will not be allocated because stdin is not a terminal.сообщение. -t -tЧтобы избежать этого, нужно использовать ssh с параметрами. Смотрите эту тему на SO
Buzut
8
Если вы пытаетесь использовать синтаксис << - 'END', убедитесь, что конечный разделитель heredoc имеет отступ с использованием табуляции, а не пробелов. Обратите внимание, что копирование / вставка из stackexchange даст вам пробелы. Измените их на вкладки, и функция отступа должна работать.
fbicknel
249

Кроме того, не забудьте экранировать переменные, если вы хотите забрать их с хоста назначения.

Это застало меня в прошлом.

Например:

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

распечатывает / home / user2

пока

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

распечатывает / home / user

Другой пример:

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 локально. Если удаленная команда производит огромное количество выводимых данных, вы, конечно же, хотите избежать копирования всего этого обратно на локальный сервер. Между прочим, одинарные кавычки вокруг удаленной команды исключают необходимость экранирования.
tripleee
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 = 'значение1' ENV_VAR_2 = 'значение2' bash -c 'echo $ ENV_VAR_1 $ ENV_VAR_2'

Это облегчает определение переменных до запуска команды. В этом случае echo - наша команда, которую мы выполняем. Все до echo определяет переменные окружения.

Таким образом, мы объединяем эти две функции и ответ YarekT, чтобы получить:

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

В этом случае мы устанавливаем ARG1 и ARG2 в локальные значения. Отправка всего после user @ host как remote_command. Когда удаленный компьютер выполняет команду ARG1 и ARG2 устанавливают локальные значения, благодаря локальной оценке командной строки, которая определяет переменные среды на удаленном сервере, а затем выполняет команду bash -s с использованием этих переменных. Вуаля.

chubbsondubs
источник
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"

Это запросит у вас пароль, если только вы не скопировали открытый ключ пользователя hostA в файл author_keys в каталоге пользователя .ssh. Это позволит проводить аутентификацию без пароля (если она принята в качестве метода авторизации в конфигурации сервера ssh)

Винко Врсалович
источник
3
Проголосовал за тебя. Это правильное решение. Очевидно, что ключи должны быть защищены, но они также могут быть аннулированы так же, как пароль через серверную часть.
будет
8
Я не думаю, что это отвечает на вопрос. В этом примере показано, как выполнить удаленную команду, а не как выполнить локальный сценарий на удаленной машине.
Джейсон Р. Кумбс
Не уверен, но вы не можете передать свой скрипт на hostA для запуска на hostB, используя этот метод?
nevets1219
27

Я начал использовать Fabric для более сложных операций. Fabric требует Python и пару других зависимостей, но только на клиентском компьютере. Сервер должен быть только ssh-сервером. Я считаю, что этот инструмент гораздо более мощный, чем сценарии оболочки, переданные SSH, и стоит того, чтобы его настроить (особенно если вам нравится программирование на Python). Fabric обрабатывает выполняемые сценарии на нескольких хостах (или хостах определенных ролей), помогает упростить идемпотентные операции (такие как добавление строки в скрипт конфигурации, но не, если она уже есть) и позволяет создавать более сложную логику (такую ​​как Python). язык могу предоставить).

Джейсон Р. Кумбс
источник
11

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

Джереми
источник
8
Это работает, только если скрипт находится в каталоге по умолчанию (домашнем) на удаленном компьютере. Я думаю, что вопрос заключается в том, как запустить скрипт, хранящийся локально на пульте.
metasim
1
ssh username @ ip "chmod + x script.sh" <br/> ssh username @ ip "путь к файлу sh на удаленном хосте"
мани Дипак
10
cat ./script.sh | ssh <user>@<host>
cglotr
источник
1
Очень интересно ...
Ноа Бройлс
8

Предполагая, что вы хотите сделать это автоматически с «локального» компьютера, без ручного входа на «удаленный» компьютер, вам нужно посмотреть расширение TCL, известное как Expect, оно разработано именно для такой ситуации. Я также предоставил ссылку на скрипт для входа / взаимодействия через SSH.

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

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

Dexygen
источник
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). перед запуском чего-либо на удаленном хосте, потому что переменные окружения целевого и исходного хостов могут быть deffer.

Анкуш Саху
источник
Это не объясняет, как переместить локальный скрипт на удаленный хост.
Кирелагин
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
alpha_989
источник
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

Сначала скопируйте скрипт на компьютер B, используя scp

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

Затем просто запустите скрипт

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

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

Baishampayan Ghose
источник
Привет, я применил рекомендуемое предложение, но оно дает мне следующую ошибку [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'? Неправильная командная строка, имя команды должно быть отделено от части user @ host пробелом, а не двоеточием.
bortzmeyer
5
Я опровергаю это, потому что это основное утверждение, что его нельзя запустить без копирования, неверно.
Джейсон Р. Кумбс
Было бы полезно, добавил Джейсон, почему это неправильно, вместо того, чтобы просто констатировать факт. Бесполезный.
Крис
[user @ machineA] $ ssh root @ MachineB 'bash -s' </ machinea / path / to / script
Алексей Кислицын