2

I have file with several lines. Each with a timestamp followed by text:

1534888050 some text
1534888051 some more text
1534888052 text

... and want to convert the timestamp to a readable format, while keeping the text. The result shall be:

Tue Aug 21 21:47:30 UTC 2018 some text
Tue Aug 21 21:47:31 UTC 2018 some more text
Tue Aug 21 21:47:32 UTC 2018 text

date -d@"${somedate}" converts the timestamp itself correctly.

Now I want the same with one filter / pipe command through sed or awk (or any other common command).

This does not work, but roughly explains what I am thinking of:

echo '1534888050 some text' | sed "s#^\([0-9]*\) .*#`date -d@\1`#g"

(The \1 is the matched timestamp)

Any way to achieve this in an elegant manner ?

| improve this question | | | | |
3

The dateutils package is great for such things, specifically the dconv tool. (When installed on Ubuntu systems the command is available as dateutils.dconv.)

Here is an illustration:

$ cat testfile.txt 
1534888050 some text
1534888051 some more text
1534888052 text
$ dateutils.dconv -i '%s' -f '%a %b %d %T %Y' -S <testfile.txt 
Tue Aug 21 21:47:30 2018 some text
Tue Aug 21 21:47:31 2018 some more text
Tue Aug 21 21:47:32 2018 text
$ dateutils.dconv -i '%s' -f '%F %T' -S <testfile.txt 
2018-08-21 21:47:30 some text
2018-08-21 21:47:31 some more text
2018-08-21 21:47:32 text
$ 

The -S flag is key; it tells the tool to operate in "sed mode" and include the non-date text in the output.

The -i flag specifies the input date format and the -f flag specifies the output date format.

| improve this answer | | | | |
2

If you have GNU awk (aka gawk) you can use its built-in strftime function to convert an epoch time to a format of your choice:

$ TZ=UTC gawk '{$1 = strftime("%a %b %d %H:%M:%S %Z %Y", $1)} 1' file
Tue Aug 21 21:47:30 UTC 2018 some text
Tue Aug 21 21:47:31 UTC 2018 some more text
Tue Aug 21 21:47:32 UTC 2018 text

You could also use perl with the POSIX module:

$ TZ=UTC perl -MPOSIX -alne '
    print join " ", strftime("%a %b %d %H:%M:%S %Z %Y", localtime shift @F), @F
  ' file
Tue Aug 21 21:47:30 UTC 2018 some text
Tue Aug 21 21:47:31 UTC 2018 some more text
Tue Aug 21 21:47:32 UTC 2018 text

Although the GNU implementation of sed does have an e modifier to allow execution of external commands, it operates on the whole pattern space - making it rather cumbersome to use in practice.

| improve this answer | | | | |
0

Using Awk:

{
  "date -d @" $1 | getline $1
}
1

Result:

Tue, Aug 21, 2018  4:47:30 PM some text
Tue, Aug 21, 2018  4:47:31 PM some more text
Tue, Aug 21, 2018  4:47:32 PM text
| improve this answer | | | | |
0

If sed is a must, use GNU sed with:

$ sed -e 'h;s/^\([^ ]*\) \(.*\)/date -ud @\1/;e' -e 'x;s//\2/;x;G;s/\n/ /' infile.txt
Tue Aug 21 21:47:30 UTC 2018 some text
Tue Aug 21 21:47:31 UTC 2018 some more text
Tue Aug 21 21:47:32 UTC 2018 text

Otherwise, the shell could do:

$ TZ=UTC0 bash -c 'while read a b; do printf '\''%(%c)T %s\n'\'' "$a" "$b"; done <infile'
Tue 21 Aug 2018 09:47:30 PM UTC some text
Tue 21 Aug 2018 09:47:31 PM UTC some more text
Tue 21 Aug 2018 09:47:32 PM UTC text

Or, better yet, use awk:

$ awk '{c="date -ud @" $1;c|getline $1;close(c)}1' infile.txt
Tue Aug 21 21:47:30 UTC 2018 some text
Tue Aug 21 21:47:31 UTC 2018 some more text
Tue Aug 21 21:47:32 UTC 2018 text
| improve this answer | | | | |
0

What you need to realize is that GNU sed replaces the whole of the pattern space with the result of comnand evaluation in the RHS of the s///e. So to accommodate that behavior, we make use of the echo + date commands combo as shown :

 sed -e  's/^\([1-9][0-9]*\)\(.*\)/echo "`date -d@\"\1\"`\2"/e'  input-file.txt

And with Perl, it is simple in that, what is matched on the LHS is only replaced on the RHS, as one would intuitively expect also:

  perl -pe 's/(\d+)/qx(date -d@"$1") =~ s|\n||r/e'  input-file.txt 
| improve this answer | | | | |
0

In awk you can use

awk '{ cmd = "date -d@" $1; cmd | getline date; close (cmd); $1 = date; print $0; }'

To restrict to numeric values, check for the pattern of only digits:

awk '$1 ~ /^[0-9]*$/ { cmd = "date -d@" $1; cmd | getline $1; close (cmd); print $0; }'
| improve this answer | | | | |
  • Which could be shortened to awk '{ cmd = "date -d@" $1; cmd | getline $1; close (cmd)} 1' – RudiC Aug 21 '18 at 23:34
  • Interesting. How would I ensure this is only applied on a ^[0-9]+ pattern? (There are lines that don't start with a timestamp that cause errors with this.) – kevcrumb Aug 26 '18 at 21:33
  • See the example. – RalfFriedl Aug 27 '18 at 5:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.