Я изучаю grep
команды.
Я хочу создать программу, которая, когда пользователь вводит более одного слова, выводит строку, содержащую слово в файле данных.
Поэтому я соединил слова, которые набрал пользователь, с помощью «|». и поместите их в команду grep
, чтобы создать программу, которую я задумал.
Но это операция ИЛИ. Я хочу сделать операцию AND.
Итак, я научился использовать операцию AND с командами grep
следующим образом.
cat <file> | grep 'pattern1' | grep 'pattern2' | grep 'pattern3'
Но я не знаю, как поместить пользовательский ввод в позицию «шаблон1», «шаблон2», «шаблон3». Потому что количество слов, которые вводит пользователь, не определено.
По мере увеличения пользовательского ввода grep
должно выполняться с использованием все большего и большего количества каналов, но я не знаю, как построить эту часть.
Пользовательский ввод выглядит следующим образом:
$ [the name of my program] 'pattern1' 'pattern2' 'pattern3' ...
Я был бы очень признателен за вашу помощь.
В принципе, то, что вы просите, можно было бы сделать с помощью цикла с выводом во временный файл.
file=inputfile
temp=$(mktemp -d -t multigrep.XXXXXXXXX) || exit
trap 'rm -rf "$temp"' ERR EXIT
for regex in "$@"; do
grep "$regex" "$file" >"$temp"/output
mv "$temp"/output "$temp"/input
file = "$temp"/input
done
cat "$temp"/input
Однако лучшим решением, вероятно, будет настроить Awk на проверку всех шаблонов за один раз и избежать повторного чтения одних и тех же строк.
Передача аргументов в Awk с сохранением кавычек не совсем тривиальна. Здесь мы просто передаем их как аргументы командной строки и обрабатываем их в массив внутри самого скрипта Awk.
awk 'BEGIN { for(i=1; i<ARGC; ++i) a[i]=ARGV[i];
ARGV[1] = "-"; ARGC=1 }
{ for(n=1; n<=i; ++n) if ($0 !~ a[n]) next; }1' "$@" <file
Вкратце, в блоке BEGIN
мы копируем аргументы командной строки из ARGV
в a
, затем заменяем ARGV
и ARGC
, чтобы передать Awk новый массив (кажущихся) аргументов командной строки, который состоит только из -
, что означает чтение стандартного ввода. . Затем мы просто перебираем a
и переходим к следующей строке, если текущая строка ввода из стандартного ввода не совпадает. Любые оставшиеся строки соответствуют всем шаблонам, которые мы передали, и, таким образом, печатаются.
предлагаю использовать логику шаблона awk
:
awk '/RegExp-pattern-1/ && /RegExp-pattern-2/ && /RegExp-pattern-3/ 1' input.txt
Преимущества: вы можете играть с логическими операторами &&
||
на шаблонах RegExp. И вы сканируете весь файл один раз.
Недостатки: должен предоставлять список файлов (не может проходить подкаталоги) и ограниченный синтаксис RegExp по сравнению с grep -E
или grep -P
Конечно, но задача состоит в том, чтобы перенести эти шаблоны в Awk без их жесткого кодирования в новом файле скрипта.
Это было действительно полезно! Большое спасибо.
С помощью grep -f
вы можете найти несколько элементов, каждый из которых находится в строке файла.
С помощью <(command)
вы можете позволить Bash думать, что результатом command
является файл.
С printf "%s\n"
и списком аргументов каждый аргумент печатается с новой строки.
Вместе:
grep -f <(printf "%s\n" "$@") datafile
Подстановка процесса — это функция Bash, но формулировка там странная. На самом деле это Bash, из-за которого результат выглядит как файл для grep
.
Когда вы хотите
awk
, а неgrep -f
, вы можете использовать трюки ARGC сawk 'NR==FNR {a[FNR]=$0; next} i==0 && NR>FNR {i=NR-1} {for(n=1; n<=i; ++n) if ($0 ~ a[n]) print }' <(printf "%s\n" $*) file
. Я бы предпочел изменить i и n:awk 'NR==FNR {a[FNR]=$0; next} n==0 && NR>FNR {n=NR-1} {for(i=1; i<=n; ++i) if ($0 ~ a[i]) print }' <(printf "%s\n" $*) file