How to replace epoch timestamps in a file with other formats?
Clash Royale CLAN TAG#URR8PPP
up vote
9
down vote
favorite
I have a file that contains epoch dates which I need converting to human-readable. I already know how to do the date conversion, eg:
[server01 ~]$ date -d@1472200700
Fri 26 Aug 09:38:20 BST 2016
..but I'm struggling to figure out how to get sed
to walk through the file and convert all the entries.
The file format looks like this:
#1472047795
ll /data/holding/email
#1472047906
cat /etc/rsyslog.conf
#1472048038
ll /data/holding/web
text-processing sed date
add a comment |
up vote
9
down vote
favorite
I have a file that contains epoch dates which I need converting to human-readable. I already know how to do the date conversion, eg:
[server01 ~]$ date -d@1472200700
Fri 26 Aug 09:38:20 BST 2016
..but I'm struggling to figure out how to get sed
to walk through the file and convert all the entries.
The file format looks like this:
#1472047795
ll /data/holding/email
#1472047906
cat /etc/rsyslog.conf
#1472048038
ll /data/holding/web
text-processing sed date
1
For future reference (assuming this is a Bash history file; it looks like one), look to theHISTTIMEFORMAT
shell variable to control the format at time of writing.
– Toby Speight
Aug 30 '16 at 14:44
@Toby the value of HISTTIMEFORMAT is used when displaying (to stdout), but only its status (set to anything even null vs unset) matters when writing HISTFILE.
– dave_thompson_085
Sep 3 '16 at 10:43
Thanks @dave, I didn't know that (not being a user of history times myself).
– Toby Speight
Sep 5 '16 at 9:00
date -d
is not portable to say Solaris... I'm assuming this is on a system with mostly GNU tools? (GNU AWK / Perl tend to be the more portable methods to deal with date conversions).gawk ' if ($0 ~ /^#[0-9]*$/) print strftime("%c",substr($0,2)); else print ' < file
(strftime
seems non-portable...)
– Gert van den Berg
Jan 13 '17 at 8:07
add a comment |
up vote
9
down vote
favorite
up vote
9
down vote
favorite
I have a file that contains epoch dates which I need converting to human-readable. I already know how to do the date conversion, eg:
[server01 ~]$ date -d@1472200700
Fri 26 Aug 09:38:20 BST 2016
..but I'm struggling to figure out how to get sed
to walk through the file and convert all the entries.
The file format looks like this:
#1472047795
ll /data/holding/email
#1472047906
cat /etc/rsyslog.conf
#1472048038
ll /data/holding/web
text-processing sed date
I have a file that contains epoch dates which I need converting to human-readable. I already know how to do the date conversion, eg:
[server01 ~]$ date -d@1472200700
Fri 26 Aug 09:38:20 BST 2016
..but I'm struggling to figure out how to get sed
to walk through the file and convert all the entries.
The file format looks like this:
#1472047795
ll /data/holding/email
#1472047906
cat /etc/rsyslog.conf
#1472048038
ll /data/holding/web
text-processing sed date
text-processing sed date
edited Nov 18 at 10:05
Rui F Ribeiro
38.2k1475123
38.2k1475123
asked Aug 30 '16 at 7:38
machinist
6716
6716
1
For future reference (assuming this is a Bash history file; it looks like one), look to theHISTTIMEFORMAT
shell variable to control the format at time of writing.
– Toby Speight
Aug 30 '16 at 14:44
@Toby the value of HISTTIMEFORMAT is used when displaying (to stdout), but only its status (set to anything even null vs unset) matters when writing HISTFILE.
– dave_thompson_085
Sep 3 '16 at 10:43
Thanks @dave, I didn't know that (not being a user of history times myself).
– Toby Speight
Sep 5 '16 at 9:00
date -d
is not portable to say Solaris... I'm assuming this is on a system with mostly GNU tools? (GNU AWK / Perl tend to be the more portable methods to deal with date conversions).gawk ' if ($0 ~ /^#[0-9]*$/) print strftime("%c",substr($0,2)); else print ' < file
(strftime
seems non-portable...)
– Gert van den Berg
Jan 13 '17 at 8:07
add a comment |
1
For future reference (assuming this is a Bash history file; it looks like one), look to theHISTTIMEFORMAT
shell variable to control the format at time of writing.
– Toby Speight
Aug 30 '16 at 14:44
@Toby the value of HISTTIMEFORMAT is used when displaying (to stdout), but only its status (set to anything even null vs unset) matters when writing HISTFILE.
– dave_thompson_085
Sep 3 '16 at 10:43
Thanks @dave, I didn't know that (not being a user of history times myself).
– Toby Speight
Sep 5 '16 at 9:00
date -d
is not portable to say Solaris... I'm assuming this is on a system with mostly GNU tools? (GNU AWK / Perl tend to be the more portable methods to deal with date conversions).gawk ' if ($0 ~ /^#[0-9]*$/) print strftime("%c",substr($0,2)); else print ' < file
(strftime
seems non-portable...)
– Gert van den Berg
Jan 13 '17 at 8:07
1
1
For future reference (assuming this is a Bash history file; it looks like one), look to the
HISTTIMEFORMAT
shell variable to control the format at time of writing.– Toby Speight
Aug 30 '16 at 14:44
For future reference (assuming this is a Bash history file; it looks like one), look to the
HISTTIMEFORMAT
shell variable to control the format at time of writing.– Toby Speight
Aug 30 '16 at 14:44
@Toby the value of HISTTIMEFORMAT is used when displaying (to stdout), but only its status (set to anything even null vs unset) matters when writing HISTFILE.
– dave_thompson_085
Sep 3 '16 at 10:43
@Toby the value of HISTTIMEFORMAT is used when displaying (to stdout), but only its status (set to anything even null vs unset) matters when writing HISTFILE.
– dave_thompson_085
Sep 3 '16 at 10:43
Thanks @dave, I didn't know that (not being a user of history times myself).
– Toby Speight
Sep 5 '16 at 9:00
Thanks @dave, I didn't know that (not being a user of history times myself).
– Toby Speight
Sep 5 '16 at 9:00
date -d
is not portable to say Solaris... I'm assuming this is on a system with mostly GNU tools? (GNU AWK / Perl tend to be the more portable methods to deal with date conversions). gawk ' if ($0 ~ /^#[0-9]*$/) print strftime("%c",substr($0,2)); else print ' < file
(strftime
seems non-portable...)– Gert van den Berg
Jan 13 '17 at 8:07
date -d
is not portable to say Solaris... I'm assuming this is on a system with mostly GNU tools? (GNU AWK / Perl tend to be the more portable methods to deal with date conversions). gawk ' if ($0 ~ /^#[0-9]*$/) print strftime("%c",substr($0,2)); else print ' < file
(strftime
seems non-portable...)– Gert van den Berg
Jan 13 '17 at 8:07
add a comment |
6 Answers
6
active
oldest
votes
up vote
6
down vote
accepted
Assuming consistent file format, with bash
you can read the file line by line, test if it's in given format and then do the conversion:
while IFS= read -r i; do [[ $i =~ ^#([0-9]10)$ ]] &&
date -d@"$BASH_REMATCH[1]"; done <file.txt
BASH_REMATCH
is an array whose first element is the first captured group in Regex matching, =~
, in this case the epoch.
If you want to keep the file structure:
while IFS= read -r i; do if [[ $i =~ ^#([0-9]10)$ ]]; then printf '#%sn'
"$(date -d@"$BASH_REMATCH[1]")"; else printf '%sn' "$i"; fi; done <file.txt
this will output the modified contents to STDOUT, to save it in a file e.g. out.txt
:
while ...; do ...; done >out.txt
Now if you wish, you can replace the original file:
mv out.txt file.txt
Example:
$ cat file.txt
#1472047795
ll /data/holding/email
#1472047906
cat /etc/rsyslog.conf
#1472048038
ll /data/holding/web
$ while IFS= read -r i; do [[ $i =~ ^#([0-9]10)$ ]] && date -d@"$BASH_REMATCH[1]"; done <file.txt
Wed Aug 24 20:09:55 BDT 2016
Wed Aug 24 20:11:46 BDT 2016
Wed Aug 24 20:13:58 BDT 2016
$ while IFS= read -r i; do if [[ $i =~ ^#([0-9]10)$ ]]; then printf '#%sn' "$(date -d@"$BASH_REMATCH[1]")"; else printf '%sn' "$i"; fi; done <file.txt
#Wed Aug 24 20:09:55 BDT 2016
ll /data/holding/email
#Wed Aug 24 20:11:46 BDT 2016
cat /etc/rsyslog.conf
#Wed Aug 24 20:13:58 BDT 2016
ll /data/holding/web
Nice....that prints the converted date to screen, now how do I get that command to replace the entries in the file?
– machinist
Aug 30 '16 at 8:01
@machinist Check my edits..
– heemayl
Aug 30 '16 at 8:17
1
If you are using a recent version ofbash
,printf
can do the conversion itself:printf '#%(%F %H)Tn' "$BASH_REMATCH[1]"
.
– chepner
Aug 30 '16 at 19:54
add a comment |
up vote
14
down vote
While it's possible with GNU sed
with things like:
sed -E 's/^#([0-9]+).*$/date -d @1/e'
That would be terribly inefficient (and is easy to introduce arbitrary command injection vulnerabilities1) as that would mean running one shell and one date
command for each #xxxx
line, virtually as bad as a shell while read
loop. Here, it would be better to use things like perl
or gawk
, that is text processing utilities that have date conversion capabilities built-in:
perl -MPOSIX -pe 's/^#(d+).*/ctime $1/se'
Or:
gawk '/^#/$0 = strftime("%c", substr($0, 2));1'
1 If we had written ^#([0-9]).*
instead of ^#([0-9]).*$
(as I did in an earlier version of this answer), then in multi-byte locales like UTF-8 ones (the norm nowadays), with an input like #1472047795<0x80>;reboot
, where that <0x80>
is the byte value 0x80 which does not form a valid character, that s
command would have ended up running date -d@1472047795<0x80>; reboot
for instance. While with the extra $
, those lines would not be substituted. An alternative approach would be: s/^#([0-9])/date -d @1 #/e
, that is leave the part after the #xxx
date as a shell comment
What about using just a single instance ofdate -f
to do all the conversions in a stream-wise manner?
– Digital Trauma
Aug 30 '16 at 20:58
The perl command seems to add a new line after ctime $1 and I can't find any way to remove it.
– Alex Harvey
Jun 23 '17 at 7:47
1
@Alex. Right. See edit. Adding thes
flag makes so that.*
also includes the newline on input. You can also usestrftime "%c", localtime $1
.
– Stéphane Chazelas
Jun 23 '17 at 14:02
@StéphaneChazelas thanks so much. It's a great answer.
– Alex Harvey
Jun 23 '17 at 14:04
add a comment |
up vote
2
down vote
All the other answers spawn a new date
process for every epoch date that needs to be converted. This could potentially add performance overhead if your input is large.
However GNU date has a handy -f
option that allows a single process instance of date
to continuously read input dates without the need for a new fork. So we can use sed
, paste
and date
in this manner such that each one only gets spawned once (2x for sed
) regardless of how large the input is:
$ paste -d 'n' <( sed '2~2d;y/#/@/' epoch.txt | date -f - ) <( sed '1~2d' epoch.txt )
Wed Aug 24 07:09:55 PDT 2016
ll /data/holding/email
Wed Aug 24 07:11:46 PDT 2016
cat /etc/rsyslog.conf
Wed Aug 24 07:13:58 PDT 2016
ll /data/holding/web
$
- The two
sed
commands respectively basically delete even and odd lines of the input; the first one also replaces#
with@
to give the correct epoch timestamp format. - The first
sed
output is then piped todate -f
which does the required date conversion, for every line of input that it receives. - These two streams are then interlaced into the single required output using
paste
. The<( )
constructs are bash process substitutions that effectively trick paste into thinking it is reading from given filenames when it is in fact reading the output piped from the command inside.-d 'n'
tellspaste
to separate odd and even output lines with a newline. You could change (or remove) this if for example you want the timestamp on the same line as the other text.
Note that there are several GNUisms and Bashisms in this command. This is not Posix-compliant and should not be expected to be portable outside of the GNU/Linux world. For example date -f
does something else on OSXes BSD date
variant.
date -d
(from the question) is also non-portable... (On FreeBSD it will try to mess with DST settings, on Solaris it will give an error...) The question does not specify an OS though...
– Gert van den Berg
Jan 13 '17 at 8:14
@GertvandenBerg yes, this is addressed in the last paragraph of this answer.
– Digital Trauma
Jan 13 '17 at 15:56
I mean that the asker's sample also has portability issues... (They should probably have tagged an OS...)
– Gert van den Berg
Jan 14 '17 at 16:06
add a comment |
up vote
1
down vote
Assuming the date format you have in your post is what you want, the following regex should fit your needs.
sed -E 's/#(1[0-9]9)(.*)/echo 1 $(date -d @1)/e' log.file
Be mindful of the fact this will only replace one epoch per line.
I'm getting the following error with that command:sed: -e expression #1, char 48: invalid reference 3 on 's' command's RHS
– machinist
Aug 30 '16 at 7:53
1
My mistake, edited the post.
– Hatclock
Aug 30 '16 at 8:25
add a comment |
up vote
0
down vote
using sed :
sed -r 's/#([0-9]*)/echo $(date -d @1)/eg' test.txt
output :
ر أغس 24 16:09:55 EET 2016
ll /data/holding/email
ر أغس 24 16:11:46 EET 2016
cat /etc/rsyslog.conf
ر أغس 24 16:13:58 EET 2016
ll /data/holding/web
as my locale language is Arabic :)
add a comment |
up vote
0
down vote
My solution how to do that in a pipeline
cat test.txt | sed 's/^/echo "/; s/([0-9]10)/`date -d @1`/; s/$/"/' | bash
add a comment |
6 Answers
6
active
oldest
votes
6 Answers
6
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
6
down vote
accepted
Assuming consistent file format, with bash
you can read the file line by line, test if it's in given format and then do the conversion:
while IFS= read -r i; do [[ $i =~ ^#([0-9]10)$ ]] &&
date -d@"$BASH_REMATCH[1]"; done <file.txt
BASH_REMATCH
is an array whose first element is the first captured group in Regex matching, =~
, in this case the epoch.
If you want to keep the file structure:
while IFS= read -r i; do if [[ $i =~ ^#([0-9]10)$ ]]; then printf '#%sn'
"$(date -d@"$BASH_REMATCH[1]")"; else printf '%sn' "$i"; fi; done <file.txt
this will output the modified contents to STDOUT, to save it in a file e.g. out.txt
:
while ...; do ...; done >out.txt
Now if you wish, you can replace the original file:
mv out.txt file.txt
Example:
$ cat file.txt
#1472047795
ll /data/holding/email
#1472047906
cat /etc/rsyslog.conf
#1472048038
ll /data/holding/web
$ while IFS= read -r i; do [[ $i =~ ^#([0-9]10)$ ]] && date -d@"$BASH_REMATCH[1]"; done <file.txt
Wed Aug 24 20:09:55 BDT 2016
Wed Aug 24 20:11:46 BDT 2016
Wed Aug 24 20:13:58 BDT 2016
$ while IFS= read -r i; do if [[ $i =~ ^#([0-9]10)$ ]]; then printf '#%sn' "$(date -d@"$BASH_REMATCH[1]")"; else printf '%sn' "$i"; fi; done <file.txt
#Wed Aug 24 20:09:55 BDT 2016
ll /data/holding/email
#Wed Aug 24 20:11:46 BDT 2016
cat /etc/rsyslog.conf
#Wed Aug 24 20:13:58 BDT 2016
ll /data/holding/web
Nice....that prints the converted date to screen, now how do I get that command to replace the entries in the file?
– machinist
Aug 30 '16 at 8:01
@machinist Check my edits..
– heemayl
Aug 30 '16 at 8:17
1
If you are using a recent version ofbash
,printf
can do the conversion itself:printf '#%(%F %H)Tn' "$BASH_REMATCH[1]"
.
– chepner
Aug 30 '16 at 19:54
add a comment |
up vote
6
down vote
accepted
Assuming consistent file format, with bash
you can read the file line by line, test if it's in given format and then do the conversion:
while IFS= read -r i; do [[ $i =~ ^#([0-9]10)$ ]] &&
date -d@"$BASH_REMATCH[1]"; done <file.txt
BASH_REMATCH
is an array whose first element is the first captured group in Regex matching, =~
, in this case the epoch.
If you want to keep the file structure:
while IFS= read -r i; do if [[ $i =~ ^#([0-9]10)$ ]]; then printf '#%sn'
"$(date -d@"$BASH_REMATCH[1]")"; else printf '%sn' "$i"; fi; done <file.txt
this will output the modified contents to STDOUT, to save it in a file e.g. out.txt
:
while ...; do ...; done >out.txt
Now if you wish, you can replace the original file:
mv out.txt file.txt
Example:
$ cat file.txt
#1472047795
ll /data/holding/email
#1472047906
cat /etc/rsyslog.conf
#1472048038
ll /data/holding/web
$ while IFS= read -r i; do [[ $i =~ ^#([0-9]10)$ ]] && date -d@"$BASH_REMATCH[1]"; done <file.txt
Wed Aug 24 20:09:55 BDT 2016
Wed Aug 24 20:11:46 BDT 2016
Wed Aug 24 20:13:58 BDT 2016
$ while IFS= read -r i; do if [[ $i =~ ^#([0-9]10)$ ]]; then printf '#%sn' "$(date -d@"$BASH_REMATCH[1]")"; else printf '%sn' "$i"; fi; done <file.txt
#Wed Aug 24 20:09:55 BDT 2016
ll /data/holding/email
#Wed Aug 24 20:11:46 BDT 2016
cat /etc/rsyslog.conf
#Wed Aug 24 20:13:58 BDT 2016
ll /data/holding/web
Nice....that prints the converted date to screen, now how do I get that command to replace the entries in the file?
– machinist
Aug 30 '16 at 8:01
@machinist Check my edits..
– heemayl
Aug 30 '16 at 8:17
1
If you are using a recent version ofbash
,printf
can do the conversion itself:printf '#%(%F %H)Tn' "$BASH_REMATCH[1]"
.
– chepner
Aug 30 '16 at 19:54
add a comment |
up vote
6
down vote
accepted
up vote
6
down vote
accepted
Assuming consistent file format, with bash
you can read the file line by line, test if it's in given format and then do the conversion:
while IFS= read -r i; do [[ $i =~ ^#([0-9]10)$ ]] &&
date -d@"$BASH_REMATCH[1]"; done <file.txt
BASH_REMATCH
is an array whose first element is the first captured group in Regex matching, =~
, in this case the epoch.
If you want to keep the file structure:
while IFS= read -r i; do if [[ $i =~ ^#([0-9]10)$ ]]; then printf '#%sn'
"$(date -d@"$BASH_REMATCH[1]")"; else printf '%sn' "$i"; fi; done <file.txt
this will output the modified contents to STDOUT, to save it in a file e.g. out.txt
:
while ...; do ...; done >out.txt
Now if you wish, you can replace the original file:
mv out.txt file.txt
Example:
$ cat file.txt
#1472047795
ll /data/holding/email
#1472047906
cat /etc/rsyslog.conf
#1472048038
ll /data/holding/web
$ while IFS= read -r i; do [[ $i =~ ^#([0-9]10)$ ]] && date -d@"$BASH_REMATCH[1]"; done <file.txt
Wed Aug 24 20:09:55 BDT 2016
Wed Aug 24 20:11:46 BDT 2016
Wed Aug 24 20:13:58 BDT 2016
$ while IFS= read -r i; do if [[ $i =~ ^#([0-9]10)$ ]]; then printf '#%sn' "$(date -d@"$BASH_REMATCH[1]")"; else printf '%sn' "$i"; fi; done <file.txt
#Wed Aug 24 20:09:55 BDT 2016
ll /data/holding/email
#Wed Aug 24 20:11:46 BDT 2016
cat /etc/rsyslog.conf
#Wed Aug 24 20:13:58 BDT 2016
ll /data/holding/web
Assuming consistent file format, with bash
you can read the file line by line, test if it's in given format and then do the conversion:
while IFS= read -r i; do [[ $i =~ ^#([0-9]10)$ ]] &&
date -d@"$BASH_REMATCH[1]"; done <file.txt
BASH_REMATCH
is an array whose first element is the first captured group in Regex matching, =~
, in this case the epoch.
If you want to keep the file structure:
while IFS= read -r i; do if [[ $i =~ ^#([0-9]10)$ ]]; then printf '#%sn'
"$(date -d@"$BASH_REMATCH[1]")"; else printf '%sn' "$i"; fi; done <file.txt
this will output the modified contents to STDOUT, to save it in a file e.g. out.txt
:
while ...; do ...; done >out.txt
Now if you wish, you can replace the original file:
mv out.txt file.txt
Example:
$ cat file.txt
#1472047795
ll /data/holding/email
#1472047906
cat /etc/rsyslog.conf
#1472048038
ll /data/holding/web
$ while IFS= read -r i; do [[ $i =~ ^#([0-9]10)$ ]] && date -d@"$BASH_REMATCH[1]"; done <file.txt
Wed Aug 24 20:09:55 BDT 2016
Wed Aug 24 20:11:46 BDT 2016
Wed Aug 24 20:13:58 BDT 2016
$ while IFS= read -r i; do if [[ $i =~ ^#([0-9]10)$ ]]; then printf '#%sn' "$(date -d@"$BASH_REMATCH[1]")"; else printf '%sn' "$i"; fi; done <file.txt
#Wed Aug 24 20:09:55 BDT 2016
ll /data/holding/email
#Wed Aug 24 20:11:46 BDT 2016
cat /etc/rsyslog.conf
#Wed Aug 24 20:13:58 BDT 2016
ll /data/holding/web
edited Aug 30 '16 at 8:17
answered Aug 30 '16 at 7:53
heemayl
34k370100
34k370100
Nice....that prints the converted date to screen, now how do I get that command to replace the entries in the file?
– machinist
Aug 30 '16 at 8:01
@machinist Check my edits..
– heemayl
Aug 30 '16 at 8:17
1
If you are using a recent version ofbash
,printf
can do the conversion itself:printf '#%(%F %H)Tn' "$BASH_REMATCH[1]"
.
– chepner
Aug 30 '16 at 19:54
add a comment |
Nice....that prints the converted date to screen, now how do I get that command to replace the entries in the file?
– machinist
Aug 30 '16 at 8:01
@machinist Check my edits..
– heemayl
Aug 30 '16 at 8:17
1
If you are using a recent version ofbash
,printf
can do the conversion itself:printf '#%(%F %H)Tn' "$BASH_REMATCH[1]"
.
– chepner
Aug 30 '16 at 19:54
Nice....that prints the converted date to screen, now how do I get that command to replace the entries in the file?
– machinist
Aug 30 '16 at 8:01
Nice....that prints the converted date to screen, now how do I get that command to replace the entries in the file?
– machinist
Aug 30 '16 at 8:01
@machinist Check my edits..
– heemayl
Aug 30 '16 at 8:17
@machinist Check my edits..
– heemayl
Aug 30 '16 at 8:17
1
1
If you are using a recent version of
bash
, printf
can do the conversion itself: printf '#%(%F %H)Tn' "$BASH_REMATCH[1]"
.– chepner
Aug 30 '16 at 19:54
If you are using a recent version of
bash
, printf
can do the conversion itself: printf '#%(%F %H)Tn' "$BASH_REMATCH[1]"
.– chepner
Aug 30 '16 at 19:54
add a comment |
up vote
14
down vote
While it's possible with GNU sed
with things like:
sed -E 's/^#([0-9]+).*$/date -d @1/e'
That would be terribly inefficient (and is easy to introduce arbitrary command injection vulnerabilities1) as that would mean running one shell and one date
command for each #xxxx
line, virtually as bad as a shell while read
loop. Here, it would be better to use things like perl
or gawk
, that is text processing utilities that have date conversion capabilities built-in:
perl -MPOSIX -pe 's/^#(d+).*/ctime $1/se'
Or:
gawk '/^#/$0 = strftime("%c", substr($0, 2));1'
1 If we had written ^#([0-9]).*
instead of ^#([0-9]).*$
(as I did in an earlier version of this answer), then in multi-byte locales like UTF-8 ones (the norm nowadays), with an input like #1472047795<0x80>;reboot
, where that <0x80>
is the byte value 0x80 which does not form a valid character, that s
command would have ended up running date -d@1472047795<0x80>; reboot
for instance. While with the extra $
, those lines would not be substituted. An alternative approach would be: s/^#([0-9])/date -d @1 #/e
, that is leave the part after the #xxx
date as a shell comment
What about using just a single instance ofdate -f
to do all the conversions in a stream-wise manner?
– Digital Trauma
Aug 30 '16 at 20:58
The perl command seems to add a new line after ctime $1 and I can't find any way to remove it.
– Alex Harvey
Jun 23 '17 at 7:47
1
@Alex. Right. See edit. Adding thes
flag makes so that.*
also includes the newline on input. You can also usestrftime "%c", localtime $1
.
– Stéphane Chazelas
Jun 23 '17 at 14:02
@StéphaneChazelas thanks so much. It's a great answer.
– Alex Harvey
Jun 23 '17 at 14:04
add a comment |
up vote
14
down vote
While it's possible with GNU sed
with things like:
sed -E 's/^#([0-9]+).*$/date -d @1/e'
That would be terribly inefficient (and is easy to introduce arbitrary command injection vulnerabilities1) as that would mean running one shell and one date
command for each #xxxx
line, virtually as bad as a shell while read
loop. Here, it would be better to use things like perl
or gawk
, that is text processing utilities that have date conversion capabilities built-in:
perl -MPOSIX -pe 's/^#(d+).*/ctime $1/se'
Or:
gawk '/^#/$0 = strftime("%c", substr($0, 2));1'
1 If we had written ^#([0-9]).*
instead of ^#([0-9]).*$
(as I did in an earlier version of this answer), then in multi-byte locales like UTF-8 ones (the norm nowadays), with an input like #1472047795<0x80>;reboot
, where that <0x80>
is the byte value 0x80 which does not form a valid character, that s
command would have ended up running date -d@1472047795<0x80>; reboot
for instance. While with the extra $
, those lines would not be substituted. An alternative approach would be: s/^#([0-9])/date -d @1 #/e
, that is leave the part after the #xxx
date as a shell comment
What about using just a single instance ofdate -f
to do all the conversions in a stream-wise manner?
– Digital Trauma
Aug 30 '16 at 20:58
The perl command seems to add a new line after ctime $1 and I can't find any way to remove it.
– Alex Harvey
Jun 23 '17 at 7:47
1
@Alex. Right. See edit. Adding thes
flag makes so that.*
also includes the newline on input. You can also usestrftime "%c", localtime $1
.
– Stéphane Chazelas
Jun 23 '17 at 14:02
@StéphaneChazelas thanks so much. It's a great answer.
– Alex Harvey
Jun 23 '17 at 14:04
add a comment |
up vote
14
down vote
up vote
14
down vote
While it's possible with GNU sed
with things like:
sed -E 's/^#([0-9]+).*$/date -d @1/e'
That would be terribly inefficient (and is easy to introduce arbitrary command injection vulnerabilities1) as that would mean running one shell and one date
command for each #xxxx
line, virtually as bad as a shell while read
loop. Here, it would be better to use things like perl
or gawk
, that is text processing utilities that have date conversion capabilities built-in:
perl -MPOSIX -pe 's/^#(d+).*/ctime $1/se'
Or:
gawk '/^#/$0 = strftime("%c", substr($0, 2));1'
1 If we had written ^#([0-9]).*
instead of ^#([0-9]).*$
(as I did in an earlier version of this answer), then in multi-byte locales like UTF-8 ones (the norm nowadays), with an input like #1472047795<0x80>;reboot
, where that <0x80>
is the byte value 0x80 which does not form a valid character, that s
command would have ended up running date -d@1472047795<0x80>; reboot
for instance. While with the extra $
, those lines would not be substituted. An alternative approach would be: s/^#([0-9])/date -d @1 #/e
, that is leave the part after the #xxx
date as a shell comment
While it's possible with GNU sed
with things like:
sed -E 's/^#([0-9]+).*$/date -d @1/e'
That would be terribly inefficient (and is easy to introduce arbitrary command injection vulnerabilities1) as that would mean running one shell and one date
command for each #xxxx
line, virtually as bad as a shell while read
loop. Here, it would be better to use things like perl
or gawk
, that is text processing utilities that have date conversion capabilities built-in:
perl -MPOSIX -pe 's/^#(d+).*/ctime $1/se'
Or:
gawk '/^#/$0 = strftime("%c", substr($0, 2));1'
1 If we had written ^#([0-9]).*
instead of ^#([0-9]).*$
(as I did in an earlier version of this answer), then in multi-byte locales like UTF-8 ones (the norm nowadays), with an input like #1472047795<0x80>;reboot
, where that <0x80>
is the byte value 0x80 which does not form a valid character, that s
command would have ended up running date -d@1472047795<0x80>; reboot
for instance. While with the extra $
, those lines would not be substituted. An alternative approach would be: s/^#([0-9])/date -d @1 #/e
, that is leave the part after the #xxx
date as a shell comment
edited Jun 23 '17 at 14:00
answered Aug 30 '16 at 8:33
Stéphane Chazelas
294k54552894
294k54552894
What about using just a single instance ofdate -f
to do all the conversions in a stream-wise manner?
– Digital Trauma
Aug 30 '16 at 20:58
The perl command seems to add a new line after ctime $1 and I can't find any way to remove it.
– Alex Harvey
Jun 23 '17 at 7:47
1
@Alex. Right. See edit. Adding thes
flag makes so that.*
also includes the newline on input. You can also usestrftime "%c", localtime $1
.
– Stéphane Chazelas
Jun 23 '17 at 14:02
@StéphaneChazelas thanks so much. It's a great answer.
– Alex Harvey
Jun 23 '17 at 14:04
add a comment |
What about using just a single instance ofdate -f
to do all the conversions in a stream-wise manner?
– Digital Trauma
Aug 30 '16 at 20:58
The perl command seems to add a new line after ctime $1 and I can't find any way to remove it.
– Alex Harvey
Jun 23 '17 at 7:47
1
@Alex. Right. See edit. Adding thes
flag makes so that.*
also includes the newline on input. You can also usestrftime "%c", localtime $1
.
– Stéphane Chazelas
Jun 23 '17 at 14:02
@StéphaneChazelas thanks so much. It's a great answer.
– Alex Harvey
Jun 23 '17 at 14:04
What about using just a single instance of
date -f
to do all the conversions in a stream-wise manner?– Digital Trauma
Aug 30 '16 at 20:58
What about using just a single instance of
date -f
to do all the conversions in a stream-wise manner?– Digital Trauma
Aug 30 '16 at 20:58
The perl command seems to add a new line after ctime $1 and I can't find any way to remove it.
– Alex Harvey
Jun 23 '17 at 7:47
The perl command seems to add a new line after ctime $1 and I can't find any way to remove it.
– Alex Harvey
Jun 23 '17 at 7:47
1
1
@Alex. Right. See edit. Adding the
s
flag makes so that .*
also includes the newline on input. You can also use strftime "%c", localtime $1
.– Stéphane Chazelas
Jun 23 '17 at 14:02
@Alex. Right. See edit. Adding the
s
flag makes so that .*
also includes the newline on input. You can also use strftime "%c", localtime $1
.– Stéphane Chazelas
Jun 23 '17 at 14:02
@StéphaneChazelas thanks so much. It's a great answer.
– Alex Harvey
Jun 23 '17 at 14:04
@StéphaneChazelas thanks so much. It's a great answer.
– Alex Harvey
Jun 23 '17 at 14:04
add a comment |
up vote
2
down vote
All the other answers spawn a new date
process for every epoch date that needs to be converted. This could potentially add performance overhead if your input is large.
However GNU date has a handy -f
option that allows a single process instance of date
to continuously read input dates without the need for a new fork. So we can use sed
, paste
and date
in this manner such that each one only gets spawned once (2x for sed
) regardless of how large the input is:
$ paste -d 'n' <( sed '2~2d;y/#/@/' epoch.txt | date -f - ) <( sed '1~2d' epoch.txt )
Wed Aug 24 07:09:55 PDT 2016
ll /data/holding/email
Wed Aug 24 07:11:46 PDT 2016
cat /etc/rsyslog.conf
Wed Aug 24 07:13:58 PDT 2016
ll /data/holding/web
$
- The two
sed
commands respectively basically delete even and odd lines of the input; the first one also replaces#
with@
to give the correct epoch timestamp format. - The first
sed
output is then piped todate -f
which does the required date conversion, for every line of input that it receives. - These two streams are then interlaced into the single required output using
paste
. The<( )
constructs are bash process substitutions that effectively trick paste into thinking it is reading from given filenames when it is in fact reading the output piped from the command inside.-d 'n'
tellspaste
to separate odd and even output lines with a newline. You could change (or remove) this if for example you want the timestamp on the same line as the other text.
Note that there are several GNUisms and Bashisms in this command. This is not Posix-compliant and should not be expected to be portable outside of the GNU/Linux world. For example date -f
does something else on OSXes BSD date
variant.
date -d
(from the question) is also non-portable... (On FreeBSD it will try to mess with DST settings, on Solaris it will give an error...) The question does not specify an OS though...
– Gert van den Berg
Jan 13 '17 at 8:14
@GertvandenBerg yes, this is addressed in the last paragraph of this answer.
– Digital Trauma
Jan 13 '17 at 15:56
I mean that the asker's sample also has portability issues... (They should probably have tagged an OS...)
– Gert van den Berg
Jan 14 '17 at 16:06
add a comment |
up vote
2
down vote
All the other answers spawn a new date
process for every epoch date that needs to be converted. This could potentially add performance overhead if your input is large.
However GNU date has a handy -f
option that allows a single process instance of date
to continuously read input dates without the need for a new fork. So we can use sed
, paste
and date
in this manner such that each one only gets spawned once (2x for sed
) regardless of how large the input is:
$ paste -d 'n' <( sed '2~2d;y/#/@/' epoch.txt | date -f - ) <( sed '1~2d' epoch.txt )
Wed Aug 24 07:09:55 PDT 2016
ll /data/holding/email
Wed Aug 24 07:11:46 PDT 2016
cat /etc/rsyslog.conf
Wed Aug 24 07:13:58 PDT 2016
ll /data/holding/web
$
- The two
sed
commands respectively basically delete even and odd lines of the input; the first one also replaces#
with@
to give the correct epoch timestamp format. - The first
sed
output is then piped todate -f
which does the required date conversion, for every line of input that it receives. - These two streams are then interlaced into the single required output using
paste
. The<( )
constructs are bash process substitutions that effectively trick paste into thinking it is reading from given filenames when it is in fact reading the output piped from the command inside.-d 'n'
tellspaste
to separate odd and even output lines with a newline. You could change (or remove) this if for example you want the timestamp on the same line as the other text.
Note that there are several GNUisms and Bashisms in this command. This is not Posix-compliant and should not be expected to be portable outside of the GNU/Linux world. For example date -f
does something else on OSXes BSD date
variant.
date -d
(from the question) is also non-portable... (On FreeBSD it will try to mess with DST settings, on Solaris it will give an error...) The question does not specify an OS though...
– Gert van den Berg
Jan 13 '17 at 8:14
@GertvandenBerg yes, this is addressed in the last paragraph of this answer.
– Digital Trauma
Jan 13 '17 at 15:56
I mean that the asker's sample also has portability issues... (They should probably have tagged an OS...)
– Gert van den Berg
Jan 14 '17 at 16:06
add a comment |
up vote
2
down vote
up vote
2
down vote
All the other answers spawn a new date
process for every epoch date that needs to be converted. This could potentially add performance overhead if your input is large.
However GNU date has a handy -f
option that allows a single process instance of date
to continuously read input dates without the need for a new fork. So we can use sed
, paste
and date
in this manner such that each one only gets spawned once (2x for sed
) regardless of how large the input is:
$ paste -d 'n' <( sed '2~2d;y/#/@/' epoch.txt | date -f - ) <( sed '1~2d' epoch.txt )
Wed Aug 24 07:09:55 PDT 2016
ll /data/holding/email
Wed Aug 24 07:11:46 PDT 2016
cat /etc/rsyslog.conf
Wed Aug 24 07:13:58 PDT 2016
ll /data/holding/web
$
- The two
sed
commands respectively basically delete even and odd lines of the input; the first one also replaces#
with@
to give the correct epoch timestamp format. - The first
sed
output is then piped todate -f
which does the required date conversion, for every line of input that it receives. - These two streams are then interlaced into the single required output using
paste
. The<( )
constructs are bash process substitutions that effectively trick paste into thinking it is reading from given filenames when it is in fact reading the output piped from the command inside.-d 'n'
tellspaste
to separate odd and even output lines with a newline. You could change (or remove) this if for example you want the timestamp on the same line as the other text.
Note that there are several GNUisms and Bashisms in this command. This is not Posix-compliant and should not be expected to be portable outside of the GNU/Linux world. For example date -f
does something else on OSXes BSD date
variant.
All the other answers spawn a new date
process for every epoch date that needs to be converted. This could potentially add performance overhead if your input is large.
However GNU date has a handy -f
option that allows a single process instance of date
to continuously read input dates without the need for a new fork. So we can use sed
, paste
and date
in this manner such that each one only gets spawned once (2x for sed
) regardless of how large the input is:
$ paste -d 'n' <( sed '2~2d;y/#/@/' epoch.txt | date -f - ) <( sed '1~2d' epoch.txt )
Wed Aug 24 07:09:55 PDT 2016
ll /data/holding/email
Wed Aug 24 07:11:46 PDT 2016
cat /etc/rsyslog.conf
Wed Aug 24 07:13:58 PDT 2016
ll /data/holding/web
$
- The two
sed
commands respectively basically delete even and odd lines of the input; the first one also replaces#
with@
to give the correct epoch timestamp format. - The first
sed
output is then piped todate -f
which does the required date conversion, for every line of input that it receives. - These two streams are then interlaced into the single required output using
paste
. The<( )
constructs are bash process substitutions that effectively trick paste into thinking it is reading from given filenames when it is in fact reading the output piped from the command inside.-d 'n'
tellspaste
to separate odd and even output lines with a newline. You could change (or remove) this if for example you want the timestamp on the same line as the other text.
Note that there are several GNUisms and Bashisms in this command. This is not Posix-compliant and should not be expected to be portable outside of the GNU/Linux world. For example date -f
does something else on OSXes BSD date
variant.
answered Aug 30 '16 at 20:49
Digital Trauma
5,75211528
5,75211528
date -d
(from the question) is also non-portable... (On FreeBSD it will try to mess with DST settings, on Solaris it will give an error...) The question does not specify an OS though...
– Gert van den Berg
Jan 13 '17 at 8:14
@GertvandenBerg yes, this is addressed in the last paragraph of this answer.
– Digital Trauma
Jan 13 '17 at 15:56
I mean that the asker's sample also has portability issues... (They should probably have tagged an OS...)
– Gert van den Berg
Jan 14 '17 at 16:06
add a comment |
date -d
(from the question) is also non-portable... (On FreeBSD it will try to mess with DST settings, on Solaris it will give an error...) The question does not specify an OS though...
– Gert van den Berg
Jan 13 '17 at 8:14
@GertvandenBerg yes, this is addressed in the last paragraph of this answer.
– Digital Trauma
Jan 13 '17 at 15:56
I mean that the asker's sample also has portability issues... (They should probably have tagged an OS...)
– Gert van den Berg
Jan 14 '17 at 16:06
date -d
(from the question) is also non-portable... (On FreeBSD it will try to mess with DST settings, on Solaris it will give an error...) The question does not specify an OS though...– Gert van den Berg
Jan 13 '17 at 8:14
date -d
(from the question) is also non-portable... (On FreeBSD it will try to mess with DST settings, on Solaris it will give an error...) The question does not specify an OS though...– Gert van den Berg
Jan 13 '17 at 8:14
@GertvandenBerg yes, this is addressed in the last paragraph of this answer.
– Digital Trauma
Jan 13 '17 at 15:56
@GertvandenBerg yes, this is addressed in the last paragraph of this answer.
– Digital Trauma
Jan 13 '17 at 15:56
I mean that the asker's sample also has portability issues... (They should probably have tagged an OS...)
– Gert van den Berg
Jan 14 '17 at 16:06
I mean that the asker's sample also has portability issues... (They should probably have tagged an OS...)
– Gert van den Berg
Jan 14 '17 at 16:06
add a comment |
up vote
1
down vote
Assuming the date format you have in your post is what you want, the following regex should fit your needs.
sed -E 's/#(1[0-9]9)(.*)/echo 1 $(date -d @1)/e' log.file
Be mindful of the fact this will only replace one epoch per line.
I'm getting the following error with that command:sed: -e expression #1, char 48: invalid reference 3 on 's' command's RHS
– machinist
Aug 30 '16 at 7:53
1
My mistake, edited the post.
– Hatclock
Aug 30 '16 at 8:25
add a comment |
up vote
1
down vote
Assuming the date format you have in your post is what you want, the following regex should fit your needs.
sed -E 's/#(1[0-9]9)(.*)/echo 1 $(date -d @1)/e' log.file
Be mindful of the fact this will only replace one epoch per line.
I'm getting the following error with that command:sed: -e expression #1, char 48: invalid reference 3 on 's' command's RHS
– machinist
Aug 30 '16 at 7:53
1
My mistake, edited the post.
– Hatclock
Aug 30 '16 at 8:25
add a comment |
up vote
1
down vote
up vote
1
down vote
Assuming the date format you have in your post is what you want, the following regex should fit your needs.
sed -E 's/#(1[0-9]9)(.*)/echo 1 $(date -d @1)/e' log.file
Be mindful of the fact this will only replace one epoch per line.
Assuming the date format you have in your post is what you want, the following regex should fit your needs.
sed -E 's/#(1[0-9]9)(.*)/echo 1 $(date -d @1)/e' log.file
Be mindful of the fact this will only replace one epoch per line.
edited Aug 30 '16 at 8:25
answered Aug 30 '16 at 7:48
Hatclock
656
656
I'm getting the following error with that command:sed: -e expression #1, char 48: invalid reference 3 on 's' command's RHS
– machinist
Aug 30 '16 at 7:53
1
My mistake, edited the post.
– Hatclock
Aug 30 '16 at 8:25
add a comment |
I'm getting the following error with that command:sed: -e expression #1, char 48: invalid reference 3 on 's' command's RHS
– machinist
Aug 30 '16 at 7:53
1
My mistake, edited the post.
– Hatclock
Aug 30 '16 at 8:25
I'm getting the following error with that command:
sed: -e expression #1, char 48: invalid reference 3 on 's' command's RHS
– machinist
Aug 30 '16 at 7:53
I'm getting the following error with that command:
sed: -e expression #1, char 48: invalid reference 3 on 's' command's RHS
– machinist
Aug 30 '16 at 7:53
1
1
My mistake, edited the post.
– Hatclock
Aug 30 '16 at 8:25
My mistake, edited the post.
– Hatclock
Aug 30 '16 at 8:25
add a comment |
up vote
0
down vote
using sed :
sed -r 's/#([0-9]*)/echo $(date -d @1)/eg' test.txt
output :
ر أغس 24 16:09:55 EET 2016
ll /data/holding/email
ر أغس 24 16:11:46 EET 2016
cat /etc/rsyslog.conf
ر أغس 24 16:13:58 EET 2016
ll /data/holding/web
as my locale language is Arabic :)
add a comment |
up vote
0
down vote
using sed :
sed -r 's/#([0-9]*)/echo $(date -d @1)/eg' test.txt
output :
ر أغس 24 16:09:55 EET 2016
ll /data/holding/email
ر أغس 24 16:11:46 EET 2016
cat /etc/rsyslog.conf
ر أغس 24 16:13:58 EET 2016
ll /data/holding/web
as my locale language is Arabic :)
add a comment |
up vote
0
down vote
up vote
0
down vote
using sed :
sed -r 's/#([0-9]*)/echo $(date -d @1)/eg' test.txt
output :
ر أغس 24 16:09:55 EET 2016
ll /data/holding/email
ر أغس 24 16:11:46 EET 2016
cat /etc/rsyslog.conf
ر أغس 24 16:13:58 EET 2016
ll /data/holding/web
as my locale language is Arabic :)
using sed :
sed -r 's/#([0-9]*)/echo $(date -d @1)/eg' test.txt
output :
ر أغس 24 16:09:55 EET 2016
ll /data/holding/email
ر أغس 24 16:11:46 EET 2016
cat /etc/rsyslog.conf
ر أغس 24 16:13:58 EET 2016
ll /data/holding/web
as my locale language is Arabic :)
edited Aug 30 '16 at 8:32
answered Aug 30 '16 at 8:07
hassan
17115
17115
add a comment |
add a comment |
up vote
0
down vote
My solution how to do that in a pipeline
cat test.txt | sed 's/^/echo "/; s/([0-9]10)/`date -d @1`/; s/$/"/' | bash
add a comment |
up vote
0
down vote
My solution how to do that in a pipeline
cat test.txt | sed 's/^/echo "/; s/([0-9]10)/`date -d @1`/; s/$/"/' | bash
add a comment |
up vote
0
down vote
up vote
0
down vote
My solution how to do that in a pipeline
cat test.txt | sed 's/^/echo "/; s/([0-9]10)/`date -d @1`/; s/$/"/' | bash
My solution how to do that in a pipeline
cat test.txt | sed 's/^/echo "/; s/([0-9]10)/`date -d @1`/; s/$/"/' | bash
answered Jan 13 '17 at 7:50
kayn
1312
1312
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f306576%2fhow-to-replace-epoch-timestamps-in-a-file-with-other-formats%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
For future reference (assuming this is a Bash history file; it looks like one), look to the
HISTTIMEFORMAT
shell variable to control the format at time of writing.– Toby Speight
Aug 30 '16 at 14:44
@Toby the value of HISTTIMEFORMAT is used when displaying (to stdout), but only its status (set to anything even null vs unset) matters when writing HISTFILE.
– dave_thompson_085
Sep 3 '16 at 10:43
Thanks @dave, I didn't know that (not being a user of history times myself).
– Toby Speight
Sep 5 '16 at 9:00
date -d
is not portable to say Solaris... I'm assuming this is on a system with mostly GNU tools? (GNU AWK / Perl tend to be the more portable methods to deal with date conversions).gawk ' if ($0 ~ /^#[0-9]*$/) print strftime("%c",substr($0,2)); else print ' < file
(strftime
seems non-portable...)– Gert van den Berg
Jan 13 '17 at 8:07