5

Consider this script

#!awk -f
BEGIN {
  "date" | getline foo
  print foo
}

It will print the current date, as expected. However if you put it in a loop

#!awk -f
BEGIN {
  while (1) {
    "date" | getline foo
    printf "%s\r", foo
  }
}

it just prints the same date repeatedly. I would like to capture external command in a loop using either getline or system

Zombo
  • 1
  • 62
  • 391
  • 407
  • Could you please clarify further what is your expectation when you say "I would like to capture external command in a loop using either getline or system" ? – toddlermenot Nov 28 '14 at 19:36
  • 1
    You're on dangerous ground with `getline` and using it incorrectly (see http://awk.info/?tip/getline). Please clarify if you are trying to run an external command once and capture every line of it's output or running an external command multiple times and capturing the first line of its output each time or something else and update your question to show sample input (e.g. the text you want to run getline on) and the expected output of the command as a whole given that input. – Ed Morton Nov 28 '14 at 21:10
  • Make that http://awk.freeshell.org/AllAboutGetline instead. – Ed Morton Aug 04 '17 at 17:43

2 Answers2

4

You need to close the process to get awk to spawn it again.

BEGIN {
  while (1) {
    "date" | getline foo
    printf "%s\n", foo
    close("date")
  }
}

I expect the subsequent reads in your original are actually failing on EOF and leaving foo unset.

$ awk 'BEGIN {
  while (1) {
    print "ret: " ("date" | getline foo)
    printf "%s\n", foo
  }
}'
ret: 1
Fri Nov 28 15:17:07 EST 2014
ret: 0
Fri Nov 28 15:17:07 EST 2014
Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
4
$ cat tst.awk
BEGIN {
    cmd = "date"
    while (!done) {
        if ( (cmd | getline foo) > 0 ) {
            print foo
            done = (++i == 5 ? 1 : 0)
        }
        else {
            done = 1
        }
        close(cmd)
    }
}
$ awk -f tst.awk
Fri, Nov 28, 2014  3:18:21 PM
Fri, Nov 28, 2014  3:18:21 PM
Fri, Nov 28, 2014  3:18:21 PM
Fri, Nov 28, 2014  3:18:21 PM
Fri, Nov 28, 2014  3:18:21 PM

or if you prefer (but this is a potentially infinite loop):

BEGIN {
    cmd = "date"
    while ( (cmd | getline foo) > 0 ) {
        print foo
        close(cmd)
    }
}

Here is a stopwatch in GNU awk:

$ cat tst.awk
/s/ { start = systime() }
/e/ { end = systime(); print "elapsed:", end - start, "secs\n" }
/x/ { exit }
$
$ awk -f tst.awk
s
e
elapsed: 2 secs

s
e
elapsed: 6 secs

x

Here's how to do what your bash script (https://superuser.com/a/694393) is doing:

$ cat tst.awk
BEGIN {
    cmd = "date +%s.%N"
    if  ( (cmd | getline x) > 0 ) {
        close(cmd)
        while ( (cmd | getline y) > 0 ) {
            close(cmd)
            printf "%s\r", y-x
            if (++i == 10) exit
        }
    }
}
$
$ awk -f tst.awk
$ 7176013

I don't know what the second date in your shell command is doing but I figure you can figure that part out and code it in awk or set up a cmd2 variable to call date again if necessary.

Oh, what the heck:

BEGIN {
    date_sN = "date +%s.%N"
    date_TN_start = "date +%T.%N -ud@"

    if  ( (date_sN | getline x) > 0 ) {
        close(date_sN)
        while ( (date_sN | getline y) > 0 ) {
            close(date_sN)
            date_TN = sprintf("%s%.11f", date_TN_start, y - x)
            if  ( (date_TN | getline d) > 0 ) {
                close(date_TN)
                printf "%s\r", d
            }
            if (++i == 10) exit
        }
    }
}
Community
  • 1
  • 1
Ed Morton
  • 188,023
  • 17
  • 78
  • 185