Search code examples
functionawk

@include in awk, why isn't this library function executed?


I tried to use my library of functions from lib-of-fns.awk in my current working directory by executing using-fns.awk - a script with the following content:

#!/usr/bin/awk -f

@include "lib-of-fns.awk"
BEGIN { print_all("using-fns.awk") }

The content of lib-of-fns.awk is:

#!/usr/bin/awk -f

function print_all(fname) {
  /.*/ fname
}


It does not work - no input of any file in my stdout whereas if I manually run the command awk '/.*/' using-fns.awk I get the content of using-fns.awk printed to the screen. Why so?

P.S. Using gawk.


Solution

  • This has nothing to do with libraries, your print_all function wouldn't work in a single file or script, i.e. this:

    awk '
        function print_all(fname) {
          /.*/ fname
        }
        BEGIN { print_all("using-fns.awk") }
    '
    

    would produce no output as it's not close to the syntax of what you appear to be trying to do.

    You should test any library functions work when called from within the same file/script before adding the complexity of putting them in a separate library file. Your function is equivalent to writing:

        function print_all(fname,      rslt) {
          if ($0 ~ /.*/) {
            rslt = 1
          }
          else {
            rslt = 0
          }
          rslt fname   # concatenates 2 strings and does nothing with the result.
        }
    

    If you're trying to print every line that matches the regexp .* (which will be all of them) from the file name passed as an argument then change this:

    function print_all(fname) {
      /.*/ fname
    }
    

    to this:

    function print_all(fname,    line) {
        while ( (getline line < fname) > 0 ) {
            if ( line ~ /.*/ ) {
                print line
            }
        }
        close(fname)    # just to be sure
    }
    

    and then you can test it using:

    $ seq 3 > "using-fns.awk"
    $ awk '
    awk '
    function print_all(fname,    line) {
        while ( (getline line < fname) > 0 ) {
            if ( line ~ /.*/ ) {
                print line
            }
        }
        close(fname)    # just to be sure
    }
    BEGIN { print_all("using-fns.awk") }
    '
    1
    2
    3
    

    I'm assuming that the test for /.*/ is just an example for testing purposes and you aren't REALLY going to needlessly compare every line to .*.

    Make sure to read and fully understand http://awk.freeshell.org/AllAboutGetline before using getline though.

    Alternatively, you could spawn a subshell to call awk from your function if you want to keep a similar syntax to what you currently have:

    $ awk '
    function print_all(fname) {
        system("awk \047/.*/\047 \047" fname "\047")
    }
    BEGIN { print_all("using-fns.awk") }
    '
    1
    2
    3
    

    but that'd be pointless and inefficient and could produce unexpected output ordering when mixing the output from your main script with the output from the script called from the subshell so I wouldn't do it.

    FWIW if I were to write a script to print the lines that match some regexp from a file whose name is determined inside the script, I'd usually do the following instead of calling getline in a loop or spawning a subshell:

    $ awk 'BEGIN{ARGV[ARGC++]="using-fns.awk"} /.*/'
    1
    2
    3
    

    By the way - don't use a shebang to call awk.