Заключение в кавычки в ssh $host$FOO и ssh $host “sudo su пользователь-c $FOO” вводит конструкции

Функции не экспортируются в подпроцессы. Поэтому существуют файлы, названные .kshrc или .bashrc: определить функции что shoiuld быть доступным в подоболочках также.

При запущении скрипта обычно не получаются.*shrc сценарии. Необходимо было бы кодировать это явно, как в . ~/.kshrc.

31
21.03.2011, 02:19
4 ответа

Контакт с несколькими уровнями заключения в кавычки (действительно, несколькими уровнями парсинга/интерпретации) может быть сложным. Это помогает иметь в виду несколько вещей:

  • Каждый “уровень заключения в кавычки” может потенциально включить различный язык.
  • Заключающие в кавычки правила варьируются языком.
  • При контакте больше чем с одним или двумя вложенными уровнями является обычно самым легким работать “от нижней части,” (т.е. самый внутренний к наиболее удаленному).

Уровни заключения в кавычки

Давайте посмотрим на Ваши команды в качестве примера.

pgrep -fl java | grep -i datanode | awk '{print $1}'

Ваша первая команда в качестве примера (выше) использует четыре языка: Ваша оболочка, regex в pgrep, regex в grep (который мог бы отличаться от regex языка в pgrep), и awk. Существует два уровня включенной интерпретации: оболочка и один уровень после оболочки для каждой из включенных команд. Существует только один явный уровень заключения в кавычки (заключение в кавычки оболочки в awk).

ssh host …

Затем Вы добавили уровень ssh на вершине. Это - эффективно другой уровень оболочки: ssh не интерпретирует саму команду, это вручает его оболочке на удаленном конце (через (например). sh -c …) и та оболочка интерпретирует строку.

ssh host "sudo su user -c …"

Затем Вы спросили о добавлении другого уровня оболочки в середине при помощи su (через sudo, который не интерпретирует его аргументы команды, таким образом, мы можем проигнорировать его). На данном этапе у Вас есть три уровня продолжения вложения (awk → оболочка, окружите оболочку → (ssh), окружите оболочку → (su пользователь-c), таким образом, я советую использовать “нижнюю часть,” подход. Я предположу, что Вашими оболочками является совместимый Bourne (например, sh, пепел, тире, ksh, удар, zsh, и т.д.). Некоторый другой вид оболочки (рыба, дистанционное управление, и т.д.) мог бы потребовать другого синтаксиса, но метод все еще применяется.

Нижняя часть,

  1. Сформулируйте строку, которую Вы хотите представить на самом внутреннем уровне.
  2. Выберите механизм заключения в кавычки из репертуара заключения в кавычки следующего самого высокого языка.
  3. Заключите желаемую строку в кавычки согласно своему выбранному механизму заключения в кавычки.
    • Часто существует много изменений, как подать заявку который заключение в кавычки механизма. Выполнение его вручную обычно является вопросом практики и опыта. При выполнении его программно, обычно лучше выбрать самое легкое для разбираний (обычно “самое литеральное” (наименьшее количество Escape)).
  4. Дополнительно, используйте получающуюся заключенную в кавычки строку с дополнительным кодом.
  5. Если Вы еще не достигли своего желаемого уровня заключения в кавычки/интерпретации, берете получающуюся заключенную в кавычки строку (плюс какой-либо добавленный код) и используете его в качестве стартовой строки на шаге 2.

Заключение в кавычки семантики варьируется

Вещь иметь в виду вот состоит в том, что каждый язык (заключающий уровень в кавычки) может дать немного отличающуюся семантику (или даже решительно различную семантику) к тому же символу заключения в кавычки.

Большинство языков имеет “литеральный” механизм заключения в кавычки, но они варьируются по точно, насколько литеральный они. Одинарная кавычка подобных Границе оболочек является на самом деле литеральной (что означает, что Вы не можете использовать ее для заключения в кавычки самого символа одинарной кавычки). Другие языки (Perl, Ruby) являются менее литеральными в этом, они интерпретируют некоторые последовательности обратной косой черты в единственных заключенных в кавычки регионах небуквально (а именно, \\ и \' результат в \ и ', но другие последовательности обратной косой черты являются на самом деле литеральными).

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

Ваш пример

Самый внутренний уровень Вашего примера является awk программой.

{print $1}

Вы собираетесь встроить это в командную строку оболочки:

pgrep -fl java | grep -i datanode | awk …

Мы должны защитить (как минимум) пространство и $ в awk программе. Очевидный выбор состоит в том, чтобы использовать одинарную кавычку в оболочке вокруг целой программы.

  • '{print $1}'

Существует другой выбор хотя:

  • {print\ \$1} непосредственно выйдите из пространства и $
  • {print' $'1} одинарная кавычка только пространство и $
  • "{print \$1}" двойная кавычка целое и Escape $
  • {print" $"1} двойная кавычка только пространство и $
    Это может изгибать правила немного (незавершенный $ в конце двойной заключенной в кавычки строки является литеральным), но это, кажется, работает в большинстве оболочек.

Если бы программа использовала запятую между открытыми и близкими фигурными скобками, то мы должны были бы также заключить в кавычки или выйти или из запятой или из фигурных скобок для предотвращения “расширения фигурной скобки” в некоторых оболочках.

Мы выбираем '{print $1}' и встройте его в остальную часть оболочки “код”:

pgrep -fl java | grep -i datanode | awk '{print $1}'

Затем, Вы хотели выполнить это через su и sudo.

sudo su user -c …

su user -c … точно так же, как some-shell -c … (кроме выполнения под некоторым другим UID), таким образом, su просто добавляет другой уровень оболочки. sudo не интерпретирует свои аргументы, таким образом, он не добавляет уровней заключения в кавычки.

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

'pgrep -fl java | grep -i datanode | awk '\''{print $1}'\'

Существует четыре строки здесь, которые оболочка интерпретирует и свяжет: первая единственная заключенная в кавычки строка (pgrep … awk), завершенная одинарная кавычка, единственно заключенная в кавычки awk программа, другая завершенная одинарная кавычка.

Существуют, конечно, много альтернатив:

  • pgrep\ -fl\ java\ \|\ grep\ -i\ datanode\ \|\ awk\ \'{print\ \$1} выйдите из всего важного
  • pgrep\ -fl\ java\|grep\ -i\ datanode\|awk\ \'{print\$1} то же, но без лишнего пробела (даже в awk программе!)
  • "pgrep -fl java | grep -i datanode | awk '{print \$1}'" двойная кавычка все это, Escape $
  • 'pgrep -fl java | grep -i datanode | awk '"'"'{print \$1}'"'" Ваше изменение; немного дольше, чем обычный путь из-за использования двойных кавычек (два символа) вместо Escape (один символ)

Используя другое заключение в кавычки на первом уровне допускает другие изменения на этом уровне:

  • 'pgrep -fl java | grep -i datanode | awk "{print \$1}"'
  • 'pgrep -fl java | grep -i datanode | awk {print\ \$1}'

Встраивание первой вариации в sudo / *su* командная строка дает это:

sudo su user -c 'pgrep -fl java | grep -i datanode | awk '\''{print $1}'\'

Вы могли использовать ту же строку в любых других единственных контекстах уровня оболочки (например. ssh host …).

Затем, Вы добавили уровень ssh на вершине. Это - эффективно другой уровень оболочки: ssh не интерпретирует саму команду, но это вручает его оболочке на удаленном конце (через (например). sh -c …) и та оболочка интерпретирует строку.

ssh host …

Процесс является тем же: возьмите строку, выберите метод заключения в кавычки, используйте ее, встройте ее.

Используя одинарные кавычки снова:

'sudo su user -c '\''pgrep -fl java | grep -i datanode | awk '\'\\\'\''{print $1}'\'\\\'

Теперь существует одиннадцать строк, которые интерпретируются и связываются: 'sudo su user -c ', завершенная одинарная кавычка, 'pgrep … awk ', завершенная одинарная кавычка, оставленная обратная косая черта, две завершенных одинарных кавычки, сингл заключил в кавычки awk программу, завершенную одинарную кавычку, завершенную обратную косую черту, и финал вышел из одинарной кавычки.

Конечная форма похожа на это:

ssh host 'sudo su user -c '\''pgrep -fl java | grep -i datanode | awk '\'\\\'\''{print $1}'\'\\\'

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

#!/bin/sh

sq() { # single quote for Bourne shell evaluation
    # Change ' to '\'' and wrap in single quotes.
    # If original starts/ends with a single quote, creates useless
    # (but harmless) '' at beginning/end of result.
    printf '%s\n' "$*" | sed -e "s/'/'\\\\''/g" -e 1s/^/\'/ -e \$s/\$/\'/
}

# Some shells (ksh, bash, zsh) can do something similar with %q, but
# the result may not be compatible with other shells (ksh uses $'...',
# but dash does not recognize it).
#
# sq() { printf %q "$*"; }

ap='{print $1}'
s1="pgrep -fl java | grep -i datanode | awk $(sq "$ap")"
s2="sudo su user -c $(sq "$s1")"

ssh host "$(sq "$s2")"
35
27.01.2020, 19:38
  • 1
    Большое объяснение! –  Gilles 'SO- stop being evil' 12.12.2010, 17:52

См. ответ Chris Johnsen для четкого, всестороннего объяснения с общим решением. Я собираюсь дать несколько дополнительных подсказок, которые помогают в некоторых общих обстоятельствах.

Одинарные кавычки выходят из всего кроме одинарной кавычки. Таким образом, если Вы знаете, что значение переменной не включает одинарной кавычки, можно интерполировать его безопасно между одинарными кавычками в сценарии оболочки.

su -c "grep '$pattern' /root/file"  # assuming there is no ' in $pattern

Если Ваша локальная оболочка является ksh93 или zsh, можно справиться с одинарными кавычками в переменной путем перезаписи их к '\''. (Хотя удар также имеет ${foo//pattern/replacement} создайте, его обработка одинарных кавычек не имеет смысла мне.)

su -c "grep '${pattern//'/'\''}' /root/file"  # if the outer shell is zsh
su -c "grep '${pattern//\'/\'\\\'\'}' /root/file"  # if the outer shell is ksh93

Другая подсказка, чтобы избежать необходимости иметь дело с вложенным заключением в кавычки должна передать строки через переменные среды как можно больше. Ssh и sudo склонны отбрасывать большинство переменных среды, но они часто настраиваются для разрешения LC_* через, потому что они обычно очень важны для удобства использования (они содержат информацию о локали) и редко считаются чувствительной безопасностью.

LC_CMD='what you would use locally' ssh $host 'sudo su user -c "$LC_CMD"'

Здесь, с тех пор LC_CMD содержит отрывок оболочки, он должен быть предоставлен буквально самой внутренней оболочке. Поэтому переменная сразу расширена оболочкой выше. Оболочка innermost-one видит "$LC_CMD", и самая внутренняя оболочка видит команды.

Похожий метод полезен для передачи данных утилите обработки текста. При использовании интерполяции оболочки утилита будет рассматривать значение переменной как команда, например. sed "s/$pattern/$replacement/" не будет работать, если переменные будут содержать /. Так используйте awk (не sed), и любой -v опция или ENVIRON выстройте для передачи данных из оболочки (если Вы проходите ENVIRON, не забудьте экспортировать переменные).

awk -vpattern="$pattern" replacement="$replacement" '{gsub(pattern,replacement); print}'
7
27.01.2020, 19:38

Как насчет того, чтобы использовать больше двойных кавычек?

Затем Ваш ssh $host $CMD должен работать просто великолепно с этим:

CMD="pgrep -fl java | grep -i datanode | awk '{print $1}'"

Теперь к более сложному, ssh $host "sudo su user -c \"$CMD\"". Я предполагаю все, что необходимо сделать, выйти из чувствительных символов в CMD: $, \ и ". Таким образом, я попытался бы видеть, работает ли это: echo $CMD | sed -e 's/[$\\"]/\\\1/g'.

Если это смотрит хорошо, перенесите echo+sed в функцию оболочки, и Вы хороши для движения с ssh $host "sudo su user -c \"$(escape_my_var $CMD)\"".

0
27.01.2020, 19:38

Как Chris Johnson описывает очень хорошо, у Вас есть несколько уровней заключенной в кавычки абстракции здесь; Вы сообщаете своему локальному shell сообщать удаленному shell через ssh то, что это должно сообщить sudo сообщать su сообщать удаленному shell выполнять Ваш конвейер pgrep -fl java | grep -i datanode | awk '{print $1}' как user. Такая команда требует большого количества утомительных \'"quote quoting"\'.

Если Вы послушаете мой совет, Вы будете предшествовать всей ерунде и делать:

% ssh $host <<REM=LOC_EXPANSION <<'REMOTE_CMD' |
> localhost_data='$(commands run on localhost at runtime)' #quotes don't affect expansion
> more_localhost_data="$(values set at heredoc expansion)" #remote shell will receive m_h_d="result"
> REM=LOC_EXPANSION
> commands typed exactly as if located at 
> the remote terminal including variable 
> "${{more_,}localhost_data}" operations
> 'quotes and' \all possibly even 
> a\wk <<'REMOTELY_INVOKED_HEREDOC' |
> {as is often useful with $awk
> so long as the terminator for}
> REMOTELY_INVOKED_HEREDOC
> differs from that of REM=LOC_EXPANSION and
> REMOTE_CMD
> and here you can | pipeline operate on |\
> any output | received from | ssh as |\
> run above | in your local | terminal |\
> however | tee > ./you_wish.result
<desired output>

ДЛЯ БОЛЬШЕ:

Проверьте, что мои (возможно, слишком длинно-обветренный) отвечают на Передачу по каналу путей с различными типами кавычек для замены наклонной черты, в которой я обсуждаю часть теории позади, почему это работает.

- Mike

2
27.01.2020, 19:38
  • 1
    Это выглядит интересным, но я не могу заставить это работать. Вы могли отправить минимальный рабочий пример? –  John Lawrence Aspden 28.02.2017, 22:10
  • 2
    я верю этому примеру, требует zsh, так как он использует несколько перенаправлений для stdin. В других подобных Границе оболочках, втором << просто заменяет первый. Это должно сказать "zsh только" где-нибудь, или я пропускал что-то? (Умный прием, хотя, чтобы иметь heredoc это частично подвергается локальному расширению) –   07.02.2018, 04:33
  • 3
    Вот совместимая версия удара: unix.stackexchange.com/questions/422489 / … –  dabest1 07.02.2018, 11:38

Теги

Похожие вопросы