Функции не экспортируются в подпроцессы. Поэтому существуют файлы, названные .kshrc или .bashrc: определить функции что shoiuld быть доступным в подоболочках также.
При запущении скрипта обычно не получаются.*shrc сценарии. Необходимо было бы кодировать это явно, как в . ~/.kshrc
.
Контакт с несколькими уровнями заключения в кавычки (действительно, несколькими уровнями парсинга/интерпретации) может быть сложным. Это помогает иметь в виду несколько вещей:
Давайте посмотрим на Ваши команды в качестве примера.
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, и т.д.). Некоторый другой вид оболочки (рыба, дистанционное управление, и т.д.) мог бы потребовать другого синтаксиса, но метод все еще применяется.
Вещь иметь в виду вот состоит в том, что каждый язык (заключающий уровень в кавычки) может дать немного отличающуюся семантику (или даже решительно различную семантику) к тому же символу заключения в кавычки.
Большинство языков имеет “литеральный” механизм заключения в кавычки, но они варьируются по точно, насколько литеральный они. Одинарная кавычка подобных Границе оболочек является на самом деле литеральной (что означает, что Вы не можете использовать ее для заключения в кавычки самого символа одинарной кавычки). Другие языки (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")"
См. ответ 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}'
Как насчет того, чтобы использовать больше двойных кавычек?
Затем Ваш 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)\""
.
Как 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
<<
просто заменяет первый. Это должно сказать "zsh только" где-нибудь, или я пропускал что-то? (Умный прием, хотя, чтобы иметь heredoc это частично подвергается локальному расширению)
–
07.02.2018, 04:33