Работа с несколькими уровнями цитирования (на самом деле, несколькими уровнями синтаксического анализа / интерпретации) может стать сложной. Это помогает запомнить несколько вещей:
- Каждый «уровень цитирования» может включать другой язык.
- Правила цитирования зависят от языка.
- При работе с более чем одним или двумя вложенными уровнями, как правило, легче всего работать «снизу вверх» (т. Е. От самого внутреннего до самого внешнего).
Уровни цитирования
Давайте посмотрим на ваши примеры команд.
pgrep -fl java | grep -i datanode | awk '{print $1}'
Ваша первая примерная команда (выше) использует четыре языка: ваша оболочка, регулярное выражение в pgrep , регулярное выражение в grep (которое может отличаться от языка регулярных выражений в pgrep ) и awk . Для каждой из задействованных команд существует два уровня интерпретации: оболочка и один уровень после оболочки. Существует только один явный уровень цитирования (цитирование оболочки в awk ).
ssh host …
Далее вы добавили уровень ssh сверху. Это фактически другой уровень оболочки: ssh не интерпретирует саму команду, он передает ее оболочке на удаленном конце (через (например) sh -c …
), и эта оболочка интерпретирует строку.
ssh host "sudo su user -c …"
Затем вы спросили о добавлении еще одного уровня оболочки в середине с помощью su (через sudo , который не интерпретирует аргументы своей команды, поэтому мы можем его игнорировать). На данный момент у вас есть три уровня вложенности ( awk → shell, shell → shell ( ssh ), shell → shell ( su user -c ), поэтому я советую использовать подход «снизу вверх». Я предполагаю, что ваши оболочки совместимы с Bourne (например, sh , ash , dash , ksh , bash , zsh и т. д.). Некоторые другие виды оболочек ( fish , rcи т. д.) может потребоваться другой синтаксис, но метод все еще применяется.
Вверх дном
- Сформулируйте строку, которую вы хотите представить на самом внутреннем уровне.
- Выберите механизм цитирования из репертуара цитирования следующего по приоритетности языка.
- Цитируйте желаемую строку в соответствии с выбранным вами механизмом цитирования.
- Часто существует много вариантов того, как применять какой механизм цитирования. Делать это вручную - это вопрос практики и опыта. Делая это программно, обычно лучше выбрать самый легкий, чтобы получить право (обычно «самый буквальный» (наименьшее количество побегов)).
- При желании можно использовать результирующую строку в кавычках с дополнительным кодом.
- Если вы еще не достигли желаемого уровня цитирования / интерпретации, возьмите получившуюся строку в кавычках (плюс любой добавленный код) и используйте ее в качестве начальной строки в шаге 2.
Цитирую семантику варя
Здесь следует иметь в виду, что каждый язык (уровень цитирования) может давать несколько разную семантику (или даже существенно различную семантику) одному и тому же символу цитирования.
Большинство языков имеют «буквальный» механизм цитирования, но они различаются по точности. Одиночная кавычка оболочек типа Борна на самом деле буквальная (что означает, что вы не можете использовать ее для кавычек самого символа одинарной кавычки). Другие языки (Perl, Ruby) менее буквальны в том смысле, что они интерпретируют некоторые последовательности обратной косой черты внутри областей в одинарных кавычках не буквально (в частности, \\
и \'
приводят к \
и '
, но другие последовательности обратной косой черты на самом деле буквальны).
Вам нужно будет прочитать документацию для каждого из ваших языков, чтобы понять правила цитирования и общий синтаксис.
Ваш пример
Самым внутренним уровнем вашего примера является программа awk .
{print $1}
Вы собираетесь встроить это в командную строку оболочки:
pgrep -fl java | grep -i datanode | awk …
Нам нужно защитить (как минимум) пространство и $
в программе awk . Очевидный выбор - использовать одинарные кавычки в оболочке вокруг всей программы.
Есть и другие варианты:
{print\ \$1}
прямо покинуть пространство и $
{print' $'1}
одиночная кавычка только пробел и $
"{print \$1}"
двойная цитата целого и избежать $
{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}'"
двойная кавычка, избегай $
'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")"