将shell变量作为/ pattern /传递给awk


59

在我的一个shell函数中具有以下内容:

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

,因此当称为时_process $arg$arg将作为传递$1,并用作搜索模式。它以这种方式工作,因为shell扩展$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/为变量,而是按字面意义。

Answers:


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"


这正是我想要的一种解决方案(第一个示例),因为它保留了语义,这是我的目标。谢谢。
branquito 2014年

1
我没有注意到BEGIN块的删除:在数字上下文中,未分配的变量被视为0,否则将其视为空字符串。因此,在以下情况下,未分配的变量将为falseif (p) ...
glenn jackman

是的,我注意到,每次将它在BEGIN块上都设置为零,因为它可以作为开关。但是有趣的是,我现在尝试使用编写脚本$0 ~ pattern,它不起作用,但是/'"$1"'/起作用了!:O
branquito

可能与$line检索方式有关whois $line$line对WHILE DO块中来自文件的输出进行模式搜索。
branquito 2014年

请显示-的内容,$line在问题中进行正确格式化。
格伦·杰克曼

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对于有效的转义序列,所有s的工作都相同:

$ 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更改为退格字符)。


因此,您说的是,例如,如果pattern \d{3}能找到三个数字,那么如果我对您的理解很好,那将无法按预期工作?
branquito 2014年

2
对于\d哪个无效的C转义序列,取决于您的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`表示数字)。
斯特凡Chazelas

它说:awk警告-转义序列\d' treated as plain d'd {3},所以我想在这种情况下会有问题吗?
branquito 2014年

1
抱歉,我不好,我的答案有错字。那么环境变量的名称必须匹配ENVIRON["PATTERN"]PATTERN环境变量。如果要使用shell变量,则需要先将其导出(export variable)或使用ENV=VALUE awk '...ENVIRON["ENV"]'传递答案的env-var语法。
斯特凡Chazelas

1
因为您需要导出一个shell变量,以便将其在环境中传递给命令。
斯特凡Chazelas

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/查找模式的行为相同,则可能是一个不错的解决方案。我会尝试。
branquito 2014年

1
我进行的快速测试似乎同样有效,但我什至无法保证... :)
Hunter Eidson 2014年

0

不,但是您可以简单地将模式插入到传递给awk的双引号字符串中:

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

请注意,您现在必须转义双引号的awk文字,但这仍然是实现此目的的最简单方法。


如果$pattern包含空格,这种方法是安全的吗,我上面的示例将起作用,因为$ 1受“ $ 1”双引号保护,但是不能确定情况如何。
branquito 2014年

2
原始示例在单引号的第二个字符串处结束',然后保护$1通孔双引号,然后为awk程序的后半部分附加另一个单引号。如果我理解正确,那么这应该与$1通过外部单引号保护过孔具有完全相同的效果-awk永远不会看到您在其周围加上双引号。
Kilian Foth,2014年

4
但是,如果$pattern包含^/ {system("rm -rf /")};,那么您将遇到大麻烦。
斯特凡Chazelas

仅用“”包裹起来,这种方法的缺点是什么?
branquito 2014年

-3

您可以使用eval函数,该函数在本示例中在运行awk之前解析nets变量。

nets="searchtext"
eval "awk '/"${nets}"/'" file.txt
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.