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