Why does this awk script that runs on CentOS not run on Ubuntu?

(for reference, the code is likely also here: https://github.com/garyexplains/examples which is probably a better source than a video.)


Would there be any reason why this simple script on CentOS wouldn't run on Ubuntu?

[nsaunders@rolly awk]$ 
[nsaunders@rolly awk]$ awk -f loop.awk numbers.txt 
1 2 3 
1 2 3 4 5 6 7 
1 2 3 4 5 6 7 8 9 10 11 12 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 

[nsaunders@rolly awk]$ 
[nsaunders@rolly awk]$ cat loop.awk 
func printlist(n) {
    for(i=1;i<=n;i++) {
        printf("%d ",i)
    }
    printf("\n")
}


{printlist($1)}

[nsaunders@rolly awk]$ 
[nsaunders@rolly awk]$ cat numbers.txt 
3
7
12
15
16
31

[nsaunders@rolly awk]$ 

I'm getting:

awk: loop.awk: line 11: function printlist never defined
awk: loop.awk: line 11: function printlist never defined

awk version on Ubuntu:

mawk 1.3.4 20200120
Copyright 2008-2019,2020, Thomas E. Dickey
Copyright 1991-1996,2014, Michael D. Brennan

random-funcs:       srandom/random
regex-funcs:        internal
compiled limits:
sprintf buffer      8192
maximum-integer     2147483647

And on the CentOS machine:

[nsaunders@rolly ~]$ 
[nsaunders@rolly ~]$ awk -W version
GNU Awk 4.2.1, API: 2.0 (GNU MPFR 3.1.6-p2, GNU MP 6.1.2)
Copyright (C) 1989, 1991-2018 Free Software Foundation.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
[nsaunders@rolly ~]$ 

isn't awk pretty much awk regardless of what machine you're on?


As noted in the The GNU Awk User’s Guide

In many awk implementations, including gawk, the keyword function may be abbreviated func. (c.e.) However, POSIX only specifies the use of the keyword function.

So the error is possibly because the implementation of awk on your Ubuntu system is mawk, or that something (such as a POSIXLY_CORRECT environment variable) is affecting how gawk behaves on your system.

Ubuntu used to ship with mawk as the default awk - but as far as I know, all currently supported versions use gawk by default. If you installed mawk via the packaqge management system, you should be able to use the update-alternatives mechanism to query/set the default ex.

update-alternatives --query awk

sudo update-alternatives --config awk

isn't awk pretty much awk regardless of what machine you're on?

No, and you may even have multiple implementations on the same system. gawk (GNU Awk) has a number of features not present in mawk. The one you've run into isn't the only one. The gawk documentation contains a list of common features that differ across major AWK implementations.

As steeldriver says, using func as an abbreviation for function is supported by some but not all AWK implementations. To solve this particular problem, the best thing to do is just change func to function. With that single change, your script works with mawk as well as gawk.

If for some reason you can't or don't wish to do that, or if you need other nonstandard functionality that gawk provides but mawk doesn't, you can use gawk, which is provided by the gawk package.

gawk -f loop.awk numbers.txt

As shown in steeldriver's answer, you can use update-alternatives to make awk resolve to gawk rather than mawk (or that may happen automatically when you install gawk).

However, you're using awk -f, so if you're willing to add a shebang line to the top of your AWK script and mark the script executable (chmod +x loop.awk), then you can have it specify which interpreter ought to be used:

#!/usr/bin/awk -f

func printlist(n) {
    for(i=1;i<=n;i++) {
        printf("%d ",i)
    }
    printf("\n")
}

{printlist($1)}

You would then run the script with the command:

./loop.awk numbers.txt

Then other scripts remain unaffected.

For this tiny script with a single occurrence of the func keyword that can be written as function, adding a shebang probably isn't your best choice, unless you were going to do that anyway. For more complex situations where you need gawk features not present in mawk, though, I suggest considering it. Adding a shebang also has the benefit of clarifying what implementation of AWK you wish to be used, and a script for which this doesn't matter and that is written to be portable can have a #!/usr/bin/awk -f shebang.

(Of course, scripts with such shebangs aren't totally portable in the broader sense, since they assume awk is in /usr/bin.)