用基于匹配组的评估stringreplacestring(优雅的方式,而不是用于…)

我正在寻找一种方法来replace正则expression式匹配的文件的string与另一个string,这些string将从匹配的string中生成/评估。

例如,我想要replace此文件中的时间戳(时间戳+持续时间)

1357222500 3600 ... Maybe intermediate strings... 1357226100 3600 ... Maybe intermediate strings... ... 

由人类可读的date表示(date范围)。

到目前为止,我总是使用像Bash这样的shell脚本遍历每行,匹配X行,得到匹配的组string,并在处理后打印行,例如这样(从内存中):

 IFS=" " for L in `cat file.txt`; do if [[ "${L}" =~ ^([0-9]{1,10})\ ([0-9]{1,4})\ .*$ ]]; then # Written as three lines for better readability/recognition echo -n "`date --date=@${BASH_REMATCH[1]}` - " echo -n "`date --date=@$(( ${BASH_REMATCH[1]} + ${BASH_REMATCH[2]} ))`" echo "" else echo "$L" fi done 

我想知道是否有这样一个虚构的(?)“sed-2.0”:

 cat file.txt | sed-2.0 's+/^\([0-9]\{1,10\}\) \([0-9]\{1,4\}\) .*$+`date --date="@\1"` - `date --date="@$(( \1 + \2 ))`' 

而sed-2.0replace中的反引号将被评估为shell命令传递匹配的组\1\2

我知道这不能按预期的方式工作,但我想写这样的东西。

编辑1

上面的问题编辑:在Bash脚本例子中添加了缺lessecho ""

这应该是预期的输出:

 Do 3. Jan 15:15:00 CET 2013 - Do 3. Jan 16:15:00 CET 2013 Maybe intermediate strings... Do 3. Jan 16:15:00 CET 2013 - Do 3. Jan 17:15:00 CET 2013 Maybe intermediate strings... ... 

请注意,时间戳取决于时区。

编辑2

上面的问题编辑:修正了Bash脚本例子的语法错误,添加了注释。

编辑3

上面的问题编辑:修正了Bash脚本例子的语法错误。 把“old-school example”这个短语改成了“bash script example”。


肯特和杰克曼的回答总结

两种方法有很大的不同:执行时间。 我已经比较了所有四种方法,下面是结果:

gawk使用strftime()

 /usr/bin/time gawk '/^[0-9]+ [0-9]+ / {t1=$1; $1=strftime("%c -",t1); $2=strftime("%c",t1+$2)} 1' /tmp/test ... 0.06user 0.12system 0:00.30elapsed 60%CPU (0avgtext+0avgdata 1148maxresident)k 0inputs+0outputs (0major+327minor)pagefaults 0swaps 

gawk使用getline执行( Gnu AWK手册 )

 /usr/bin/time gawk '/^[0-9]{1,10} [0-9]{1,4}/{l=$1+$2; "date --date=@"$1|getline d1; "date --date=@"l|getline d2;print d1" - "d2;next;}1' /tmp/test ... 1.89user 7.59system 0:10.34elapsed 91%CPU (0avgtext+0avgdata 5376maxresident)k 0inputs+0outputs (0major+557419minor)pagefaults 0swaps 

自定义Bash脚本

 ./sed-2.0.sh /tmp/test ... 3.98user 10.33system 0:15.41elapsed 92%CPU (0avgtext+0avgdata 1536maxresident)k 0inputs+0outputs (0major+759829minor)pagefaults 0swaps 

sed使用e选项

 /usr/bin/time sed -r 's#^([0-9]{1,10}) ([0-9]{1,4})(.*$)#echo $(date --date=@\1 )" - "$(date --date=@$((\1+\2)))#ge' /tmp/test ... 3.88user 16.76system 0:21.89elapsed 94%CPU (0avgtext+0avgdata 1272maxresident)k 0inputs+0outputs (0major+1253409minor)pagefaults 0swaps 

input数据

 for N in `seq 1 1000`; do echo -e "$(( 1357226100 + ( $N * 3600 ) )) 3600 ...\nSomething else ..." >> /tmp/test ; done 

我们可以看到使用strffime()方法的AWK是最快的。 但即使Bash脚本比shell执行速度还要快。

肯特向我们展示了一种更通用,更普遍的方式来完成我所要求的。 我的问题其实不仅限于我的时间戳例子。 在这种情况下,我必须做到这一点(用人类可读的date表示replace时间戳+持续时间),但是我有必须执行其他代码的情况。

glenn jackman向我们展示了一个特定的解决scheme,它适合于在AWK中直接进行string操作和计算的情况。

所以,这取决于你的时间(或者你的脚本可能运行的时间),数据量和用例哪个方法应该是首选的。

Solutions Collecting From Web of "用基于匹配组的评估stringreplacestring(优雅的方式,而不是用于…)"

awk oneliner 🙁日期时间格式可能与您的输出不同)

 awk '/^[0-9]{1,10} [0-9]{1,4}/{l=$1+$2; "date --date=@"$1|getline d1; "date --date=@"l|getline d2;print d1" - "d2;next;}1' file 

测试:

 kent$ echo "1357222500 3600 ... Maybe intermediate strings... 1357226100 3600 ... Maybe intermediate strings... ..."|awk '/^[0-9]{1,10} [0-9]{1,4}/{l=$1+$2; "date --date=@"$1|getline d1; "date --date=@"l|getline d2;print d1" - "d2;next;}1' Thu Jan 3 15:15:00 CET 2013 - Thu Jan 3 16:15:00 CET 2013 Maybe intermediate strings... Thu Jan 3 15:15:00 CET 2013 - Thu Jan 3 17:15:00 CET 2013 Maybe intermediate strings... ... 

Gnu sed

如果你有gnu sed,那么你的“不工作”sed行的想法可以通过应用gnu sed的s/foo/shell cmds/ge如下所示:

 sed -r 's#^([0-9]{1,10}) ([0-9]{1,4})(.*$)#echo $(date --date=@\1 )" - "$(date --date=@$((\1+\2)))#ge' file 

测试

 kent$ echo "1357222500 3600 ... Maybe intermediate strings... 1357226100 3600 ... Maybe intermediate strings... ..."|sed -r 's#^([0-9]{1,10}) ([0-9]{1,4})(.*$)#echo $(date --date=@\1 )" - "$(date --date=@$((\1+\2)))#ge' Thu Jan 3 15:15:00 CET 2013 - Thu Jan 3 16:15:00 CET 2013 Maybe intermediate strings... Thu Jan 3 16:15:00 CET 2013 - Thu Jan 3 17:15:00 CET 2013 Maybe intermediate strings... ... 

如果我愿意这样做,我个人会用awk去。 因为它简单易懂。

最后我粘贴我的sed / awk版本信息:

 kent$ sed --version|head -1 sed (GNU sed) 4.2.2 kent$ awk -V|head -1 GNU Awk 4.0.1 

根据您的示例输入:

 gawk '/^[0-9]+ [0-9]+ / {t1=$1; $1=strftime("%c -",t1); $2=strftime("%c",t1+$2)} 1' 

输出

 Thu 03 Jan 2013 09:15:00 AM EST - Thu 03 Jan 2013 10:15:00 AM EST ... Maybe intermediate strings... Thu 03 Jan 2013 10:15:00 AM EST - Thu 03 Jan 2013 11:15:00 AM EST ... Maybe intermediate strings... ...