Какой самый простой способ использовать ssh и запускать несколько команд в Bash?

349

У меня уже настроен агент ssh, и я могу запускать команды на внешнем сервере в скрипте Bash, выполняя такие вещи, как:

ssh blah_server "ls; pwd;"

Теперь, что я действительно хотел бы сделать, это запустить много длинных команд на внешнем сервере. Заключать все это в кавычки было бы довольно уродливо, и я бы действительно предпочел избегать ssh'ing несколько раз, чтобы избежать этого.

Итак, есть ли способ, которым я могу сделать это за один раз, заключенный в скобки или что-то? Я ищу что-то вроде:

ssh blah_server (
   ls some_folder;
   ./someaction.sh;
   pwd;
)

В принципе, я буду рад любому решению, если оно чистое.

редактировать

Чтобы прояснить, я говорю об этом как о части большего сценария bash. Другим людям может понадобиться разобраться со сценарием, поэтому я бы хотел сохранить его в чистоте. Я не хочу иметь bash-скрипт с одной строкой, которая выглядит следующим образом:

ssh blah_server "ls some_folder; ./someaction.sh 'some params'; pwd; ./some_other_action 'other params';"

потому что это очень некрасиво и трудно читать.

Eli
источник
2
Хм, а как насчет того, чтобы поместить все это в скрипт на сервере и просто вызвать его одним sshвызовом?
Николай Фетисов
@Nikolai если команда зависит от клиентской стороны, они могут быть записаны в сценарий оболочки, а затем scp, sshи выполнение. Это будет самый чистый путь, я думаю.
Хачик
6
Это часть большого скрипта bash, поэтому я бы не стал разделять его с половиной жизни на моем персональном компьютере, а другая половина - с сервером и через ssh. Если это вообще возможно, я бы хотел оставить его как один скрипт на моем персональном компьютере. Неужели нет чистого способа заключить несколько команд в ssh?
Эли
Лучше всего использовать не bash, а Perl, Python, Ruby и т. Д.
salva
1
Почему вы не хотите помещать удаленные команды в кавычки? Вы можете иметь новые строки внутри кавычек, сколько захотите; и использование строки вместо стандартного ввода означает, что стандартный ввод доступен, например, для чтения ввода в удаленный скрипт. (Хотя в Unix одинарные кавычки обычно предпочтительнее двойных, если вам не нужна локальная оболочка для оценки некоторых частей строки.)
tripleee

Ответы:

457

Как насчет Bash Here Document :

ssh otherhost << EOF
  ls some_folder; 
  ./someaction.sh 'some params'
  pwd
  ./some_other_action 'other params'
EOF

Чтобы избежать проблем, упомянутых @Globalz в комментариях, возможно, вам удастся (в зависимости от того, что вы делаете на удаленном сайте) заменить первую строку на

ssh otherhost /bin/bash << EOF

Обратите внимание, что вы можете выполнить подстановку переменных в документе Here, но вам, возможно, придется столкнуться с проблемами цитирования. Например, если вы заключите в кавычки «предельную строку» (то есть EOFв приведенном выше), то вы не сможете выполнять подстановки переменных. Но без цитирования предельной строки переменные подставляются. Например, если вы определили $NAMEвыше в своем сценарии оболочки, вы можете сделать

ssh otherhost /bin/bash << EOF
touch "/tmp/${NAME}"
EOF

и это создаст файл в месте назначения otherhostс именем того, что вы назначили $NAME. Другие правила, касающиеся цитирования сценариев оболочки, также применимы, но они слишком сложны, чтобы их здесь приводить.

Пол Томблин
источник
6
Это выглядит именно то, что я хочу! Как это работает? Может быть, у вас есть ссылка на страницу, которая объясняет это?
Эли
9
+1 Просто думал, что сам - вот одно место, чтобы прочитать об этом: tldp.org/LDP/abs/html/here-docs.html
bosmacs
14
Я также получаю этот вывод на свой локальный: псевдо-терминал не будет выделен, потому что stdin не является терминалом.
Глобалз
14
Для вас может быть важно заключить слово в кавычки (то есть сначала «EOF»), чтобы предотвратить расширение командных строк. Смотрите: человек Баш | меньше + / «Здесь документы»
сборка
6
Пол, я только предлагаю это, потому что с почти 200 тысячами просмотров по этому вопросу, похоже, что многие люди приходят сюда, когда пишут сценарии с помощью ssh. Обычно необходимо вводить значения при написании сценариев. Если бы не шум в других вопросах и комментариях, я бы просто сделал один и был на моем пути, но это вряд ли будет замечено на данном этапе. Сноска в одну строку может спасти людей от некоторых серьезных головных болей.
koyae
123

Отредактируйте ваш скрипт локально, затем передайте его в ssh, например

cat commands-to-execute-remotely.sh | ssh blah_server

где commands-to-execute-remotely.shвыглядит ваш список выше:

ls some_folder
./someaction.sh
pwd;
bosmacs
источник
7
Это имеет большое преимущество в том, что вы точно знаете , что выполняется удаленным скриптом - никаких проблем с цитированием. Если вам нужны динамические команды, вы можете использовать сценарий оболочки с подоболочкой, все еще пронизывающий ssh, то есть ( echo $mycmd $myvar ; ...) | ssh myhost- как и в случае использования cat, вы точно знаете, что происходит в потоке команд ssh. И, конечно , подоболочка в сценарии может быть несколько строк для удобства чтения - см linuxjournal.com/content/bash-sub-shells
RichVel
6
Можете ли вы сделать это с аргументами в commands-to-execute-remotely.sh?
elaRosca
1
Я думаю, что это гораздо более полезно, чем решение Paultomblin. Поскольку скрипт может быть перенаправлен на любой ssh-сервер без необходимости открывать файл. Также это намного более читабельно.
Патрик Бассут
3
да, но с использованием echo или документа here (см. верхний ответ): используйте: $localvarдля интерпретации локальной переменной, \$remotevarдля удаленной интерпретации дистанционно определенной переменной, \$(something with optionnal args)чтобы получить результат выполнения чего-либо на удаленном сервере. Пример того, что вы можете использовать ssh через 1 (или, как показано здесь, несколько команд ssh):echo " for remotedir in /*/${localprefix}* ; do cd \"\$remotedir\" && echo \"I am now in \$(pwd) on the remote server \$(hostname) \" ; done " | ssh user1@hop1 ssh user2@hop2 ssh user@finalserver bash
Оливье Дюлак
2
Хм ... как это отличается от использования перенаправления ввода, то есть ssh blah_server < commands-to-execute-remotely.sh?
flow2k
41

Чтобы соответствовать вашему примеру кода, вы можете заключить команды в одинарные или двойные кавычки. Например

ssh blah_server "
  ls
  pwd
"
Андрей Б
источник
Мне нравится этот формат, но, к сожалению, он бесполезен для хранения стандартных данных в переменной.
Signus
1
Сигнус, что ты имеешь в виду под "хранением стандартных данных в переменной"?
Андрей Б
1
@Signus Совершенно возможно сделать то, что вы описываете, хотя вы, вероятно, захотите использовать одинарные кавычки вместо двойных кавычек вокруг удаленных команд (или экранировать операторы, которые необходимо экранировать внутри двойных кавычек, чтобы предотвратить перехват вашей локальной оболочки и интерполируя их).
tripleee
Я не могу поместить в код двойных кавычек команду вроде этого fileToRemove = $ (найти. -Type f -name 'xxx.war'). fileToRemove должен иметь имя файла внутри, но вместо этого он имеет пустую строку. Нужно ли что-то избежать?
jkonst
37

Я вижу два пути:

Сначала вы создаете управляющий сокет следующим образом:

 ssh -oControlMaster=yes -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip>

и запускай свои команды

 ssh -oControlMaster=no -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip> -t <yourcommand>

Таким образом, вы можете написать команду ssh без фактического повторного подключения к серверу.

Вторым было бы динамически сгенерировать скрипт, scpзапустить его и запустить.

конечная станция
источник
20

Это также можно сделать следующим образом. Поместите ваши команды в скрипт, назовем его command-inc.sh

#!/bin/bash
ls some_folder
./someaction.sh
pwd

Сохранить файл

Теперь запустите его на удаленном сервере.

ssh user@remote 'bash -s' < /path/to/commands-inc.sh

Никогда не подводил для меня.

RJ
источник
Подобно тому, что я думал первоначально! Но зачем это bash -sнужно?
flow2k
Кроме того, #!/bin/bashдействительно используется?
flow2k
2
-S есть для совместимости. От man bash -s Если присутствует опция -s или если после обработки опции не осталось аргументов, команды считываются из стандартного ввода. Эта опция позволяет устанавливать позиционные параметры при вызове интерактивной оболочки.
RJ
1
RJ, спасибо! С моим первым комментарием, похоже, что мы просто делаем ssh user@remote < /path/to/commands-inc.sh(кажется, работает для меня). Ну, я полагаю, ваша версия гарантирует, что мы используем bashоболочку, а не какую-то другую оболочку - вот в чем цель, не так ли?
flow2k,
2
Спасибо. Да, у некоторых из нас есть свои заметки и привычки из старых версий bash и других оболочек. :-)
RJ
10

Поместите все команды в сценарий, и он может быть запущен как

ssh <remote-user>@<remote-host> "bash -s" <./remote-commands.sh
Джай Пракаш
источник
Несколько странно, но это хорошо работает. И аргументы могут быть переданы в сценарий (до или после перенаправления). например, 'ssh <удаленный пользователь> @ <удаленный хост> "bash -s" arg1 <./ remote-commands.sh arg2' например, 'ssh <удаленный пользователь> @ <удаленный хост> "bash -s" - - -arg1 <./ remote-commands.sh arg2 '
gaoithe
У меня был похожий вопрос с другим ответом выше, но какова цель включения bash -s?
flow2k
1
@ flow2k -s указывает на bash для чтения команд со стандартного ввода. Вот описание со manстраницIf the -s option is present, or if no arguments remain after option processing, then commands are read from the standard input. This option allows the positional parameters to be set when invoking an interactive shell.
Джай Пракаш
@JaiPrakash Спасибо за это, но я действительно думаю , если мы можем просто покончить с bashкомандой в целом, то есть ssh remote-user@remote-host <./remote-commands.sh. Я попробовал свой путь, и это, казалось, сработало, хотя я не уверен, что это каким-то образом недостаточно.
flow2k
@ flow2k из моего понимания страниц справочника не будет недостатка без этой опции. Это необходимо, если вы пытаетесь передать какие-либо аргументы. - спасибо
Jai Prakash
9

SSH и запуск нескольких команд в Bash.

Отдельные команды с точкой с запятой в строке, передаваемые в echo, передаются в команду ssh. Например:

echo "df -k;uname -a" | ssh 192.168.79.134

Pseudo-terminal will not be allocated because stdin is not a terminal.
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/sda2       18274628 2546476  14799848  15% /
tmpfs             183620      72    183548   1% /dev/shm
/dev/sda1         297485   39074    243051  14% /boot
Linux newserv 2.6.32-431.el6.x86_64 #1 SMP Sun Nov 10 22:19:54 EST 2013 x86_64 x86_64 x86_64 GNU/Linux
Арнаб
источник
arnab, в будущем опишите, пожалуйста, вкратце, что делает ваш код. Затем поговорите о том, как это работает, затем вставьте код. Затем поместите код в блок кода, чтобы его было легко прочитать.
Эрик Лещинский
Исходя из вопроса, вы предложили именно то , чего ОП хочет избежать: «Я не хочу иметь bash-скрипт с одной строкой, которая выглядит как ...»
jww
6

Для любого, кто споткнулся здесь, как я - мне удалось избежать точки с запятой и новой строки:

Первый шаг: точка с запятой. Таким образом, мы не нарушаем команду ssh:

ssh <host> echo test\;ls
                    ^ backslash!

Перечислен удаленный каталог hosts / home (вошел как root), тогда как

ssh <host> echo test;ls
                    ^ NO backslash

перечислил текущий рабочий каталог.

Следующий шаг: разрыв строки:

                      v another backslash!
ssh <host> echo test\;\
ls

Это снова перечислило удаленный рабочий каталог - улучшено форматирование:

ssh <host>\
  echo test\;\
  ls

Если на самом деле лучше, чем здесь документ или цитаты из пунктирных линий - ну, не мне решать ...

(Используя bash, Ubuntu 14.04 LTS.)

Аконкагуа
источник
1
Что еще лучше, вы можете использовать && и || и сюда тоже - эхо тест \ & \ & ls
Миро Кропачек
@MiroKropacek Это тоже хорошо. Лучше? Зависит от того, что ты хочешь; выполнить вторую команду условно , затем да, выполнить ее безоговорочно (независимо от того, была ли первая успешной или неудачной), затем нет ...
Аконкагуа
@MiroKropacek, я не должен был избегать &&, когда ставил двойные кавычки вокруг команд:ssh host "echo test && ls"
not2savvy
5

Отправленные ответы с использованием многострочных строк и нескольких сценариев bash не работали для меня.

  • Длинные многострочные строки трудно поддерживать.
  • Отдельные скрипты bash не поддерживают локальные переменные.

Вот функциональный способ ssh и запуска нескольких команд, сохраняя локальный контекст.

LOCAL_VARIABLE=test

run_remote() {
    echo "$LOCAL_VARIABLE"
    ls some_folder; 
    ./someaction.sh 'some params'
    ./some_other_action 'other params'
}

ssh otherhost "$(set); run_remote"
Михаил Петрочук
источник
3
Вы думаете об этом, как будто единственная причина, по которой кто-то захочет это сделать, - это то, что он сидит в командной строке. У этого вопроса есть и другие распространенные причины, такие как выполнение команд в распределенных средах с помощью таких инструментов, как rundeck, jenkins и т. Д.
Charles Addis,
это работает, но я получаю нежелательные сообщения об ошибках
Аннари
5

Не уверен, что самый чистый для длинных команд, но, конечно, самый простой:

ssh user@host "cmd1; cmd2; cmd3"
DimiDak
источник
Лучший в простоте!
not2savvy
4

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

#!/bin/bash
ssh <my_user>@<my_host> "bash -s" << EOF
    # here you just type all your commmands, as you can see, i.e.
    touch /tmp/test1;
    touch /tmp/test2;
    touch /tmp/test3;
EOF
sjas
источник
Часть «$ (which bash) -s» дает вам местоположение bash на локальной машине, а не на удаленной машине. Я думаю, что вы хотите вместо этого использовать $ (which bash) -s (одинарные кавычки для подавления подстановки локальных параметров).
jsears
1
Пол Томблин предоставил ответ Bash Here Document 6 лет назад. Какую ценность добавляет этот ответ?
jww
-sфлаг, я процитирую его, вы можете избежать замены первой строки на ... , и я не смог уйти, просто вызвав bash, смутно помню.
Sjas
4

Самый простой способ настроить систему на использование одиночных сессий ssh ​​по умолчанию с мультиплексированием.

Это можно сделать, создав папку для сокетов:

mkdir ~/.ssh/controlmasters

А затем добавьте следующее в вашу конфигурацию .ssh:

Host *
    ControlMaster auto
    ControlPath ~/.ssh/controlmasters/%r@%h:%p.socket
    ControlMaster auto
    ControlPersist 10m

Теперь вам не нужно изменять какой-либо ваш код. Это позволяет выполнять несколько вызовов ssh и scp без создания нескольких сессий, что полезно, когда требуется больше взаимодействия между локальными и удаленными компьютерами.

Благодаря ответу @ terminus, http://www.cyberciti.biz/faq/linux-unix-osx-bsd-ssh-multiplexing-to-speed-up-ssh-connections/ и https://en.wikibooks.org / wiki / OpenSSH / Кулинарная книга / Мультиплексирование .

Джонатан
источник