bash для с командой awk внутри - PullRequest
       70

bash для с командой awk внутри

0 голосов
/ 11 февраля 2020

У меня есть этот кусок кода bash сценария

for file in "$(ls | grep .*.c)"
do
cat $file |awk '/.*open/{print $0}'|awk -v nomeprog=$file 'BEGIN{FS="(";printf "the file e %s with the  open call:", nameprog}//{ print $2}'
done 

, это дает мне эту ошибку: * awk: cmd. строка: 1: файл. c awk: cmd. строка: 1: ^ синтаксическая ошибка

* у меня есть эта ошибка, когда у меня есть больше файла c в папке, только с одним файлом это работает

1 Ответ

1 голос
/ 11 февраля 2020

В целом, вы, вероятно, должны следовать рекомендации Чарльза Даффи, чтобы использовать более подходящие инструменты для этой задачи. Но я хотел бы go узнать, почему текущий сценарий не работает и как это исправить, в качестве учебного упражнения.

Кроме того, две быстрые рекомендации по проверке и устранению неисправностей сценария оболочки: запустите свои сценарии через shellcheck. net, чтобы указать на распространенные ошибки, а при отладке ставить set -x перед проблемным разделом (и set +x после), чтобы оболочка распечатала то, что, по ее мнению, происходит как скрипт выполняется.

Проблема связана с тем, как вы используете переменную file. Давайте посмотрим, что это делает:

for file in "$(ls | grep .*.c)"

Сначала ls печатает список файлов в текущем каталоге, по одному на строку. ls действительно предназначен для интерактивного использования, и его вывод может быть неоднозначным и трудно анализируемым; в сценарии почти всегда есть лучшие способы получения списков имен файлов (и я покажу вам одно из них).

Вывод ls передается grep .*.c, что неправильно во многих отношениях. Во-первых, поскольку этот шаблон содержит подстановочный знак ("*"), оболочка попытается развернуть его в список совпадающих имен файлов. Если каталог содержит какие-либо скрытые (с начальным "."). c файлами, он заменит их списком, и ничего не будет работать вообще правильно. Всегда заключать аргумент шаблона в grep, чтобы предотвратить это.

Но сам шаблон (".*.c") также неверен; он ищет любое количество произвольных символов («.*»), за которым следует один произвольный символ («.» - это регулярное выражение, поэтому «.» не обрабатывается буквально), за которым следует "c". И он ищет этот в любом месте строки , поэтому любое имя файла, которое содержит «c» где-то, кроме первой позиции, будет совпадать. Шаблон, который вам нужен, будет выглядеть примерно так: '[.]c$' (обратите внимание, что я обернул его в одинарные кавычки, поэтому оболочка не будет пытаться обрабатывать $ как ссылку на переменную, как в двойных кавычках).

Тогда есть еще одна проблема, которая является (частью) проблемой, с которой вы на самом деле сталкиваетесь: вывод этого ls | grep раскрывается в двойных кавычках . Двойные кавычки говорят об этом, чтобы оболочка не делала свое обычное слово-разбиение-и-подстановка-расширение для результата. Обычная (но все же неправильная) вещь, которую нужно здесь сделать, - это исключить двойные кавычки, потому что разделение слов, вероятно, разбивает список имен файлов на отдельные имена, так что вы можете перебирать их один за другим. (Если в имени файла нет забавных символов, в этом случае он может давать странные результаты.) Но с двойными кавычками он не разделяет их, он просто обрабатывает все это как один большой элемент, поэтому ваш l oop запускается один раз с file установлен в "src1.c\nsrc2.c\nsrc3.c" (где \n представляет фактические новые строки).

Это проблема, с которой вы можете столкнуться, анализируя ls. Не делайте этого, просто используйте подстановочный знак оболочки напрямую:

for file in *.c

Это намного проще, позволяет избежать путаницы в синтаксисе шаблонов регулярных выражений по сравнению с синтаксисом шаблонов шаблонов, неоднозначности в выводе ls и т. Д. c. Это просто, понятно, и просто работает .

Этого, вероятно, достаточно, чтобы заставить его работать на вас, но есть несколько других вещей, которые вам действительно следует исправить, если вы делаете что-то вроде этого. Во-первых, вы должны заключить в кавычки переменные (т.е. использовать "$file" вместо $file). Это еще одна часть ошибки, которую вы получаете; посмотрите на вторую команду awk:

awk -v nomeprog=$file 'BEGIN{FS="(";printf "the file e %s with the  open call:", nameprog}//{ print $2}'

Если для file установлено значение "src1.c\nsrc2.c\nsrc3.c", оболочка выполнит на ней свою функцию разделения слов и подстановочных знаков, давая:

awk -v nomeprog=src1.c src2.c src3.c 'BEGIN{FS="(";printf "the file e %s with the  open call:", nameprog}//{ print $2}'

awk, таким образом, установит для своей переменной nomeprog значение "src1. c", а затем попытается запустить "src2. c" в качестве команды awk (для входных файлов с именем «src3. c» и «НАЧАЛО {FS = ...»). «src2. c», конечно, не является допустимой командой awk, поэтому вы получаете синтаксическую ошибку.

Такая путаница типична для хаоса, который может возникнуть в результате ссылок на переменные без кавычек. Сделайте двойные кавычки для ссылок на переменные.

Другая вещь, которая гораздо менее важна, заключается в том, что у вас есть бесполезное использование cat. Каждый раз, когда у вас есть шаблон:

cat somefile | somecommand

(и это всего лишь один файл, а не несколько, которые нужно cat указывать вместе), вы должны просто использовать:

somecommand <somefile

и в некоторых случаях, таких как awk и grep, сама команда может принимать входные имена файлов непосредственно в качестве аргументов, поэтому вы можете просто использовать:

somecommand somefile

, так что в вашем случае вместо

cat "$file" | awk '/.*open/{print $0}' | awk -v nomeprog="$file" 'BEGIN{FS="(";printf "the file e %s with the  open call:", nameprog}//{ print $2}'

Я бы просто использовал:

awk '/.*open/{print $0}' "$file" | awk -v nomeprog="$file" 'BEGIN{FS="(";printf "the file e %s with the  open call:", nameprog}//{ print $2}'

(Хотя, как отметил Чарльз Даффи, даже , что можно упростить довольно много.)

...