12

I'm using a command-line application which is essentially a collection of bash shell scripts. The application was written to run on BSD/OSX and also on Linux. One of the scripts relies on awk. It contains two awk commands: one written for nawk (the standard BSD awk implementation) and one written for gawk (the GNU awk implementation).

The two awk commands in question are not cross-compatible with the different environments; in particular the nawk command fails when run with gawk. The script checks the kernel name (i.e. uname -s) in order to determine the host environment, and then runs the appropriate awk command. However I prefer to work on Mac OS X with the GNU core utilities installed, so the script fails to run correctly.

In the process of thinking about how best to fix this bug it occurred to me that it would be nice to know how to programmatically distinguish between different flavors of the common command-line utilities, preferably in a relatively robust and portable way.

I noticed that nawk doesn't accept the '-V' flag to print the version information, so I figured that something like the following should work:

awk -V &>/dev/null && echo gawk || echo nawk

Another variation could be:

awk -Wversion &>/dev/null && echo gawk || echo nawk

This seems to work on my two testing environments (OS X and CentOS). Here are my questions:

  • Is this the best way to go?
  • Is there a way to extend this to handle other variations of awk (e.g. mawk, jawk, etc.)?
  • Is it even worth worrying about other versions of awk?

I should also mention that I know very little about awk.

10
  • If the awk command isn't extremely complicated, or even if it is, you might consider porting it to perl or something else which is uniform.
    – Wildcard
    Oct 16, 2015 at 13:30
  • Let see awk version by awk -Wv instead of host environment
    – Costas
    Oct 16, 2015 at 13:35
  • 1
    My awk is very weak but I believe that it is quite simple. I actually already rewrote it in pure bash. But this is one of those situations where I'm more interested in satisfying my curiosity than in actually solving the original problem.
    – igal
    Oct 16, 2015 at 13:37
  • @Costas Something like that actually did occur to me, but I wasn't sure how fragile it might be; I know very little about awk. I've added my current solution to my post.
    – igal
    Oct 16, 2015 at 13:49
  • 1
    One alternative is to ignore what version of awk is being used, and code to the POSIX specification instead.
    – chepner
    Oct 16, 2015 at 18:23

6 Answers 6

11
if awk --version 2>&1 | grep -q "GNU Awk"
then
    awk 'BEGIN {print "I am GNU Awk"}'

elif awk -Wv 2>&1 | grep -q "mawk"
then
    awk 'BEGIN {print "I am mawk"}'

else
    awk 'BEGIN {print "I might be nawk, might not be"}'
fi

Alternatively, test is awk is a symbolic link:

awk=$( command -v awk )
[[ -L $awk ]] && readlink $awk # make some decision about the result of that
1
  • This one is okay... but there are a few variations (hard to distinguish) for old-awk, nawk, and the newer BWK versions. For those, a sample script might be be tailored. Oct 18, 2015 at 17:45
3

Try using the which command and use its exit code.

which nawk
if [[ $? == 0 ]]; then
    command="nawk"
else
    command="gawk"
fi

then format your script to use the variable as the command

$command '{print $1}` 

would be read as

nawk '{print $1}`

if which finds nawk. Otherwise it would use gawk

1
  • 1
    since command is a bash builtin you might want to use a different name for your variable, and you might also want to see the incredibly detailed answer about why for many shells there are better alternatives to which at unix.stackexchange.com/a/85250/109842 Oct 16, 2015 at 14:33
1

GNU Awk is often installed as gawk, with awk as a symlink to it on systems where GNU is the default. I would guess this is the case on BSD and OS X systems, since they already have their own awk.

if gawk '{ exit; }' < /dev/null 2> /dev/null
then
    echo "gawk available"
else
    echo "gawk not available"
fi
2
  • Thank you for your input. This is a good idea and you're correct that this is the case for me on OS X. But I was hoping for an intrinsic solution, so to speak.
    – igal
    Oct 16, 2015 at 14:16
  • This is very innacurate, as there are many more versions of awk than just "mawk / gawk" (regular awk, for exemple, is neither) Sep 21, 2020 at 17:22
-1

A dirty hack

if nawk 'BEGIN { nawk-only-function() ;}' 
then 
   nawk -f nfile.awk ...
else 
   gawk -f gfile.awk ...
fi
  • where nawk-only-function() exist only on nawk
3
  • This would be really useful with an example of what nawk-only-function might be. But as presented this anwer is not helpful.
    – symcbean
    May 16, 2023 at 16:12
  • @symcbean : @Archemar - just do index("", ""). If the result returned is 0 instead of 1 then you know you're on nawk. Another test would be "" ~ /\0/, which is false (0) for everyone else but true (1) for nawk. A third test is try doing sprintf("%c", 0). If that returns an empty string instead of the null byte \0, you're on nawk. These 3 are the most reliable tests I know of that cannot be circumvented via setting any flag or variable. But that test doesn't work if you use printf() instead of sprintf() - another one of nawk's inconsistencies Nov 1, 2023 at 1:37
  • These 3 tests I mentioned have the added bonus of not triggering fatal error message(s) if executed via any other awk variant. nawk is so strange that ::::::::::::::::: :::::::::::::::::::::::::::::: :::: nawk 'BEGIN { print ("" ~ /./), ("" ~ /\0/) }' ==> 0 1 - it thinks the empty string ("") doesn't contain anything, but thinks it contains at least 1 null byte (\0) Nov 1, 2023 at 1:45
-2

I'd probably try abusing the AC_PROG_AWK m4 macro in autoconf to choose one, ordering them appropriately. It's probably overkill though

3
  • Could you edit to provide a bit more detail on how you'd use that macro for this scenario? Oct 17, 2015 at 0:33
  • It doesn't actually tell which one it found, but simply looks for awk implementations and chooses one based on its name. Oct 18, 2015 at 17:43
  • Or what this macro is, where one can find it, how one would abuse it, etc. This might be a good answer, but I have no idea what you are suggesting. Remember that most users here are no C programmers, they're users and administrators of nix machines.
    – terdon
    Nov 1, 2023 at 13:56
-3

i have a scripted utility :

    for __ in mawk1 mawk2 gawk nawk; do

        if [ "$ZSH_VERSION" ]; then
            $( printf '%s' "$__" ) --version
        else
            eval "$__ --version"
        fi

    done | mawk 'NF *= /^(GNU |m)?[Aa]wk/' OFS= \
                 FS='^[[:alpha:]]+[ ]?([[:alpha:]]+[ ]+)?|[,].+$'

1.3.4 20230808
1.9.9.6
5.2.2
20200816

but I suppose the completely unquoted version

$__ --version

works with many shells too, but I'll stick with the safer syntax. fish shell requires some other syntax that I'm not familiar with.

6
  • 3
    What is that Rube Goldberg loop there? Why not just for awk in mawk1 mawk2 gawk nawk; do "$awk" --version; done?
    – muru
    Oct 30, 2023 at 14:50
  • @muru : technically yes you can. i didn't cuz if you also have some variant named awk along your path, then for awk in mawk1 mawk2 gawk awk; do "$awk" --version; done feels more confusing to me that's all. I did it that way cuz i think one of those stranger shells act up otherwise. the printf weirdness was just my usual code to test my code under a dozen gawk invocation flags. heck you can do it unquoted in you like. I would've preferred <<< but the wretched Austin Group is so damn brain dead they mandate here-docs but not here-strings. Nov 1, 2023 at 1:28
  • …. even though a proper implementation for here-docs already has all the necessary ingredients to also implement here-strings practically for free, but those stable geniuses think so otherwise. Just as einstinenian as their decision not to standardize invocation flags for date and stat utilities between gnu and bsd variants Nov 1, 2023 at 1:33
  • The surprising thing isn't the __ variable, it is the use of eval and the check fo zsh and everything else instead of just doing for __ in mawk gawk nawk; do $__ --version | head -n1 | perl -pe 's/.*?\s([0-9. ]+).*/$1/'; done?
    – terdon
    Nov 1, 2023 at 11:30
  • 2
    Sure, anything would be better than what you are using, please edit and fix it.
    – terdon
    Nov 2, 2023 at 8:32

You must log in to answer this question.

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