Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
688 views
in Technique[技术] by (71.8m points)

regex - How to change date format in sed?

Say I want to convert a date in timestamp to another format through the date command. In console I would say date -d@<timestamp> but I happen to want to do this to many fields in a text file.

Using the e to execute in sed (sed (GNU sed) 4.2.2, actually) I am saying:

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

It works, nice!

Now I created a dummy file myfile:

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

Which I would like to have replaced to the same but having the relative year of the timestamps:

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

However, if I try to run the same command as above it fails:

$ sed -r 's#([0-9]{10})#date -d@"1" "+%Y";#e' myfile
sh: my: command not found
sh: but: command not found

Because sed interprets the first words as something to execute.

Obviously it works if I just fetch these data and nothing else:

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

So I wonder: what should I do to call date against captured groups in sed and replace text with it, considering it is surrounded by other text that have to remain untouched?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

e switch in sed substitute applies sh -c to unmatched text as well as evident from this command:

echo 'a 1449158360' | sed -r 's#([0-9]{10})#date -d@1 "+%Y";#e'
sh: a: command not found 

So even though we are matching only 1449158360 but sh -c is being run on a 1449158360.

Due to absence of non-greedy and lookaheads regex in sed this workaround regex might appear crazy but this is how you can run it for multiple matching input from file as in your question:

sed -r 's#(([^0-9][0-9]{0,9})*)([0-9]{10})(([0-9]{0,9}[^0-9])*)#printf "%s%s%s" "1" $(date -d@3 "+%Y") "4";#ge' file

Basically we are matching <before>10-digits<after> in this regex.

Output:

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

To clarify the regex used I have created this demo.

By no means this is a generic solution to e mode issue, treat it as a regex based workaround.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...