Как использовать кавычки в переменных Bash [дубликат]

Вопрос или проблема

Мне нужно присвоить команду переменной и выполнить ее с использованием переменной в 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 также может быть полезен, если необходимо использование переменных. В обоих случаях важно следить за контролем команд, особенно если они включают пользовательский ввод, чтобы избежать риска инъекций.

Оцените материал
Добавить комментарий

Капча загружается...