115

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
3
  • 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
    Sep 9, 2010 at 15:29
  • 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. Sep 9, 2010 at 21:47
  • for a in "$@"; do [[ "$a" == "-disCopperBld" ]] && echo match; done
    – bvj
    Sep 19, 2020 at 0:50

5 Answers 5

88

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)

3
  • 7
    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 Jan 22, 2015 at 22:47
  • Here is a similar example with additional echo $1 | sed argument value processing. Jan 22, 2015 at 22:57
  • If you like me think that loops in scripted languages are not a good idea, here's an alternative (the answer below): superuser.com/a/186304/1276328
    – mirekphd
    May 25, 2022 at 10:22
84

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.

9
  • 3
    IMHO 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
    Feb 1, 2015 at 12:51
  • 9
    Did 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. Jul 14, 2015 at 8:37
  • 3
    Not working. bash -c 'echo args=$*; [[ "$@" == "bar" ]] && echo YES || echo NO' -- foo bar
    – Tobia
    Nov 4, 2015 at 11:55
  • 7
    This 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. May 22, 2019 at 19:46
  • 12
    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
    Jun 28, 2019 at 7:20
10

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??

8
  • 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. May 30, 2017 at 13:46
  • Could you explain what the second suggestion (especially ${@#) does?
    – velop
    Nov 14, 2017 at 16:01
  • 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
    Nov 14, 2017 at 20:23
  • @velop Learn more about Bash string manipulation here: tldp.org/LDP/abs/html/string-manipulation.html
    – Rich
    Nov 14, 2017 at 20:29
  • @Rich pretty neat ^^, thx for the explanation.
    – velop
    Nov 17, 2017 at 10:21
7
[[ "$@" =~ '-disableVenusBld' ]] && disableVenusBld=true
[[ "$@" =~ '-disCopperBld'    ]] && disCopperBld=true
[[ "$@" =~ '-disableTest'     ]] && disableTest=true
[[ "$@" =~ '-disableUpdate'   ]] && disableUpdate=true

or more generally

[[ "$@" =~ 'your-string' ]] && ( doSomething )

Edit Dec 2023: resolves comment below, using anchors for exact match (a little less readable but fewer surprises):

#!/bin/bash

[[ "$*" =~ ^-disableVenusBld$ ]] && disableVenusBld=true
[[ "$*" =~ ^-disCopperBld$    ]] && disCopperBld=true
[[ "$*" =~ ^-disableTest$     ]] && disableTest=true
[[ "$*" =~ ^-disableUpdate$   ]] && disableUpdate=true
1
  • false positive an argument contains tested string as a substring, but not equal to it: xxx-disableVenusBld-xxx
    – belkka
    Dec 12, 2023 at 15:32
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.

You must log in to answer this question.

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