I have a text stream that looks like this:
----------------------------------------
s123456789_9780
heartbeat:test @ 1344280205000000: '0'
heartbeat:test @ 1344272490000000: '0'
Those long numbers are timestamps in microseconds. I would like to run this output through some sort of pipe that will change those timestamps to a more human-understandable date.
I have a date command that can do that, given just the timestamp (with the following colon):
$ date --date=@$(echo 1344272490000000: | sed 's/.......$//') +%Y/%d/%m-%H:%M:%S
2012/06/08-10:01:30
I would like to end up with something like this:
----------------------------------------
s123456789_9780
heartbeat:test @ 2012/06/08-12:10:05: '0'
heartbeat:test @ 2012/06/08-10:01:30: '0'
I don't think sed
will allow me to match the timestamp and replace it with the value of calling a shell function on it (although I'd love to be shown wrong). Perhaps awk
can do it? I'm not very familiar with awk
.
The other part that seems tricky to me is letting the lines that don't match through without modification.
I could of course write a Python program that would do this, but I'd rather keep this in shell if possible (this is generated inside a shell script, and I'd rather not have dependencies on outside files).
Bash with a little sed, preserving the whitespace of the input:
while read -r; do
parts=($REPLY)
if [[ ${parts[0]} == "heartbeat:test" ]]; then
dateStr=$(date --date=@${parts[2]%000000:} +%Y/%d/%m-%H:%M:%S)
REPLY=$(echo "$REPLY" | sed "s#[0-9]\+000000:#$dateStr#")
fi
printf "%s\n" "$REPLY"
done
This might work for you (GNU sed):
sed '/@ /!b;s//&\n/;h;s/.*\n//;s#\(.\{10\}\)[^:]*\(:.*\)#date --date=@\1 +%Y/%d/%m-%H:%M:%S"\2"#e;H;g;s/\n.*\n//' file
Explanation:
/@ /!b
bail out and just print any lines that don't contain an @
followed by a space
s//&\n/
insert a newline after the above pattern
h
copy the pattern space (PS) to the hold space (HS)
s/.*\n//
delete upto and including the @
followed by a space
s#\(.\{10\}\)[^:]*\(:.*\)#date --date=@\1 +%Y/%d/%m-%H:%M:%S"\2"#e
from whats remaining in the PS, make a back reference of the first 10 characters and from the :
to the end of the string. Have these passed in to the date
command and evaluate the result into the PS
H
append the PS to the HS inserting a newline at the same time
g
copy the HS into the PS
s/\n.*\n//
remove the original section of the string
How about:
while read s1 at tm s2
do
tm=${tm%000000:}
echo $s1 $at $(date --date @$tm +%Y/%d/%m-%H:%M:%S)
done < yourfile
I would also like to see a sed
solution, but it is a bit beyond my sed
-fu. As awk
supports strftime
it is fairly straight forward here:
awk '
/^ *heartbeat/ {
gsub(".{7}$", "", $3)
$3 = strftime("%Y/%d/%m-%T", $3)
print " ", $1, $3
}
$0 !~ /heartbeat/' file
Output:
s123456789_9780
heartbeat:test 2012/06/08-21:10:05
heartbeat:test 2012/06/08-19:01:30
$3
is the microsecond field. gsub
converts the timestamp to seconds.
The $0 !~
makes sure non-heartbeat lines are printed ({ print }
implicitly is the default block).
This does it mostly within bash
using your date
command:
#!/bin/bash
IFS=$
while read a ; do
case "$a" in
*" @ "[0-9]*) pre=${a% @ *}
a=${a#$pre @ }
post=${a##*:}
a=${a%??????:$post}
echo "$pre$(date --date=@$a +%Y/%d/%m-%H:%M:%S):$post"
;;
*) echo "$a" ;;
esac
done <<.
----------------------------------------
s123456789_9780
heartbeat:test @ 1344280205000000: '0'
heartbeat:test @ 1344272490000000: '0'
.