gawk - 有条件的(即,取决于正则表达式匹配)查找/替换为某些转换器功能

Posted

技术标签:

【中文标题】gawk - 有条件的(即,取决于正则表达式匹配)查找/替换为某些转换器功能【英文标题】:gawk – conditional (that is, depending on regex match) find/replace with some converter function 【发布时间】:2015-01-20 09:59:44 【问题描述】:

我的输入文件是

input.txt

News A 1 B 2h 0m 1s C text1
100 A 2 B 120m 1s C text2
Show A 3 B 450s C text3
Tom A 4 B 0:30 C text4
Laura A 5 B 20 C text5
Something A 6 B 1h 100m 70s C text6
50 A 7 B 10s C text7

(您在第 6 行看到奇怪的时间格式,但这是故意的,仅用于演示,以简化逻辑而无需额外的 0-59 要求)。

我想对每一行应用以下正则表达式:

^(.*?)\sA\s(.*?)\sB\s(.*?)\sC\s(.*?)$  

注意\3 的语法。有效变体:

\d1,h \d1,m \d1,s \d1,m \d1,s \d1,s \d1, 等于 \d1,s

我需要将其转换为秒,但如果这部分未能通过此验证,请保持原样。无论如何,让我们将结果命名为$sec

我需要定义以下正则表达式变量:

$price == '\d1, ', $names == 'Bob|Tom|Laura|Sandra', $tags == 'News|Show'(或者(?:regex)语法,这里不知道哪个更好)

然后,将该行替换为以下内容:

如果\1 ~ $price

"ID: \1; time: $sec seconds; description: \1 – buy for $\1!

如果\1 ~ $names

description: \4 from @\1; time: $sec seconds

如果\1 ~ $tags

ID: \2; #\1; time: $sec seconds; description: \4

else(如果\1 不匹配任何预定义的正则表达式变量,或匹配多个变量):

ID: \2; time: $sec seconds; \1; description: \4

所以输出文件应该是

output.txt

ID: 1; #News; time: 7201 seconds; description: text1
ID: 2; time: 7201 seconds; description: text2 – buy for $100!
ID: 3; #Show; time: 450 seconds; description: text3
description: text4 from @Tom; time: 0:30
description: text5 from @Laura; time: 20 seconds
ID: 6; time: 9670 seconds; Something; description: text6
ID: 7; time: 10 seconds; description: text7 – buy for $50!

我使用了这个代码:

gawk -F '\\|' 'function _time(str) 
if (str ~ /([[:digit:]]+)h\s([[:digit:]]+)m\s([[:digit:]]+)s/ ) 
match(str, /([[:digit:]]+)h\s([[:digit:]]+)m\s([[:digit:]]+)s/, arr)
return arr[1]*3600+arr[2]*60+arr[3] 
else if (str ~ /([[:digit:]]+)m\s([[:digit:]]+)s/ ) 
match(str, /([[:digit:]]+)m\s([[:digit:]]+)s/, arr)
return arr[1]*60+arr[2] 
else if (str ~ /([[:digit:]]+)s/ || str ~ /([[:digit:]]+)/) 
match(str, /([[:digit:]]+).*?/, arr)
return arr[1] 
else 
return str  
match($0, /^(.*?)\sA\s(.*?)\sB\s(.*?)\sC\s(.*?)$/, _f) 
 if (_f[1] ~ /[[:digit:]]+/) 
printf "ID: %s; time: %s seconds; description: %s – buy for $%s\n", _f[2], _time(_f[3]), _f[4], _f[1] 
else if (_f[1] ~ /Bob|Tom|Laura|Sandra/) 
printf "description: %s from %s; time: %s seconds\n", _f[4], _f[1], _time(_f[3]) 
else if (_f[1] ~ /News|Show/) 
printf "ID: %s; #%s; time: %s seconds; description: %s\n", _f[2], _f[1], _time(_f[3]), _f[4] 
else 
printf "ID: %s; time: %s seconds; %s; description: %s\n", _f[2], _time(_f[3]), _f[1], _f[4]  ' input.txt > output.txt.

有四个问题。

    为什么我看到输入行位于输出行之前?我希望用结果行替换输入行! 为什么我在第 8 个输出行看到0 seconds?我希望看到0:30 seconds。为什么0:30 匹配/([[:digit:]]+)s/ || /([[:digit:]]+)/)?我预计 else return str 会在这里发挥作用(保持 0:30 不变)。 我使用if (_f[1] ~ /regex/) 语法,但我不知道如何将/regex/ 放入变量中。当我将它放入变量时,它不起作用。 我不知道 AWK 的工作原理。它是否测试每个条件?如果是,当_f[1]匹配多个条件(if (_f[1] ~ /X|A/) ... else if (_f[1] ~ /A|B/) ... )时应该怎么做?我想把这个case放到else 。如果不是,我会选择立即打印第一场比赛的结果,以提高性能。

附加说明:我使用的是 Cygwin 命令行,没有使用单独的 .awk 文件作为代码。

【问题讨论】:

哪个版本的 gawk ?对于 0:30,它与您的最后一个正则表达式 /([[:digit:]]+)/) 匹配,因为 0 是一个数字,您使用 1 个或多个数字进行测试。通过指定正则表达式的开始和结束,您可能会获得更好的结果,例如:/^[[:digit:]]+$/ 创建捕获组的括号在比较中是无用的。剩下的我会做测试,起初它不适用于我的 gawk 版本 3.1.7 只见树木不见森林。我想您的主要问题是如何将人类可读的时间戳转换为秒,不是吗? 而不是/regex/,你可以做match($0, regex) @Jdamian:实际上,没有。绝对没有。时间转换器只是一个简单的例子,主要问题是如何将任何函数的结果与if/else逻辑结合起来。 【参考方案1】:

尝试设置FS:

awk 'print "timestamp:", $3' FS=' (A|B|C) ' input.txt

【讨论】:

我解决了所有这些问题,除了一个,但我会提出一个明确的单独问题

以上是关于gawk - 有条件的(即,取决于正则表达式匹配)查找/替换为某些转换器功能的主要内容,如果未能解决你的问题,请参考以下文章

精通awk系列(16):gawk支持的正则表达式

正则表达式基础

如何调用正则表达式匹配取决于给定的输入值[重复]

如何使用 awk 打印匹配的正则表达式模式?

gawk进阶

perl正则表达式