如何在sed中更改日期格式?

3

如果我想通过date命令将时间戳转换为另一种格式。在控制台中,我会使用date -d@<timestamp>,但我想在文本文件中对许多字段执行此操作。

我在sed(GNU sed) 4.2.2中使用e来执行,具体如下:

$ echo 1449158360 | sed -r 's#.*([0-9]{10}).*#date -d@\1 "+%Y";#e'
2015

很好,它可以运行!

现在我创建了一个虚拟文件myfile:

my timestamp is 1449158360 but also I wonder what date was 1359199960.

我希望将时间戳替换为相对年份的时间戳:

my timestamp is 2015 but also I wonder what date was 2013.

然而,如果我尝试运行与上述相同的命令,它会失败:
$ sed -r 's#([0-9]{10})#date -d@"\1" "+%Y";#e' myfile
sh: my: command not found
sh: but: command not found

因为sed将第一个单词解释为要执行的内容。

如果我只获取这些数据而不进行其他操作,显然它可以工作:

$ sed -r 's#.*([0-9]{10}).*#date -d@"\1" "+%Y";#ge' myfile
2015

我在想:如果我需要在sed中对捕获组调用date并替换文本,而且该文本被其他必须保持不变的文本包围,那么我应该怎么做呢?


可能是如何将文件中的所有Unix日期转换?的重复问题。 - Toby Speight
1
不是这样的,@TobySpeight,因为我在这里问为什么它不能按照我尝试的方式工作。 - fedorqui
你有没有看过那里的答案?具体来说,我对sed s///e的评论 - 我建议使用Perl而不是sed来完成这项工作。 - Toby Speight
1
@TobySpeight我已经阅读了它们,甚至有一篇被删除的答案是我写的:) 我看到这里的重点是你在回答中提到的,“注意/e会导致命令替换替换整个模式空间,因此您可能需要利用保留空间来保留替换前后的文本。留给读者作为练习”。就是这最后一部分我无法执行。稍后我会编辑以便更关注这个特定的问题。 - fedorqui
2个回答

6
sed 中的 e 替换选项会将 sh -c 应用于未匹配的文本,如下命令所示:
echo 'a 1449158360' | sed -r 's#([0-9]{10})#date -d@\1 "+%Y";#e'
sh: a: command not found 

因此,即使我们只匹配1449158360,但sh -c仍在a 1449158360上运行。
由于sed中缺少非贪婪和lookaheads的正则表达式,所以这种解决方法的正则表达式看起来很疯狂,但这是您可以在文件中运行多个匹配输入的方式,就像您的问题一样。
sed -r 's#(([^0-9][0-9]{0,9})*)(\b[0-9]{10}\b)(([0-9]{0,9}[^0-9])*)#printf "%s%s%s" "\1" $(date -d@\3 "+%Y") "\4";#ge' file

基本上我们在这个正则表达式中匹配的是<before>10位数字<after>

输出:

my timestamp is 2015 but also I wonder what date was 2013.

为了澄清使用的正则表达式,我创建了这个演示
这绝不是一个通用的解决方案来解决'e'模式问题,将其视为基于正则表达式的解决方法。

1
哇,这太棒了,非常感谢!我学到了两件事:首先,如何有效地在 sed 中执行命令。然后,如何使用嵌套捕获组(一开始我想知道 \2 在哪里,然后我看到 \1 可以是多个 \2 的情况)。非常感谢! - fedorqui
1
很遗憾,由于 sed 正则表达式的限制,即没有惰性量词、前瞻和非捕获组,所以这个正则表达式变得有点复杂。很高兴你喜欢它。对我来说,这是一个非常有趣的问题。 - anubhava
1
谢谢,多亏这个,我成功创建了一个方便的命令来输出 ~/zsh_history,并将其转换为人类可读的日期格式:sed -E 's/(([^0-9][0-9]{0,9})*)(\b[0-9]{10}\b)(([0-9]{0,9}[^0-9])*)/printf "%s%s%s" "\1" "$(date -ud @\3)" "\4"/ge' ~/.zsh_history | less - tiagovrtr

0

正如你所说,使用 e 标志时,sed 会将整个句子作为要执行的内容。

一个解决方法可能是这样的。

myfile 更改为:

echo "my timestamp is 1449158360 but also I wonder what date was 1359199960."

然后:

sed -r 's#([0-9]{10})#\`date -d@\1 "+%Y"\`#ge' myfile

希望这有所帮助


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接