Що є найчистішим способом 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';"

тому що це надзвичайно потворно і важко читати.


2
Гм, а як щодо того, щоб все це ввести в сценарій на сервері і просто викликати його одним sshвикликом?
Микола Фетисов

@Nikolai якщо команда залежить від клієнтської сторони, вони можуть бути записані в сценарій оболонки, а потім scp, sshі виконання. Це буде найчистіший спосіб, я думаю.
хачик

6
Це частина більшого сценарію bash, тому я краще не розділяю його на половину, яка живе на моєму персональному комп’ютері, а інша половина живе на сервері та пробігається через ssh. Якщо це взагалі можливо, я дуже хотів би просто зберегти його як один сценарій, запущений з мого персонального комп'ютера. Чи справді немає чистого способу зафіксувати купу команд в ssh?
Елі

Найкращий спосіб - не використовувати bash, але Perl, Python, Ruby тощо
сальва

1
Чому ви хочете уникати ставити віддалені команди в лапки? Ви можете мати нові рядки всередині лапок, скільки завгодно; і використання рядка замість стандартного введення означає, що стандартний вхід доступний, наприклад, для введення читання у віддалений сценарій. (Хоча на Unix, звичайно, перевагу одинарних лапок над подвійними лапками, якщо вам спеціально не потрібна локальна оболонка для оцінки деяких частин рядка.)
tripleee

Відповіді:


457

Як щодо документа Bash Here :

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 не є терміналом.
Globalz

14
Вам може бути важливо процитувати слово (тобто спочатку "EOF"), щоб запобігти розширенню командних рядків. Дивіться: man bash | менше + / "Тут документи"
збираємось

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;

7
Це має велику перевагу в тому, що ви точно знаєте , що виконується віддаленим сценарієм - проблем із цитуванням немає. Якщо вам потрібні динамічні команди, ви можете скористатися скриптом оболонки з підрозділом, як і раніше, переходячи в ssh, тобто ( echo $mycmd $myvar ; ...) | ssh myhost- як при використанні котів, ви точно знаєте, що відбувається в потоці команд ssh. І звичайно, підзаголовок у скрипті може бути багаторядковим для читабельності - дивіться linuxjournal.com/content/bash-sub-shells
RichVel

6
Чи можете ви це зробити з аргументами в commands-to-execute-remotely.sh?
elaRosca

1
Я думаю, що це набагато корисніше рішення, ніж паутомблін. Оскільки сценарій можна переспрямовувати на будь-який ssh-сервер без необхідності відкривати файл. Крім того, це набагато читабельніше.
Патрік Басут

3
так, але використовуючи документ echo або тут (див. головну відповідь): використання: $localvarінтерпретувати локально визначену змінну, \$remotevarінтерпретувати віддалену певну змінну, \$(something with optionnal args)щоб отримати висновок чогось виконаного на віддаленому сервері. Приклад, який ви можете пропустити через 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
"

Мені подобається цей формат, однак він, на жаль, не корисний для зберігання даних std у змінній.
Signus

1
Signus, що ви маєте на увазі під "зберіганням std-даних у змінну"?
Андрій Б

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

Я не в змозі помістити всередині коду подвійних лапок таку команду, як цей файлToRemove = $ (find. -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

Ніколи не провалювався для мене.


Схоже на те, що я думав спочатку! Але навіщо це bash -sпотрібно?
flow2k

Також #!/bin/bashреально використовується?
flow2k

2
The -s існує для сумісності. Від man bash -s Якщо параметр -s присутній, або якщо аргументи не залишаються після обробки опцій, команди зчитуються зі стандартного вводу. Ця опція дозволяє встановлювати позиційні параметри при виклику інтерактивної оболонки.
RJ

1
RJ, дякую! З мого першого коментаря, схоже, ми щойно робимо ssh user@remote < /path/to/commands-inc.sh(здається, це працює для мене). Я думаю, ваша версія забезпечує, що ми використовуємо bashоболонку, а не якусь іншу оболонку - це і є мета, чи не так?
flow2k

2
Дякую. Так, деякі з нас мають свої нотатки та звички від старих версій баш та інших оболонок. :-)
RJ

10

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

ssh <remote-user>@<remote-host> "bash -s" <./remote-commands.sh

Дещо дивно, але це працює добре. І аргументи можна передавати до сценарію (до або після переадресації). наприклад, 'ssh <remote-user> @ <remote-host> "bash -s" arg1 <./ remote-commands.sh arg2', наприклад, 'ssh <remote-user> @ <remote-host> "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.
Jai Prakash

@JaiPrakash Спасибі за це, але я насправді думав, чи не зможемо ми взагалі покінчити з bashкомандою, тобто ssh remote-user@remote-host <./remote-commands.sh. Я спробував свій шлях, і, здавалося, це спрацювало, хоча я не впевнений, чи є він дефіцитним.
flow2k

@ flow2k, з мого розуміння сторінок "man", без цього варіанту не буде дефіциту. Це потрібно, якщо ви намагаєтесь передати будь-які аргументи. - дякую
Джай Пракаш

9

SSH та запустіть декілька команд у Bash.

Окремі команди з крапками з комою всередині рядка, передані на ехо, і все це передається команді 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, в майбутньому, будь ласка, опишіть, що ваш код робить коротко. Потім поговоріть про те, як це працює, а потім введіть код. Потім покладіть код в блок коду, щоб його було легко читати.
Ерік Лещинський

Із запитання ви запропонували саме те , чого хоче уникнути ОП: "Я не хочу мати сценарій
башти

6

Якщо хтось натрапив сюди, як я, - у мене був успіх уникнути крапки з комою та нової лінії:

Перший крок: крапка з комою. Таким чином, ми не порушуємо команду ssh:

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

Перелічено віддалений хост / домашній каталог (увійшов як 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 Це теж добре. Краще? Залежить від того, що ви хочете; запускайте другу команду умовно , тоді так, виконуйте її беззастережно (незалежно від того, чи вдалося спочатку чи не вдалося), тоді ні ...
Aconcagua

@MiroKropacek, мені не довелося уникати &&, коли ssh host "echo test && ls"
ставив

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 тощо.
Чарльз Аддіс,

5

Не впевнений, чи найчистіший для довгих команд, але, звичайно, найпростіший:

ssh user@host "cmd1; cmd2; cmd3"

Найкраще в простоті!
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

Частина "$ (який bash) -s" дає вам місце розташування bash на локальній машині, а не на віддаленій машині. Я думаю, що ви хочете замість цього "$ (що bash) -s" (одинарні лапки, щоб придушити підстановку локальних параметрів).
jsears

1
Пол Томблін дав відповідь на документ Bash Here 6 років раніше. Яку цінність додає ця відповідь?
jww

-sПрапор, я цитую його, ви, можливо, зможете піти, замінивши перший рядок на ... , і я не зміг уникнути, просто зателефонувавши в баш, злегка пам'ятаю.
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 / Cookbook / Мультиплексування .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.