Calling an executable program using awk

ShellAwk

Shell Problem Overview


I have a program in C that I want to call by using awk in shell scripting. How can I do something like this?

Shell Solutions


Solution 1 - Shell

From the AWK man page:

system(cmd)
executes cmd and returns its exit status

The GNU AWK manual also has a section that, in part, describes the system function and provides an example:

system("date | mail -s 'awk run done' root")

Solution 2 - Shell

A much more robust way would be to use the getline() function of GNU awk to use a variable from a pipe. In form cmd | getline result, cmd is run, then its output is piped to getline. It returns 1 if got output, 0 if EOF, -1 on failure.

First construct the command to run in a variable in the BEGIN clause if the command is not dependant on the contents of the file, e.g. a simple date or an ls.

A simple example of the above would be

awk 'BEGIN {
    cmd = "ls -lrth"
    while ( ( cmd | getline result ) > 0 ) {
        print result
    }
    close(cmd);
}'

When the command to run is part of the columnar content of a file, you generate the cmd string in the main {..} as below. E.g. consider a file whose $2 contains the name of the file and you want it to be replaced with the md5sum hash content of the file. You can do

awk '{ cmd = "md5sum "$2
       while ( ( cmd | getline md5result ) > 0 ) { 
           $2 = md5result
       }
       close(cmd);
 }1'

Another frequent usage involving external commands in awk is during date processing when your awk does not support time functions out of the box with mktime(), strftime() functions.

Consider a case when you have Unix EPOCH timestamp stored in a column and you want to convert that to a human readable date format. Assuming GNU date is available

awk '{ cmd = "date -d @" $1 " +\"%d-%m-%Y %H:%M:%S\"" 
       while ( ( cmd | getline fmtDate) > 0 ) { 
           $1 = fmtDate
       }
       close(cmd);
}1' 

for an input string as

1572608319 foo bar zoo

the above command produces an output as

01-11-2019 07:38:39 foo bar zoo

The command can be tailored to modify the date fields on any of the columns in a given line. Note that -d is a GNU specific extension, the *BSD variants support -f ( though not exactly similar to -d).

More information about getline can be referred to from this AllAboutGetline article at awk.freeshell.org page.

Solution 3 - Shell

There are several ways.

  1. awk has a system() function that will run a shell command:

    system("cmd")

  2. You can print to a pipe:

    print "blah" | "cmd"

  3. You can have awk construct commands, and pipe all the output to the shell:

    awk 'some script' | sh

Solution 4 - Shell

Something as simple as this will work

awk 'BEGIN{system("echo hello")}'

and

awk 'BEGIN { system("date"); close("date")}'

Solution 5 - Shell

I use the power of awk to delete some of my stopped docker containers. Observe carefully how i construct the cmd string first before passing it to system.

docker ps -a | awk '$3 ~ "/bin/clish" { cmd="docker rm "$1;system(cmd)}'

Here, I use the 3rd column having the pattern "/bin/clish" and then I extract the container ID in the first column to construct my cmd string and passed that to system.

Solution 6 - Shell

It really depends :) One of the handy linux core utils (info coreutils) is xargs. If you are using awk you probably have a more involved use-case in mind - your question is not very detailled.

printf "1 2\n3 4" | awk '{ print $2 }' | xargs touch

Will execute touch 2 4. Here touch could be replaced by your program. More info at info xargs and man xargs (really, read these). I believe you would like to replace touch with your program.

Breakdown of beforementioned script:

printf "1 2\n3 4"
# Output:
1 2
3 4

# The pipe (|) makes the output of the left command the input of
# the right command (simplified)
printf "1 2\n3 4" | awk '{ print $2 }'
# Output (of the awk command):
2
4

# xargs will execute a command with arguments. The arguments
# are made up taking the input to xargs (in this case the output
# of the awk command, which is "2 4".
printf "1 2\n3 4" | awk '{ print $2 }' | xargs touch
# No output, but executes: `touch 2 4` which will create (or update
# timestamp if the files already exist) files with the name "2" and "4"

Update In the original answer, I used echo instead of printf. However, printf is the better and more portable alternative as was pointed out by a comment (where great links with discussions can be found).

Solution 7 - Shell

#!/usr/bin/awk -f

BEGIN {
	command = "ls -lh"
	
	command |getline
}

Runs "ls -lh" in an awk script

Solution 8 - Shell

I was able to have this done via below method

cat ../logs/em2.log.1 |grep -i 192.168.21.15 |awk '{system(`date`); print $1}'

awk has a function called system it enables you to execute any linux bash command within the output of awk.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser2030431View Question on Stackoverflow
Solution 1 - ShellCalebView Answer on Stackoverflow
Solution 2 - ShellInianView Answer on Stackoverflow
Solution 3 - ShellBarmarView Answer on Stackoverflow
Solution 4 - ShellMirageView Answer on Stackoverflow
Solution 5 - ShelleigenfieldView Answer on Stackoverflow
Solution 6 - ShellFelixView Answer on Stackoverflow
Solution 7 - ShellGrahamView Answer on Stackoverflow
Solution 8 - ShellMansur Ul HasanView Answer on Stackoverflow