Вопрос или проблема
Мне нужно присвоить команду переменной и выполнить ее с использованием переменной в linux bash. Команда отлично выполняется из командной строки, но не из переменной из-за множества кавычек. Я думаю, что нужно использовать обратную косую черту для множества кавычек, чтобы избежать ошибки. Помогите, пожалуйста, с правильным форматом.
Код:
parm="cat file|awk '$1>0 && $1="abc" {print f $0} {f=$0 ORS}'"
eval "$parm"
Содержимое:
abc
123
efg
456
Ошибка:
awk: cmd. line:1: >0 && =abc {print f bash} {f=bash ORS}
awk: cmd. line:1: ^ syntax error
Заранее спасибо!
Для хранения кода используйте функции, а не переменные:
parm() {
< file awk '
$1 > 0 {$1 = "abc"; print f $0}
{f = $0 ORS}'
}
parm
Если бы вам нужно было использовать переменные:
parm='
< file awk '\''
$1 > 0 {$1 = "abc"; print f $0}
{f = $0 ORS}'\''
'
eval "$parm"
Это, вероятно, самый простой способ, где мы используем только одинарные кавычки, а чтобы вставить одиночные кавычки, мы выходим из кавычек и добавляем одинарную кавычку с помощью \'
.
Смотрите также:
parm=$(cat << 'EOF'
< file awk '
$1 > 0 {$1 = "abc"; print f $0}
{f = $0 ORS}'
EOF
)
eval "$parm"
Когда разделитель заключен в кавычки в << 'delimiter'
, то здесь не происходит ни $param
, ни $(( arith ))
, ни `cmdsubst`
, ни $(cmdsubst)
, ни продолжения строки \
внутри документа-пересылки, что упрощает написание любого многострочного текста, содержащего что-либо.
Я заменил cat file |
(это имеет мало смысла для cat
енирования одного файла) на < file
, перенес присваивание $1 = "abc"
из условия, где оно было запутанным, и добавил разделитель (здесь это новая строка, что также улучшает читаемость) между двумя командами awk
, как требует POSIX.
Использование функций также означает, что можно параметризировать, как например:
parm() {
< "$1" awk '
$1 > 0 {$1 = "abc"; print f $0}
{f = $0 ORS}'
}
parm file
Где первый аргумент становится стандартным вводом awk
или:
parm() {
cat -- "$@" | awk '
$1 > 0 {$1 = "abc"; print f $0}
{f = $0 ORS}'
}
echo -1 3 | parm file1 - file2
Где все аргументы принимаются как пути к файлам (или stdin, если -
¹ или аргумент не передан) и объединяются, а результат передается в качестве стандартного ввода awk
через pipe или:
parm() {
awk -- '
$1 > 0 {$1 = new; print f $0}
{f = $0 ORS}' new=abc "$@"
}
echo -1 3 | parm file1 - new=xyz file2
Где все аргументы передаются в awk
, который может принимать их как пути к файлам, stdin для -
¹ (или если нет аргумента пути к файлу) или присваивания переменных awk² ³.
¹ используйте ./-
для файла, буквально называемого -
в текущем рабочем каталоге
² используйте ./foo=bar.txt
для файла, буквально называемого foo=bar.txt
в текущем рабочем каталоге.
³ Обратите внимание, что в GNU awk
также известном как gawk
, new=@/text/
интерпретируется как new=text
, с text
, принимаемым за регулярное выражение. new='\@/text/'
обходит это, но выдает предупреждение, лучший способ обойти эту ошибку – вызвать awk
как POSIXLY_CORRECT=1 awk ...
, что отключает эту ошибку для gawk и должно быть безвредно для других реализаций awk
.
Ответ или решение
Теория
Когда дело доходит до передачи команд через переменные в Баше, нужно быть особенно внимательным с кавычками и специальными символами. Это часто становится проблемой, когда в команде присутствуют строки, которые содержат кавычки. В таких случаях неправильно использованные кавычки могут привести к синтаксической ошибке. Для работы с кавычками внутри командной строки в переменной обычно используют eval
, но нужно быть осторожным, так как это может привести к проблемам безопасности, если команду не контролировать.
Пример
Сначала посмотрим на ваш оригинальный пример, который вызвал ошибку:
parm="cat file|awk '$1>0 && $1=\"abc\" {print f $0} {f=$0 ORS}'"
eval "$parm"
Ошибка здесь возникает из-за неверной интерпретации кавычек внутри команды awk
. Чтобы устранить эту проблему, можно использовать двойные кавычки и экранирование. Посмотрите на следующий прием решения:
parm='cat file|awk '\''$1>0 { $1="abc"; print f $0 } { f=$0 ORS }'\'''
eval "$parm"
Применение
Существует также более безопасный и удобный подход, который заключается в использовании функций, чтобы избежать eval и манипуляций с кавычками:
parm() {
< file awk '
$1 > 0 { $1 = "abc"; print f $0 }
{ f = $0 ORS }'
}
parm
Если все же необходимо использовать переменные, рекомендуется использовать конструкцию с here document, которая позволяет вставлять многострочные строки более надежно и с меньшим количеством экранирования:
parm=$(cat << 'EOF'
< file awk '
$1 > 0 { $1 = "abc"; print f $0 }
{ f = $0 ORS }'
EOF
)
eval "$parm"
Заключение
Оптимальным решением в данном случае будет использование функций, так как это более безопасный и удобочитаемый метод. Подход с here document также может быть полезен, если необходимо использование переменных. В обоих случаях важно следить за контролем команд, особенно если они включают пользовательский ввод, чтобы избежать риска инъекций.