Передайте змінну оболонки як / pattern / to awk


59

В одній із моїх функцій оболонки є наступне:

function _process () {
  awk -v l="$line" '
  BEGIN {p=0}
  /'"$1"'/ {p=1}
  END{ if(p) print l >> "outfile.txt" }
  '
}

, тому, коли викликається як _process $arg, $argпередається як $1і використовується як шаблон пошуку. Це працює таким чином, тому що оболонка розширюється $1замість картини awk! Також lможе використовуватися всередині програми awk, декларуючись за допомогою -v l="$line". Все добре.

Чи можна таким же чином дати шаблон для пошуку як змінної?

Наступні не будуть працювати,

awk -v l="$line" -v search="$pattern" '
  BEGIN {p=0}
  /search/ {p=1}
  END{ if(p) print l >> "outfile.txt" }
  '

, оскільки awk не буде інтерпретувати /search/як змінну, а натомість буквально.

Відповіді:


46

Використовуйте ~оператор awk , і вам не потрібно вводити буквальний регулярний вираз з правого боку:

function _process () {
    awk -v l="$line" -v pattern="$1" '
        $0 ~ pattern {p=1} 
        END {if(p) print l >> "outfile.txt"}
    '  
}

Хоча це було б більш ефективно (не потрібно читати весь файл)

function _process () {
    grep -q "$1" && echo "$line"
}

Залежно від шаблону, можливо, захочеться grep -Eq "$1"


Саме це вирішує так, як я хотів (1-й приклад), оскільки він зберігає семантику, яка була моєю метою. Дякую.
бранкіто

1
Я не зазначив видалення блоку BEGIN: неприсвоєна змінна трактується як 0 у числовому контексті або порожній рядок в іншому випадку. Отже, непризначена змінна буде помилковоюif (p) ...
Гленн Джекман

так, я помітив, його потрібно щоразу встановлювати на блоці BEGIN до нуля, оскільки він служить перемикачем. Але цікаво, що я спробував зараз сценарій з використанням $0 ~ pattern, і він не працює, проте з /'"$1"'/ним все працює! : O
бранкіто

можливо, це має щось спільне із способом $lineвилучення, пошук шаблонів проводиться на виході whois $line, що $lineнадходить з файлу в блоці WHILE DO.
бранкіто

Будь ласка, покажіть вміст $line- зробіть це у своєму питанні для правильного форматування.
glenn jackman

17
awk  -v pattern="$1" '$0 ~ pattern'

Виникає проблема в тому, що awkрозширює послідовність аварійних передач ANSI C (як, наприклад, \nдля нового рядка, \fдля стрічки форми, \\для зворотної косої лінії тощо) у $1. Таким чином, це стає проблемою, якщо $1містить символи зворотної косої риси, які є загальними для регулярних виразів (з GNU awk4.2 або вище, значення, що починаються з @/і закінчуються /, також є проблемою ). Ще один підхід, який не страждає від цього питання, - це написати його:

PATTERN=$1 awk '$0 ~ ENVIRON["PATTERN"]'

Наскільки це буде погано, буде залежати від awkреалізації.

$ nawk -v 'a=\.' 'BEGIN {print a}'
.
$ mawk -v 'a=\.' 'BEGIN {print a}'
\.
$ gawk -v 'a=\.' 'BEGIN {print a}'
gawk: warning: escape sequence `\.' treated as plain `.'
.
$ gawk5.0.1 -v 'a=@/foo/' BEGIN {print a}'
foo

Усі awkроботи однакові для дійсних послідовностей евакуації, хоча:

$ a='\\-\b' awk 'BEGIN {print ENVIRON["a"]}' | od -tc
0000000   \   \   -   \   b  \n
0000006

(вміст $aпереданого як є)

$ awk -v a='\\-\b' 'BEGIN {print a}' | od -tc
0000000   \   -  \b  \n
0000004

( \\змінено на \та \bзмінено на символ зворотного простору).


Отже, ви говорите, що якби, наприклад, \d{3}було знайдено три цифри, це не спрацювало б, як очікувалося, якби я вас добре зрозумів?
бранкіто

2
для \dякої не є дійсною послідовністю відходу С, що залежить від вашої awkреалізації (запустіть, awk -v 'a=\d{3}' 'BEGIN{print a}'щоб перевірити). Але для \` or \ b , yes definitely. (BTW, I don't know of any awk implementations that understands \ d` як значення цифри).
Стефан Шазелас

в ньому написано: awk попередження - послідовність втечі \d' treated as plain d 'd {3}, тож я думаю, у мене виникне проблема в цьому випадку?
бранкіто

1
Вибачте, мій поганий, у мене була відповідь на друк. Ім'я змінної оточення , то повинен відповідати ENVIRON["PATTERN"]для PATTERNзмінної середовища. Якщо ви хочете використовувати змінну оболонки, вам потрібно експортувати її спочатку ( export variable) або використовувати ENV=VALUE awk '...ENVIRON["ENV"]'синтаксис передачі env-var, як у моїй відповіді.
Стефан Шазелас

1
Тому що вам потрібно експортувати змінну оболонки, щоб вона була передана в оточення команді.
Стефан Шазелас

5

Спробуйте щось на кшталт:

awk -v l="$line" -v search="$pattern" 'BEGIN {p=0}; { if ( match( $0, search )) {p=1}}; END{ if(p) print l >> "outfile.txt" }'

Якщо це поводиться так само, як /regex/з точки зору пошуку шаблону, це може бути приємним рішенням. Я спробую.
бранкіто

1
Швидкі тести, які я провів, здавалося, працюють так само, але я навіть не став би це гарантувати ... :)
Мисливець Ейдсон,

0

Ні, але ви можете просто інтерполювати шаблон у рядок з подвійним цитуванням, який ви перейдете на awk:

awk -v l="$line" "BEGIN {p=0}; /$pattern/ {p=1}; END{ if(p) print l >> \"outfile.txt\" }"

Зауважте, що вам зараз доводиться уникати буквально подвійного цитування awk, але це все-таки найпростіший спосіб досягти цього.


Чи безпечний цей спосіб, якщо $patternмістять пробіли, мій приклад зверху буде працювати, оскільки $ 1 захищений подвійними лапками "$ 1", однак не уникайте того, що відбувається у вашому випадку.
бранкіто

2
Ваш оригінальний приклад закінчує одноцитований рядок на другій ', потім захищає $1через подвійні лапки, а потім приєднує іншу рядок з цитуванням з одним котируванням для другої половини програми awk. Якщо я правильно розумію, це має мати точно такий же ефект, як захист $1через зовнішні єдині лапки - awk ніколи не бачить подвійних лапок, які ви ставите навколо нього.
Кіліан Фот

4
Але якщо він $patternмістить ^/ {system("rm -rf /")};, то ви у великій неприємності.
Стефан Шазелас

це лише зворотний бік цього підходу, загорнувшись у ""?
бранкіто

-3

Ви можете використовувати функцію eval, яка розв'язує в цьому прикладі змінну мереж до запуску awk.

nets="searchtext"
eval "awk '/"${nets}"/'" file.txt
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.