check whether command has a specific command line option

350 Views Asked by At

Is there any way to check whether a command has a specific command line option from shell script?

My use case is tail command. In BSD version of tail, it has -r (reverse) option. This option is not available in GNU version

I can do a check from shell script to use this option only for MacOS, and use tac for Linux. But the problem is people can install GNU version of tail in MacOS too. So the better solution is to check whether the command has -r option.

3

There are 3 best solutions below

2
On BEST ANSWER

This is an impossible problem to solve in the general case; what happens if somebody has a different program installed as tail or an alias or shell function that changes its behaviour? You're finding out here that making portable/reliable shell scripts (particularly between very different operating systems) can be quite difficult. You'll have to decide for yourself how to make all these decisions and where you want to draw the line.

The quick & dirty way would be simply to call tail -r with some known input and see if the output matches what you'd expect. Use that to make a decision about what to do later on in your script. An example (probably not bulletproof):

#!/bin/bash

INPUT=$(mktemp)
OUTPUT=$(mktemp)
COMPARE=$(mktemp)

cat >${INPUT} <<EOF
1
2
3
EOF

cat >${COMPARE} <<EOF
3
2
1
EOF

tail -r ${INPUT} >${OUTPUT} 2>&1

cmp -s ${OUTPUT} ${COMPARE}
if [ $? == 0 ]
then
    echo "'tail -r' behaves as expected"
else
    echo "'tail -r' does not behave as expected"
fi

rm ${INPUT} ${OUTPUT} ${COMPARE}

It outputs as expected on a Mac and Linux machine I just tested.

1
On

Neither tail -r nor tac is portable; you may come up with a solution that works in both BSD and GNU but that might fail on other systems. So, implement the same functionality in awk and use it instead. Like:

awk '{buf[NR]=$0} END{for(i=NR;i>0;i--)print buf[i]}'

This will work with all POSIX awks.

A conjunction of -r and -c or -n can also be emulated portably by piping tail to above command. -r and -b might be a bit challenging but is not impossible.

0
On

The common technique is to define the function if the utility found in the environment doesn't do what you want. Something similar to:

expect=$'2\n1'
if ! test "$(printf '1\n2\n' | tail -r)" = "$expect"; then
        tail() { if test "$1" = "-r"; then shift; command tail "$@" | tac;
                else command tail "$@"; fi; }
fi

In this particular case, the above function is not correct, as you would need to implement full option parsing to provide the required functionality (eg, tail -rn2 and tail -rn 2 need to be parsed and the positional parameters manipulated before passing them to tail). It would be easier to implement tac as a function when tac is missing.