Check if any of the parameters to a bash script match a string

70

13

I'm trying to write a script where I want to check if any of the parameters passed to a bash script match a string. The way I have it setup right now is

if [ "$3" != "-disCopperBld" -a "$4" != "-disCopperBld" -a "$5" != "-disCopperBld" -a "$6" != "-disCopperBld"]

but there might be a large number of parameters, so I was wondering if there is a better way to do this?

Thanks

EDIT: I tried this chunk of code out, and called the script with the option, -disableVenusBld, but it still prints out "Starting build". Am I doing something wrong? Thanks in advance!

while [ $# -ne 0 ]
do
    arg="$1"
    case "$arg" in
        -disableVenusBld)
            disableVenusBld=true
            ;;
        -disableCopperBld)
            disableCopperBld=true
            ;;
        -disableTest)
            disableTest=true
            ;;
        -disableUpdate)
            disableUpdate=true
            ;;
        *)
            nothing="true"
            ;;
    esac
    shift
done

if [ "$disableVenusBld" != true ]; then
    echo "Starting build"
fi

iman453

Posted 2010-09-08T19:28:14.553

Reputation: 1 025

Thanks for the replies guys, I appreciate ya'll taking time out to help me. I tried a chunk of code, but I can't seem to figure out what's going wrong. Any ideas? (I've pasted the code in an edit of my original post) – iman453 – 2010-09-09T15:29:05.520

Hmm: it works for me. I added #! /bin/sh - to the top of what you've included there, made the script executable, then ./t.sh prints "Starting build", but ./t.sh -disableVenusBld prints nothing. – Norman Gray – 2010-09-09T21:47:11.697

Answers

61

It looks like you're doing option handling in a shell script. Here's the idiom for that:

#! /bin/sh -

# idiomatic parameter and option handling in sh
while test $# -gt 0
do
    case "$1" in
        --opt1) echo "option 1"
            ;;
        --opt2) echo "option 2"
            ;;
        --*) echo "bad option $1"
            ;;
        *) echo "argument $1"
            ;;
    esac
    shift
done

exit 0

(There are a couple of conventions for indenting the ;;, and some shells allow you to give the options as (--opt1), to help with brace matching, but this is the basic idea)

Norman Gray

Posted 2010-09-08T19:28:14.553

Reputation: 951

3

A shift statement is used when the number of arguments to a command is not known in advance, for instance when users can give as many arguments as they like. In such cases, the arguments are processed in a while loop with a test condition of $#. This condition is true as long as the number of arguments is greater than zero. The $1 variable and the shift statement process each argument. The number of arguments is reduced each time shift is executed and eventually becomes zero, upon which the while loop exits. source

– Serge Stroobandt – 2015-01-22T22:47:53.173

Here is a similar example with additional echo $1 | sed argument value processing.

– Serge Stroobandt – 2015-01-22T22:57:27.010

30

This worked for me. It does exactly what you asked and nothing more (no option processing). Whether that's good or bad is an exercise for the poster :)

if [[ "$*" == *YOURSTRING* ]]
then
    echo "YES"
else
    echo "NO"
fi

This takes advantage of special handling of $* and bash super-test [[]] brackets.

Rich Homolka

Posted 2010-09-08T19:28:14.553

Reputation: 27 121

2IMHO this had to be the most correct answer, since the question requires only to check the presence of a parameter. I have edited, however, changing $* to $@, since the to-be-tested string might have spaces and added link to bash's documentation about it. – h7r – 2015-02-01T12:51:55.947

9Did you mean to use the =~ operator? Otherwise I don't see why this should work, and indeed it doesn't work when I try the exact script. – Seppo Enarvi – 2015-07-14T08:37:58.697

3Not working. bash -c 'echo args=$*; [[ "$@" == "bar" ]] && echo YES || echo NO' -- foo bar – Tobia – 2015-11-04T11:55:10.853

2

This compares the entire argument list with your string. I think you need to do what is suggested by this answer. i.e: bash -c 'echo args=$*; for i in "$@" ; do [[ $i == "bar" ]] && echo "Is set!" && break ; done' -- bar foo would work.

– starfry – 2016-12-19T20:23:50.323

3This answer has been wrong for the past four years because h7r’s edit broke it.  The original answer (which I have now restored) works, provided “your string” contains no glob characters, and with some false positives.  For example, if the command is create a new certificate and “your string” is cat, this will report a match because certificate contains cat. – Scott – 2019-05-22T19:46:22.350

Note for noobs like me: the YOURSTRING might be quoted, but the asterisks must be OUTSIDE the quotes, e.g. if [[ "$*" == *"my search string"* ]] – velis – 2019-06-28T07:20:58.343

8

How about searching (with wildcards) the whole parameter space:

if [[ $@ == *'-disableVenusBld'* ]]
then

Edit: Ok, ok, so that wasn't a popular answer. How about this one, it's perfect!:

if [[ "${@#-disableVenusBld}" = "$@" ]]
then
    echo "Did not find disableVenusBld"
else
    echo "Found disableVenusBld"
fi

Edit2: Ok, ok, maybe this isn't perfect... Think it works only if -param is at the start of the list and will also match -paramXZY or -paramABC. I still think the original problem can be solved very nicely with bash string manipulation, but I haven't quite cracked it here... -Can you??

Rich

Posted 2010-09-08T19:28:14.553

Reputation: 213

Your second suggestion — comparing the filtering substitution to the full substitution — works reasonably well, and has the advantage of not disrupting the list of arguments. – Donal Fellows – 2017-05-30T13:46:08.173

Could you explain what the second suggestion (especially ${@#) does? – velop – 2017-11-14T16:01:13.927

1@velop Sure! So you know that $@ is a special variable that holds all of the command line arguments? Well I've just used Bash string manipulation on that variable to remove the substring "-disableVenusBld", and then I compare it to the original $@.

So if $@ equals -foo -bar then ${@#-disableVenusBld} would still be -foo -bar, so I can see that the flag I'm looking for isn't present. However, if $@ equals -foo -disableVenusBld -bar then ${@#-disableVenusBld} would be -foo -bar which is not equal to $@, thus telling me that the flag I'm looking for is present! Cool eh! – Rich – 2017-11-14T20:23:14.893

@velop Learn more about Bash string manipulation here: http://tldp.org/LDP/abs/html/string-manipulation.html

– Rich – 2017-11-14T20:29:20.420

@Rich pretty neat ^^, thx for the explanation. – velop – 2017-11-17T10:21:27.373

This doesn't actually work - ${@# only removes the string from the beginning, so the example -foo -disableVenusBld -bar is unchanged – Nye – 2017-12-20T11:19:21.350

@Nye oh god, you're right! Don't know how I missed this.... so instead of "${@#-disableVenusBld}" I should've used "${@/-disableVenusBld/}" I suppose...? – Rich – 2017-12-20T15:12:55.657

Note that also '-disableVenusBldFOO' would also match the above, because the ${@#...} parameter expansion construct nullifies the matched-prefix of each element. – ankostis – 2018-06-04T18:44:02.217

4

disCopperBld=
for x; do
  if [ "$x" = "-disCopperBld" ]; then disCopperBld=1; break; fi
done
if [ -n "$disCopperBld" ]; then
  ...
fi

If you need to test only the parameters starting at $3, do the search in a function:

## Usage: search_trailing_parameters NEEDLE NUM "$@"
## Search NEEDLE amongst the parameters, skipping $1 through ${$NUM}.
search_trailing_parameters () {
  needle=$1
  shift $(($2 + 2))
  for x; do
    if [ "$x" = "$needle" ]; then return 0; fi
  done
  return 1
}
if search_trailing_parameters -disCopperBld 2 "$@"; then
  ...
fi

But I wonder why you're trying to do this in the first place, it's not a common need. Usually, you'd process options in order, as in Dennis's answer to your previous question.

Gilles 'SO- stop being evil'

Posted 2010-09-08T19:28:14.553

Reputation: 58 319