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

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.)