ssh
имеет раздражающую особенность, когда вы запускаете:
ssh user@host cmd and "here's" "one arg"
Вместо того чтобы запускать cmd
его с включенными аргументами host
, он объединяет cmd
аргументы и аргументы с пробелами и запускает оболочку host
для интерпретации полученной строки (я думаю, поэтому она вызывается ssh
и не вызывается sexec
).
Хуже того, вы не знаете, какая оболочка будет использоваться для интерпретации этой строки, поскольку это оболочка входа в систему, user
которая даже не гарантированно похожа на Борну, поскольку есть еще люди, которые используют ее в tcsh
качестве оболочки входа в систему и fish
находится на подъеме.
Есть ли способ обойти это?
Предположим , у меня есть команда , как список аргументов , хранящихся в bash
массиве, каждый из которых может содержать любую последовательность байтов непустых, есть ли способ , чтобы он выполнен на , host
как user
на постоянной основе , независимо от входа оболочки , что user
на host
(что мы предположим, это одно из основных семейств оболочки Unix: Bourne, csh, rc / es, fish)?
Другое разумное предположение, которое я должен сделать, - наличие sh
команды, host
доступной в $PATH
этом, совместимой с Bourne.
Пример:
cmd=(
'printf'
'<%s>\n'
'arg with $and spaces'
'' # empty
$'even\n* * *\nnewlines'
"and 'single quotes'"
'!!'
)
Я могу запустить его локально с ksh
/ zsh
/ bash
/ yash
как:
$ "${cmd[@]}"
<arg with $and spaces>
<>
<even
* * *
newlines>
<and 'single quotes'>
<!!>
или
env "${cmd[@]}"
или
xterm -hold -e "${cmd[@]}"
...
Как бы мне запустить его host
как user
закончено ssh
?
ssh user@host "${cmd[@]}"
очевидно не сработает.
ssh user@host "$(printf ' %q' exec "${cmd[@]}")"
будет работать только в том случае, если оболочка входа удаленного пользователя будет такой же, как локальная оболочка (или понимает кавычки так же, как printf %q
в локальной оболочке, создающей ее) и работает в той же локали.
источник
cmd
аргумент был таким,/bin/sh -c
мы бы в конечном итоге создали оболочку posix в 99% случаев, не так ли? Конечно, экранировать специальные символы немного сложнее, но решит ли это начальную проблему?ssh host sh -c 'some cmd'
, так же, как этоssh host 'sh -c some cmd'
, если оболочка входа удаленного пользователя интерпретирует этуsh -c some cmd
командную строку. Нам нужно написать команду в правильном синтаксисе для этой оболочки (и мы не знаем , какой он есть) , так чтоsh
можно назвать там с-c
иsome cmd
аргументами.sh -c 'some cmd'
иsome cmd
командные строки , случается, интерпретируется одинаково во всех этих оболочках. А что если я захочу запуститьecho \'
командную строку Bourne на удаленном хосте?echo command-string | ssh ... /bin/sh
это решение, которое я дал в своем ответе, но это означает, что вы не можете передавать данные на стандартный ввод этой удаленной команды.cmd
это такcmd=(echo "foo bar")
, передаваемая в командную строку оболочкиssh
должна быть что-то вроде `'echo' 'foo bar'. The *first* space (the one before
echo) is superflous, but doen't harm. The other one (the ones before
'foo bar') is needed. With
'% q', we'd pass a
'echo''foo bar'` командной строки.Ответы:
Я не думаю, что в какой-либо реализации
ssh
есть собственный способ передачи команды от клиента к серверу без использования оболочки.Теперь все может стать проще, если вы можете указать удаленной оболочке запускать только определенный интерпретатор (например
sh
, для которого мы знаем ожидаемый синтаксис) и дать код для выполнения другим способом.Этим другим средством может быть, например, стандартный ввод или переменная окружения .
Когда ни один из них не может быть использован, я предлагаю хакерское третье решение ниже.
Использование stdin
Если вам не нужно передавать какие-либо данные в удаленную команду, это самое простое решение.
Если вы знаете, что на удаленном хосте есть
xargs
команда, которая поддерживает эту-0
опцию, и эта команда не слишком велика, вы можете сделать:Эта
xargs -0 env --
командная строка интерпретируется одинаково со всеми этими семействами оболочек.xargs
читает список аргументов с нулевым разделителем в stdin и передает их в качестве аргументовenv
. Это предполагает, что первый аргумент (имя команды) не содержит=
символов.Или вы можете использовать
sh
на удаленном хосте после цитирования каждого элемента с использованиемsh
синтаксиса цитирования.Использование переменных среды
Теперь, если вам нужно передать некоторые данные от клиента на стандартный ввод удаленной команды, вышеуказанное решение не будет работать.
Однако некоторые
ssh
развертывания сервера позволяют передавать произвольные переменные среды от клиента к серверу. Например, многие развертывания openssh в системах на основе Debian позволяют передавать переменные, имя которых начинается сLC_
.В этих случаях вы можете иметь
LC_CODE
переменную, например, содержащую код в кавычках,sh
как указано выше, и запускатьsh -c 'eval "$LC_CODE"'
на удаленном хосте после того, как попросите своего клиента передать эту переменную (опять же, это командная строка, которая интерпретируется одинаково в каждой оболочке):Создание командной строки, совместимой со всеми семействами оболочек
Если ни один из приведенных выше вариантов не является приемлемым (поскольку вам нужен stdin, а sshd не принимает никаких переменных, или потому, что вам нужно универсальное решение), вам придется подготовить командную строку для удаленного хоста, совместимого со всеми поддерживаемые снаряды.
Это особенно сложно, потому что все эти оболочки (Bourne, csh, rc, es, fish) имеют свой собственный синтаксис и, в частности, разные механизмы цитирования, а некоторые из них имеют ограничения, которые трудно обойти.
Вот решение, которое я придумала, я опишу его ниже:
Это
perl
сценарий обертки вокругssh
. Я называю этоsexec
. Вы называете это как:Итак, в вашем примере:
И оболочка превращается
cmd and its args
в командную строку, которую все оболочки интерпретируют как вызовыcmd
с ее аргументами (независимо от их содержания).Ограничения:
yash
это оболочка удаленного входа, вы не можете передать команду, аргументы которой содержат недопустимые символы, но это ограничение вyash
том, что вы все равно не можете обойтись.sh
, он также предполагает, что удаленная система имеетprintf
команду.Чтобы понять, как это работает, вам нужно знать, как работает цитирование в различных оболочках:
'...'
это сильные цитаты без специальных символов."..."
являются слабыми кавычками, где"
можно избежать обратной косой черты.csh
, То же самое, что и Борн, за исключением того, что"
нельзя сбежать внутрь"..."
. Также символ новой строки должен вводиться с префиксом с обратной косой чертой. И!
вызывает проблемы даже внутри одинарных кавычек.rc
, Единственные цитаты'...'
(сильные). Одиночная кавычка в одинарных кавычках вводится как''
(как'...''...'
). Двойные кавычки или обратная косая черта не являются особенными.es
, То же, что и для rc, за исключением того, что вне кавычек обратная косая черта может выходить за пределы одной кавычки.fish
: так же, как Борн, за исключением того, что обратный слеш убегает'
внутрь'...'
.Со всеми этими ограничениями легко заметить, что нельзя надежно процитировать аргументы командной строки, чтобы она работала со всеми оболочками.
Использование одинарных кавычек, как в:
работает во всех, кроме:
не будет работать в
rc
.не будет работать в
csh
.не будет работать в
fish
.Однако мы сможем обойти большинство из этих проблем, если нам удастся сохранить эти проблемные символы в переменных, таких как обратная косая черта
$b
, одинарная кавычка$q
, новая строка в$n
(и!
в$x
для расширения истории csh) независимым способом оболочки.будет работать во всех снарядах. Это все равно не будет работать для новой строки,
csh
хотя. Если в нем$n
содержится символ новой строки,csh
вы должны написать его так,$n:q
чтобы он расширился до новой строки, и это не будет работать для других оболочек. Итак, то, что мы в итоге делаем вместо этого, - это звонитьsh
иsh
расширять их$n
. Это также означает необходимость выполнения двух уровней цитирования: один для оболочки удаленного входа и один дляsh
.В
$preamble
этом коде самая сложная часть. Это делает использование различных различного котирования правил во всех оболочках , чтобы иметь некоторые участки кода истолкован лишь одной из оболочек ( в то время как закомментированы для других) , каждого из которых только определяющих те$b
,$q
,$n
,$x
переменных для их соответствующей оболочки.Вот код оболочки, который будет интерпретирован оболочкой входа удаленного пользователя
host
для вашего примера:Этот код в конечном итоге выполняет ту же команду при интерпретации любым из поддерживаемых оболочек.
источник
ТЛ; др
Для более сложного решения, прочитайте комментарии и изучите другой ответ .
описание
Ну, мое решение не будет работать с не-
bash
оболочками. Но при условии, что этоbash
на другом конце, все становится проще. Моя идея состоит в том, чтобы повторно использоватьprintf "%q"
для побега. Кроме того, обычно на другом конце более удобно читать сценарий, который принимает аргументы. Но если команда короткая, возможно, это нормально. Вот несколько примеров функций для использования в скриптах:local.sh
:remote.sh
:Выход:
Кроме того, вы можете сделать
printf
работу самостоятельно, если знаете, что делаете:источник
bash
она доступна на удаленном компьютере. Есть также несколько проблем с пропущенными кавычками, которые могут вызвать проблемы с пробелами и подстановочными знаками.bash
оболочки. Но, надеюсь, люди найдут его полезным. Я пытался решить другие вопросы, хотя. Не стесняйтесь сказать мне, если я что-то упускаю, кроме этойbash
вещи.ssh_run user@host "${cmd[@]}"
). У вас все еще есть некоторые пропущенные цитаты.printf %q
небезопасны для использования в другой локали (и они также довольно ошибочны; например, в локалях, использующих кодировку BIG5, они (4.3.48) заключаются в кавычкиε
какα`
!). Для этого лучше всего цитировать все и в одинарных кавычках, какshquote()
в моем ответе.