У меня есть локальный скрипт, который я хочу запустить на нескольких удаленных серверах. Синтаксис команды, который я использую, следующий:
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_*
переменную ( SendEnv
in ssh_config
), и sshd
сервер принимает ее ( AcceptEnv
in sshd_config
))
Или просто передайте содержимое local_script.sh
как удаленный код оболочки, предполагая, что оболочка входа удаленного пользователя соответствует синтаксису этого скрипта:
ssh host "$(cat local_script.sh)" < input
Или объединить код и ввод, поданный на ssh
stdin, следующим образом:
{
echo '{'; cat local_script.sh; echo '}; exit'
cat input
} | ssh host 'bash -s some arguments'
Здесь использование bash
because 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
.