73

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

netcoder
  • 66,435
  • 19
  • 125
  • 142
user2030431
  • 761
  • 1
  • 6
  • 8
  • 1
    A compiled c program is just a program... Just run it like you would any command line operation... – PearsonArtPhoto Jan 31 '13 at 20:16
  • 2
    You can call `system()` with awk to execute commands. Perhaps you could explain your needs further since there is a better method of going about this >90% of the time. – Grambot Jan 31 '13 at 20:23
  • 4
    this is usually the wrong approach. If you post a small awk script and explain when you want to call your C program we can tell either how to do that or what a better approach would be. – Ed Morton Jan 31 '13 at 22:20

9 Answers9

66

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")
Caleb
  • 124,013
  • 19
  • 183
  • 272
  • thank you for your help! i have a shell command in busybox: logread -f| awk { if..... } and inside the awk i want to call my program in c when an if statement is true with arguments. can you give me a hint? – user2030431 Jan 31 '13 at 20:34
  • 1
    try `logread -f | awk '{ if(condition01){system("yourCprogram arguments")} }'` – nullrevolution Jan 31 '13 at 21:32
  • 1
    @user2030431 You would be nice to readers if you would update your question with these/more details. And yes, I know its a bit late for that :) – Felix Mar 07 '18 at 10:41
36

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

Barmar
  • 741,623
  • 53
  • 500
  • 612
36

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.

Inian
  • 80,270
  • 14
  • 142
  • 161
  • This works great! What's the reason for using a while loop for the command instead of doing `cmd | getline fmtDate`? – Matthias Braun Jun 14 '20 at 15:17
  • 1
    @MatthiasBraun: In case of a multi-line output, the loop runs, till every last line is read. The `if` is for just a one time invocation – Inian Jun 14 '20 at 15:28
7

Something as simple as this will work

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

and

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

Mirage
  • 30,868
  • 62
  • 166
  • 261
5

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.

daparic
  • 3,794
  • 2
  • 36
  • 38
4

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).

Felix
  • 4,510
  • 2
  • 31
  • 46
  • @MountainX In which way does it work (incorrectly)? Which Operating System and Shell do you use? – Felix Mar 07 '18 at 10:16
  • In Arch Linux and bash, here are my results: [user@comp1 ~]$ echo "1 2\n3 4" | awk '{ print $2 }' | xargs touch [user@comp1 ~]$ ls -la -rw-rw---- 1 user user 0 Mar 7 13:34 2n3 – MountainX Mar 07 '18 at 18:36
  • 1
    problem may be the one discussed here: https://stackoverflow.com/questions/8467424/echo-newline-in-bash-prints-literal-n. The following works for me: printf "1 2\n3 4" | awk '{ print $2 }' | xargs touch – MountainX Mar 07 '18 at 19:00
  • Good picks and thanks for the feedback. Will update the example. – Felix Mar 07 '18 at 19:09
3
#!/usr/bin/awk -f

BEGIN {
    command = "ls -lh"

    command |getline
}

Runs "ls -lh" in an awk script

Graham
  • 53
  • 4
1

You can call easily with parameters via the system argument. For example, to kill jobs corresponding to a certain string (we can otherly of course) :

 ps aux | grep my_searched_string | awk '{system("kill " $2)}'
Cyril
  • 157
  • 2
  • 2
0

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.

Mansur Ul Hasan
  • 2,898
  • 27
  • 24