我正在寻找一种方法来替换由正则表达式匹配的文件字符串,并用另一个字符串来生成/评估匹配的字符串。
比如我想替换这个文件中的时间戳(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 ""
在if
Bash 脚本示例中添加了缺失。
这应该是预期的输出:
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 使用执行通过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
输入数据
for N in `seq 1 1000`; do echo -e "$(( 1357226100 + ( $N * 3600 ) )) 3600 ...\nSomething else ..." >> /tmp/test ; done
我们可以看到 AWK 使用该strffime()
方法是最快的。但即使是 Bash 脚本也比sed
shell 执行要快。
肯特向我们展示了一种更通用、更通用的方法来完成我所要求的。我的问题实际上不仅限于我的时间戳示例。在这种情况下,我必须这样做(用人类可读的日期表示替换时间戳+持续时间),但是我遇到了必须执行其他代码的情况。
glenn jackman向我们展示了一个特定的解决方案,该解决方案适用于您可以直接在 AWK 中进行字符串操作和计算的情况。
因此,这取决于您拥有的时间(或您的脚本可能运行的时间)、数据量以及应首选哪种方法的用例。