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.

igal
  • 9,886
  • 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 '15 at 13:30
  • Let see awk version by awk -Wv instead of host environment – Costas Oct 16 '15 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 '15 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 '15 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 '15 at 18:23
  • I too know little about awk and I have a book on it! I have hit several times the case where my "slick" awk code didn't work when I moved it from Solaris to Linux, or to HP-UX. The book starts out by saying "There are a lot of awk versions ..." so the hack answer or the perl answer might be appropriate if you know your script will be running in under various Unix flavors. – Mark Stewart Oct 16 '15 at 18:40
  • The usual way to do this is to test the operating system using 'uname', and then to choose the expected command based on the OS. – Douglas Held Oct 17 '15 at 04:29
  • @DouglasHeld As I mentioned in the original post, checking the OS (with uname) is the method that is currently failing due to having GNU utilities installed on a BSD system. – igal Oct 17 '15 at 16:10
  • You've not selected an answer here. Does that mean that none of these solutions worked?...or something else? – Seamus Feb 15 '22 at 05:53
  • None of these are a complete solution, in my opinion. It seems that there may not be a good solution. – igal Feb 15 '22 at 08:58

6 Answers6

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
glenn jackman
  • 85,964
  • 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. – Thomas Dickey Oct 18 '15 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

Kip K
  • 123
  • 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 http://unix.stackexchange.com/a/85250/109842 – Eric Renouf Oct 16 '15 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
  • 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 '15 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) – Olivier Dulac Sep 21 '20 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
Archemar
  • 31,554
  • 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 '23 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 – RARE Kpop Manifesto Nov 01 '23 at 01: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) – RARE Kpop Manifesto Nov 01 '23 at 01: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

ssta
  • 99
  • Could you [edit] to provide a bit more detail on how you'd use that macro for this scenario? – Michael Homer Oct 17 '15 at 00:33
  • It doesn't actually tell which one it found, but simply looks for awk implementations and chooses one based on its name. – Thomas Dickey Oct 18 '15 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 01 '23 at 13:56
-3

i have a scripted utility :

    for __ in mawk1 mawk2 gawk nawk; do
    if [ &quot;$ZSH_VERSION&quot; ]; then
        $( printf '%s' &quot;$__&quot; ) --version
    else
        eval &quot;$__ --version&quot;
    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.

  • 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 '23 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. – RARE Kpop Manifesto Nov 01 '23 at 01: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 – RARE Kpop Manifesto Nov 01 '23 at 01: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 01 '23 at 11:30
  • @terdon : it was from one of my ancient templates i even forgot why i needed that eval bit anyway. Or you can make that perl simpler with ::::::::: ::::::::::::: ::::::::::::: ::::::::::::: ::::::: ::::::: ::::::::::::: :::::::::mawk '$0=$2' FS='(^[^.]+|,) ' ::: no backreferences needed at all – RARE Kpop Manifesto Nov 02 '23 at 00:01
  • 2
    Sure, anything would be better than what you are using, please edit and fix it. – terdon Nov 02 '23 at 08:32