3

我正在寻找一种方法来替换由正则表达式匹配的文件字符串,并用另一个字符串来生成/评估匹配的字符串。

比如我想替换这个文件中的时间戳(timestamp + duration)

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

通过人类可读的日期表示(日期范围)。

直到现在,我总是使用像 Bash 这样的 shell 脚本来遍历每一行,匹配行 X,获取匹配的组字符串并在处理后打印该行,例如这种方式(从内存中):

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.0 替换中的反引号将被评估为传递匹配组的 shell 命令\1\2.

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

编辑 1

编辑上述问题:echo ""ifBash 脚本示例中添加了缺失。

这应该是预期的输出:

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 脚本示例的语法错误。将短语“老派示例”更改为“Bash 脚本示例”。


肯特格伦杰克曼的回答总结

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

呆呆地使用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 使用执行通过getlineGnu 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

输入数据

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

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

肯特向我们展示了一种更通用、更通用的方法来完成我所要求的。我的问题实际上不仅限于我的时间戳示例。在这种情况下,我必须这样做(用人类可读的日期表示替换时间戳+持续时间),但是我遇到了必须执行其他代码的情况。

glenn jackman向我们展示了一个特定的解决方案,该解决方案适用于您可以直接在 AWK 中进行字符串操作和计算的情况。

因此,这取决于您拥有的时间(或您的脚本可能运行的时间)、数据量以及应首选哪种方法的用例。

4

2 回答 2

3

根据您的样本输入:

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...
...
于 2012-12-31T15:13:13.637 回答
2

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
于 2012-12-31T14:21:36.093 回答