将命令行参数作为搜索模式传递给 grep 并打印与它们全部匹配的行
Pass command-line arguments to grep as search patterns and print lines which match them all
我正在学习 grep
命令。
我想制作一个程序,当用户输入多个单词时,输出包含数据文件中单词的一行。
所以我将用户输入的单词与“|”连接起来并将它们放在 grep
命令中以创建我想要的程序。
但这是 OR 操作。我想做AND运算。
所以我学会了如何使用 grep
命令进行 AND 操作,如下所示。
cat <file> | grep 'pattern1' | grep 'pattern2' | grep 'pattern3'
但我不知道如何将用户输入放在'pattern1'、'pattern2'、'pattern3'位置。因为用户输入的字数是不确定的。
随着用户输入的增加,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 并非易事。在这里,我们只是将它们作为 command-line 参数传递,并将它们处理成 Awk 脚本本身内的数组。
awk 'BEGIN { for(i=1; i<ARGC; ++i) a[i]=ARGV[i];
ARGV[1]="-"; ARGC=1 }
{ for(n=1; n<=i; ++n) if ([=11=] !~ a[n]) next; }1' "$@" <file
简而言之,在 BEGIN
块中,我们将 command-line 参数从 ARGV
复制到 a
,然后替换 ARGV
和 ARGC
向 Awk 传递一个新的(明显的)command-line 参数数组,其中仅包含 -
,这意味着读取标准输入。然后,如果来自标准输入的当前输入行不匹配,我们简单地遍历 a
并跳到下一行。任何剩余的行都匹配了我们传入的所有模式,因此被打印出来。
建议使用awk
模式逻辑:
awk '/RegExp-pattern-1/ && /RegExp-pattern-2/ && /RegExp-pattern-3/ 1' input.txt
优点:您可以在 RegExp 模式上使用逻辑运算符 &&
||
。你正在扫描整个文件一次。
缺点:必须提供文件列表(不能遍历子目录),与grep -E
或grep -P
相比,RegExp语法有限
使用 grep -f
您可以 grep 多个项目,当每个项目都在文件中的一行时。
用<(command)
可以让Bash认为command
的结果是一个文件
使用 printf "%s\n"
和参数列表,每个参数都打印在一个新行上。
在一起:
grep -f <(printf "%s\n" "$@") datafile
我正在学习 grep
命令。
我想制作一个程序,当用户输入多个单词时,输出包含数据文件中单词的一行。
所以我将用户输入的单词与“|”连接起来并将它们放在 grep
命令中以创建我想要的程序。
但这是 OR 操作。我想做AND运算。
所以我学会了如何使用 grep
命令进行 AND 操作,如下所示。
cat <file> | grep 'pattern1' | grep 'pattern2' | grep 'pattern3'
但我不知道如何将用户输入放在'pattern1'、'pattern2'、'pattern3'位置。因为用户输入的字数是不确定的。
随着用户输入的增加,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 并非易事。在这里,我们只是将它们作为 command-line 参数传递,并将它们处理成 Awk 脚本本身内的数组。
awk 'BEGIN { for(i=1; i<ARGC; ++i) a[i]=ARGV[i];
ARGV[1]="-"; ARGC=1 }
{ for(n=1; n<=i; ++n) if ([=11=] !~ a[n]) next; }1' "$@" <file
简而言之,在 BEGIN
块中,我们将 command-line 参数从 ARGV
复制到 a
,然后替换 ARGV
和 ARGC
向 Awk 传递一个新的(明显的)command-line 参数数组,其中仅包含 -
,这意味着读取标准输入。然后,如果来自标准输入的当前输入行不匹配,我们简单地遍历 a
并跳到下一行。任何剩余的行都匹配了我们传入的所有模式,因此被打印出来。
建议使用awk
模式逻辑:
awk '/RegExp-pattern-1/ && /RegExp-pattern-2/ && /RegExp-pattern-3/ 1' input.txt
优点:您可以在 RegExp 模式上使用逻辑运算符 &&
||
。你正在扫描整个文件一次。
缺点:必须提供文件列表(不能遍历子目录),与grep -E
或grep -P
使用 grep -f
您可以 grep 多个项目,当每个项目都在文件中的一行时。
用<(command)
可以让Bash认为command
的结果是一个文件
使用 printf "%s\n"
和参数列表,每个参数都打印在一个新行上。
在一起:
grep -f <(printf "%s\n" "$@") datafile