Search code examples
bashloggingprintfbash-function

Bash: Formatting multi-line function results alongside each other


I have three functions that digest an access.log file on my server.

hitsbyip() {
        cat $ACCESSLOG | awk '{ print $1 }' | uniq -c | sort -nk1 | uniq
}
hitsbyhour() {
        cat $ACCESSLOG | cut -d[ -f2 | cut -d] -f1 | awk -F: '{print $2":00"}' | sort -n | uniq -c
}
hitsbymin() {
        hr=$1
        grep "2015:${hr}" $ACCESSLOG | cut -d[ -f2 | cut -d] -f1 | awk -F: '{print $2":"$3}' | sort -nk1 -nk2 | uniq -c
}

They all work fine when used on their own. All three output 2 small colums of data.

Now I am looking to create another function called report which will simply use printf and its formatting possibilities to print 3 columns of data with header, each of them the result of my three first functions. Something like that:

report() {
       printf "%-30b %-30b %-30b\n" `hitsbyip` `hitsbyhour` `hitsbymin 10`
}

The thing is that the format is not what i want; it prints out the columns horizontaly instead of side by side.

Any help would be greatly appreciated.


Solution

  • Once you use paste to combine the output of the three commands into a single stream, then you can operate line-by-line to format those outputs.

    while IFS=$'\t' read -r by_ip by_hour by_min; do
      printf '%-30b %-30b %-30b\n' "$by_ip" "$by_hour" "$by_min"
    done < <(paste <(hitsbyip) <(hitsbyhour) <(hitsbymin 10))
    

    Elements to note:

    • <() syntax is process substitution; it generates a filename (typically of the form /dev/fd/## on platforms with such support) which will, when read, yield the output of the command given.
    • paste takes a series of filenames and puts the output of each alongside the others.
    • Setting IFS=$'\t' while reading ensures that we read content as tab-separated values (the format paste creates). See BashFAQ #1 for details on using read.
    • Putting quotes on the arguments to printf ensures that we pass each value assembled by read as a single value to printf, rather than letting them be subject to string-splitting and glob-expansion as unquoted values.