Запустить локальный скрипт с локальным входным файлом на удаленном хосте

Запустить локальный скрипт с локальным входным файлом на удаленном хосте

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

ssh <remote_server> "bash -s" < ./local_script.sh

Это прекрасно работает и даже позволяет мне передавать параметрыlocal_script.sh. Однако мне бы очень хотелось передать ему входной файл, например:

local_script.sh < local_file.txt

Объединение этих двух утверждений дает:

ssh <remote_server> "bash -s" < ./local_script.sh < local_file.txt

Когда я это запускаю, я получаю

bash: line 1: FOO: command not found
bash: line 2: BAR: command not found
bash: line 3: BAZ: command not found
...

где FOO, BAR, BAZ и т.д. — первые слова в каждой строкелокальный_файл.txt:

FOO     FOO_PARAM1
BAR     BAR_PARAM1
BAZ     BAZ_PARAM2
...

Итак, похоже, что "bash -s"на удаленном сервере происходит интерпретациялокальный_файл.txtкак файл сценария, а не входной файл дляlocal_script.sh. Есть ли способ исправить это (кроме создания скрипта-обертки)?

решение1

Хотя sshон обеспечивает два отдельных выходных потока (для stdout и stderr), он подает только один входной поток (stdin). Поэтому вам нужно будет либо передать содержимое скрипта, либо входной файл через разные механизмы.

Например, один через переменную, другой через stdin:

LC_CODE=$(cat local_script.sh) ssh host 'eval "$LC_CODE"' < input

(предполагая, что и ваш sshклиент передает LC_*переменную ( SendEnvin ssh_config), и sshdсервер принимает ее ( AcceptEnvin sshd_config))

Или просто передайте содержимое local_script.shкак удаленный код оболочки, предполагая, что оболочка входа удаленного пользователя соответствует синтаксису этого скрипта:

ssh host "$(cat local_script.sh)" < input

Или объединить код и ввод, поданный на sshstdin, следующим образом:

{
  echo '{'; cat local_script.sh; echo '}; exit'
  cat input
} | ssh host 'bash -s some arguments'

Здесь использование bashbecause bashпозаботится о чтении входных данных по одному байту за раз, чтобы не читать дальше этой }; exitстроки, хотя не все другие оболочки делают это.

Или, если sedна удаленном хосте находится GNU sed:

echo '#END-OF-SCRIPT' | cat local_script.sh - input |
  ssh host 'set some arguments; eval "$(sed -u "/^#END-OF-SCRIPT/q")"'

(здесь код оценивается оболочкой входа удаленного пользователя и предполагается, что он похож на Bourne)

решение2

Вот более элегантное решение с использованием оператора BASH 'here-strings' ('<<<')

ssh -t host "/bin/bash <(base64 --decode <<<'$(base64 < script)' )"

Он берет скрипт, преобразует его в строку base64, в команде SSH преобразует его обратно и передает в bash как имя файла канала.

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

ssh -t host "/bin/bash --rcfile <(base64 --decode <<<'$(base64 < script)' )"

Еще... https://antofthy.gitlab.io/info/apps/ssh_remote_commands.txt

решение3

Это работает хорошо и даже позволяет передавать в скрипт содержимое нескольких файлов:

ssh host "bash -s -- '$(cat local_file_1.txt)'  '$(cat local_file_2.txt)'" < local_script.sh

Объяснение в том, что это sshбудет выполнено bash -sпри входе в систему с аргументами скрипта, которые будут всем после --. Таким же $1будет содержимое local_file_1.txtи $2будет содержимое local_file_2.txt. bash -sсчитывает команды из stdin, которые поступают из < local_script.sh.

Связанный контент