【发布时间】:2012-12-31 14:01:25
【问题描述】:
我正在寻找一种方法来用另一个字符串替换由正则表达式匹配的文件字符串,该字符串将从匹配的字符串中生成/评估。
比如我想替换这个文件中的时间戳(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 替换中的反引号将被评估为传递匹配组 \1 和 \2 的 shell 命令。
我知道这不会按预期工作,但我想写这样的东西。
编辑 1
编辑上述问题:在 Bash 脚本示例的 if 中添加了缺少的 echo ""。
这应该是预期的输出:
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”。
Kent 和glenn jackman 的回答总结
这两种方法有很大的不同:执行时间。我已经比较了所有四种方法,结果如下:
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 Manual) 使用执行
/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
我们可以看到使用strffime() 方法的AWK 是最快的。但即使是 Bash 脚本也比使用 shell 执行的 sed 更快。
Kent 向我们展示了一种更通用、更通用的方式来完成我的要求。我的问题实际上不仅限于我的时间戳示例。在这种情况下,我必须这样做(用人类可读的日期表示替换时间戳 + 持续时间),但我遇到了必须执行其他代码的情况。
glenn jackman 向我们展示了一个特定的解决方案,它适用于您可以直接在 AWK 中进行字符串操作和计算的情况。
因此,这取决于您拥有的时间(或您的脚本可能运行的时间)、数据量以及应首选哪种方法的用例。
【问题讨论】:
-
你有 gnu sed 吗?你能添加一个预期的输出吗?
-
是的,Gnu sed 和 perl 都可用。添加了预期的输出。
-
旁注:使用
$(< file)而不是$(cat file)。见here。