Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redirecting printf to file in awk

Tags:

linux

bash

awk

I have a simple bash script.

It's purpose is to monitor http access log file (test.log) and output to a file (out.log) the updated hit rate:

  stdbuf -o0 tail -f test.log | awk -F'[ "]+' '{
  ipcount[$1]++;
  print "test" > "out.log"; #Truncate out.log
  for (i in ipcount) {
        printf "%15s - %d\n", i, ipcount[i] >> "out.log";
        printf "%15s - %d\n", i, ipcount[i] }
}'

The main logic works. my only problem is the redirection to "out.log" that doesn't seem to work. The last printf outputs the expected result to the standard output. But the other two printf do not output anything to "out.log", and I cannot figure out why. out.log has all permissions (777)

like image 779
Yan4321 Avatar asked Aug 06 '15 16:08

Yan4321


People also ask

What is awk '{ print $1 }'?

awk '{print $1}' information. txt prints the first column. Then the output of that command (which you saw earlier on) is piped, using the pipe symbol | , to the head command, where its -1 argument selects the first line of the column. If you wanted two lines printed, you'd do: awk '{print $1}' information.txt | head -2.

How do you store awk output in variables?

Example -1: Defining and printing variable `awk` command uses '-v' option to define the variable. In this example, the myvar variable is defined in the `awk` command to store the value, “AWK variable” that is printed later. Run the following command from the terminal to check the output.

How do I print awk output?

To print a blank line, use print "" , where "" is the empty string. To print a fixed piece of text, use a string constant, such as "Don't Panic" , as one item. If you forget to use the double-quote characters, your text is taken as an awk expression, and you will probably get an error.

What does awk F do?

The -f instructs the awk utility to get the awk program from the file source-file. Any filename can be used for source-file. For example, you could put the program: BEGIN { print "Don't Panic!" }


1 Answers

This should work for you:

tail -f test.log | awk -F'[ "]+' -v out_file="out.log" '{
    val_count[$1]++
    print "" > out_file

    for (i in val_count) {
        printf "%15s - %d\n", i, val_count[i] >> out_file
        printf "%15s - %d\n", i, val_count[i]
    }

    close(out_file)
}'

(Note: I moved the output file definition to the command line to hopefully reduce repetition.)

Your original version has one fatal issue: print "" > "out.log" only truncates out.log the first time it's called. All subsequent calls to it will simply append to it because it's already open. As a secondary issue, awk likes to buffer output, so the contents will only be flushed intermittently.

To fix this, we need to close the file after each iteration. This forcefully flushes the output to out.log and forces the > redirection to re-truncate the file on the next iteration. If you didn't need to truncate each iteration, a simple fflush(out_file) would suffice.


To illustrate the issue more clearly...

This results in an output.txt that has multiple lines because it is truncated just once (the first iteration):

ls -l | awk '{ print "This file has many lines" > "output.txt"; }'

This results in an output.txt with a single output line because it is truncated multiple times:

ls -l | awk '{ print "This file has one line" > "output.txt"; close("output.txt"); }'
like image 139
Mr. Llama Avatar answered Nov 03 '22 00:11

Mr. Llama