Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have a file:

pablo tty8 Thu Nov 1 12:51:21 2012 still logged in 
(unknown tty8 Thu Nov 1 12:50:57 2012 - Thu Nov 1 12:51:21 2012 (00:00) 
pablo tty2 Thu Nov 1 12:50:39 2012 still logged in 
pablo tty7 Thu Nov 1 12:49:45 2012 - Thu Nov 1 12:50:56 2012 (00:01) 
(unknown tty7 Thu Nov 1 12:34:32 2012 - Thu Nov 1 12:49:45 2012 (00:15)

I want to replace the file in the above date for a second. I want to print:

pablo tty8 1351770681 still logged in 
(unknown tty8 1351770657 - 1351770681 (00:00) 
pablo tty2 1351770639 still logged in 
pablo tty7 1351770585 - 1351770656 (00:01) 
(unknown tty7 1351769672 - 1351770585 (00:15)

I tried this command:

gawk --posix 'function my()
{"date -d \047"$0"\047 +%s" | getline b; 
gsub( /[A-Za-z]{3} [A-Za-z]{3} [0-9] ([0-9]{2}:){2}[0-9]{2} [0-9]{4}/,b );print}
{ my() }' file

The above command does not work:

$ gawk --posix 'function my()
> {"date -d \047"$0"\047 +%s" | getline b; 
> gsub( /[A-Za-z]{3} [A-Za-z]{3} [0-9] ([0-9]{2}:){2}[0-9]{2} [0-9]{4}/,b ); print}
> { my() }' ta
date: błędna data: `pablo tty8 Thu Nov 1 12:51:21 2012 still logged in '
pablo tty8  still logged in 
(unknown tty8 1351897200 - 1351897200 (00:00) 
date: błędna data: `pablo tty2 Thu Nov 1 12:50:39 2012 still logged in '
pablo tty2 1351897200 still logged in 
date: błędna data: `pablo tty7 Thu Nov 1 12:49:45 2012 - Thu Nov 1 12:50:56 2012 (00:01) '
pablo tty7 1351897200 - 1351897200 (00:01) 
(unknown tty7 1351897200 - 1351897200 (00:15)

How to improve the above command?

share|improve this question
1  
Please don't double post: stackoverflow.com/q/13208379/7552 –  glenn jackman Nov 3 '12 at 20:06
    
@glenn jackman, Sorry for the duplicate topic in another forum. –  nowy1 Nov 4 '12 at 9:10
    
Just wondering what are the norms on this. As an asker, the OP would definitely be tempted to post to multiple platforms in the hope of getting more attention and hence a better chance of resolving his/her question. –  Ketan Nov 12 '12 at 15:03

5 Answers 5

up vote 6 down vote accepted

To do it your way, that would have to be something like:

POSIXLY_CORRECT=1 awk '
  {
    n = ""; r = $0
    while (match(r, /[[:alpha:]]{3} [[:alpha:]]{3} +[0-9]+ ([0-9]{2}:){2}[0-9]{2} [0-9]{4}/)) {
      c = "date -d\"" substr(r,RSTART,RLENGTH) "\" +%s"
      c | getline b
      close(c)
      n = n substr(r,1,RSTART-1) b
      r =  substr(r,RSTART+RLENGTH)
    }
    print n r
  }'
share|improve this answer
    
Thank you very much for your help. –  nowy1 Nov 4 '12 at 6:35
    
I want to be able to awk like you :) –  delh Nov 4 '12 at 17:39

Here's an alternate approach (using mktime):

#!/bin/awk -f
{
    split($6,A,":");
    S1=sprintf("%d %d %d %d %d %d",$7,$4,$5,A[1],A[2],A[3])
    T1=mktime(S1)
    if ($8=="-") {
        split($12,A,":");
        S2=sprintf("%d %d %d %d %d %d",$13,$10,$11,A[1],A[2],A[3])
        T2=mktime(S2)
        print $1,$2,T1,$8,T2,$14
    }
    else {
        print $1,$2,T1,$8,$9,$10
    }
}
share|improve this answer
    
Thank you for a good solution. –  nowy1 Nov 4 '12 at 6:29

You could do it like this with GNU sed:

convert_date.sed

: a
s/(([A-Za-z]{3} ){2}[0-9]{1,2} ([0-9]{2}:){2}[0-9]{2} [0-9]{4})(.*)/\n\4\n\1/
h
s/.*\n//
s/^/date -d "/
s/$/" +%s/e
G
s/([^\n]+)\n([^\n]+)\n([^\n]+)\n.*/\2\1\3/
/([A-Za-z]{3} ){2}[0-9]{1,2} ([0-9]{2}:){2}[0-9]{2} [0-9]{4}/ta

Run it like this:

sed -rf convert_date.sed infile

Output:

pablo tty8 1351770681 still logged in 
(unknown tty8 1351770657 - 1351770681 (00:00) 
pablo tty2 1351770639 still logged in 
pablo tty7 1351770585 - 1351770656 (00:01) 
(unknown tty7 1351769672 - 1351770585 (00:15)

Explanation

This may look a bit daunting at first, but the idea is not that complicated. This regular expression, ([A-Za-z]{3} ){2}[0-9]{1,2} ([0-9]{2}:){2}[0-9]{2} [0-9]{4}, which occurs in the first replace and the conditional at the end, matches the date type used in the input, it captures and isolates the date. The surrounding bits are stored in the hold space while date -d is run on the captured date. Finally, all the bits are collected in pattern space and reorganized into the correct order.

The conditional at the end repeats the process if any dates remain in pattern space.

share|improve this answer
    
Thank you for a good solution. I just have a question. Is your solution can also be used in awk? –  nowy1 Nov 3 '12 at 19:30
    
@nowy1: Not easily no. Several useful awk solutions have been suggested, maybe those will do. –  Thor Nov 3 '12 at 23:09
    
Thanks again for your help. –  nowy1 Nov 4 '12 at 6:30

With perl and its Date::Manip module:

perl -MDate::Manip -pe '
  s/\w{3} \w{3} +\d+ \d\d:\d\d:\d\d \d+/
  UnixDate ParseDate("$&"),"%s"/ge'
share|improve this answer
    
Thank you for your help. –  nowy1 Nov 4 '12 at 6:37

The Perl solution provided by Stephane requires a non-core Perl module. One could use the core module (since 5.10), Time::Piece, similarly:

#!/usr/bin/env perl
use strict;
use warnings;
use Time::Piece;
my $t = Time::Piece->new;
while (<>) {
    s{\w{3}\s(\w{3}\s\d{1,2}\s\d\d:\d\d:\d\d\s\d{4})}
        {$t=Time::Piece->strptime($1,"%b %d %H:%M:%S %Y");
        sprintf "%s",$t->epoch}ge;
    print;
}
share|improve this answer
    
Thank you for another good solution. –  nowy1 Nov 4 '12 at 16:33
1  
Or POSIX::mktime –  Stéphane Chazelas Nov 4 '12 at 17:16

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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