2

I have a bash script:

#!/bin/bash

gawk -f realmap.awk realmap.log | column -ts: > realmap.csv

gnuplot <<-_EOF_
    set term png
    set out 'realmap.png'
    set xlabel 'index'
    set ylabel 'bytes'
    set style data lp
    plot 'realmap.csv' u 1:2 t col, '' u 1:3 t col, '' u 1:4 t col, '' u 1:5 t col, '' u 1:6 t col, '' u 1:7 t col
_EOF_

rm realmap.csv

display realmap.png

And a awk script:

#!/usr/bin/gawk -f

BEGIN{
    printf("%s:%s:%s:%s:%s:%s:%s\n", "index", "total", "used", "free", "cached", "buffers", "cache")
}

/^#/{
    gsub("#", "")
    printf("%d:", $0+1)
}

/^M/{
    printf("%d:%d:%d:%d:", $2,$3,$4,$7)
}

/^-/{
    printf("%d:%d\n", $3, $4)
}

How to combine these two scripts as one?

1
  • what do u mean by combine??? Combination depends on what functionality u want to achieve... Dec 7, 2011 at 5:30

3 Answers 3

2

You don't need a "here document". Just place the entire awk program — properly quoted so that all of the quotation marks and metacharacters within it are not recognized by the shell and so that the linefeeds and other whitespace don't cause it to be split into multiple arguments by the shell — as the first command-line argument, without using awk's -f option. Without -f, the first command line argument is the program to run. The awk manual page is your friend.

1
  • 3
    Do you have an example? Sep 21, 2016 at 19:25
9

Note:

The self consuming script pattern in the 2nd code sample can be used for ANYTHING that reads a file. Don't be distracted by the OP's use of awk.

Answer:

What you asked for was a heredoc. It's tricky to use in this case, but I love heredocs so I'm going to show you how to do it. You have to incorporate an even lesser known bash feature, process substitution with <()

#!/bin/bash

  # The <( begins a process substitution. It's valid to use with -f because what gets
  # substituted is a file descriptor like /dev/fd/5
  # The quoting on '_EOF_' prevents the shell from expanding the contents of the heredoc,
  # as if it were a big double quoted string. So, your $2, $3, etc. are safe.
gawk -f <(cat - <<-'_EOF_'
    BEGIN{
        printf("%s:%s:%s:%s:%s:%s:%s\n", "index", "total", "used", "free", "cached", "buffers", "cache")
    }

    /^#/{
        gsub("#", "")
        printf("%d:", $0+1)
    }

    /^M/{
        printf("%d:%d:%d:%d:", $2,$3,$4,$7)
    }

    /^-/{
        printf("%d:%d\n", $3, $4)
    }
_EOF_
) realmap.log | column -ts: > realmap.csv

gnuplot <<-_EOF_
    set term png
    set out 'realmap.png'
    set xlabel 'index'
    set ylabel 'bytes'
    set style data lp
    plot 'realmap.csv' u 1:2 t col, '' u 1:3 t col, '' u 1:4 t col, '' u 1:5 t col, '' u 1:6 t col, '' u 1:7 t col
_EOF_

rm realmap.csv

display realmap.png

So that is the answer you asked for. Now, the way I would do it is with what I call the self consuming script pattern.

#!/bin/bash

  # The <( begins a process substitution. It's valid to use with -f because what gets
  # substituted is a file descriptor like /dev/fd/5
  # Notice the use of brackets. That prevents the following line from matching itself.
gawk -f <(sed -e '/[B]EGIN_AWK1/,/[E]ND_AWK1/!d' $0) realmap.log | column -ts: > realmap.csv

gnuplot <<-_EOF_
    set term png
    set out 'realmap.png'
    set xlabel 'index'
    set ylabel 'bytes'
    set style data lp
    plot 'realmap.csv' u 1:2 t col, '' u 1:3 t col, '' u 1:4 t col, '' u 1:5 t col, '' u 1:6 t col, '' u 1:7 t col
_EOF_

rm realmap.csv

display realmap.png

exit  ## Execution stops here. The rest is consumed by subprocesses of this script!

#BEGIN_AWK1
    BEGIN{
        printf("%s:%s:%s:%s:%s:%s:%s\n", "index", "total", "used", "free", "cached", "buffers", "cache")
    }

    /^#/{
        gsub("#", "")
        printf("%d:", $0+1)
    }

    /^M/{
        printf("%d:%d:%d:%d:", $2,$3,$4,$7)
    }

    /^-/{
        printf("%d:%d\n", $3, $4)
    }
#END_AWK1

To me that is pretty easy to follow and you can put multiple AWK or and other scripts in one file by incrementing the delimiter.

Enjoy bashing! Feel free to come vist #bash on freenode for even quicker answers.

For more info see http://tldp.org/LDP/abs/html/process-sub.html

5
  • 2
    This is black magic!! I mean, it is clear, but it is so clever!
    – onlycparra
    Apr 12, 2022 at 1:30
  • 1
    @onlycparra indeed. And don't let your imagination be limited to awk. You can use this pattern for any scripting language or even creating text files or base64 encoded binaries. I've used it in the disparaged curl ... | bash pattern. Apr 14, 2022 at 23:23
  • btw, why [B]EGIN_AWK1 and not BEGIN_AWK1?
    – onlycparra
    Apr 15, 2022 at 22:39
  • 1
    @onlycparra it's a trick to keep the grep search from matching itself. It's very helpful in situations like ps auxww | grep [s]sh compared to ps auxww | grep ssh Apr 21, 2022 at 3:01
  • the more I learn, the more I realize I don't know anything... thanks again.
    – onlycparra
    Apr 22, 2022 at 6:34
3

A here-document with a quoted limit string should work (see here-docs Example 19-7).

Example

AWK_CODE=$(cat << 'HereDoc'
  BEGIN {FS="\t"}
  {print $0}
  HereDoc
)
awk "${AWK_CODE}" myfile.txt
1
  • I like this approach as it allows for longer awk scripts to be styled in a nice readable form
    – ramses728
    Jun 29, 2022 at 15:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .