81

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
  • 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 '10 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. – Norman Gray Sep 9 '10 at 21:47
68

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)

| improve this answer | |
  • 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 Jan 22 '15 at 22:47
  • Here is a similar example with additional echo $1 | sed argument value processing. – Serge Stroobandt Jan 22 '15 at 22:57
42

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.

| improve this answer | |
  • 2
    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 '15 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. – Seppo Enarvi Jul 14 '15 at 8:37
  • 3
    Not working. bash -c 'echo args=$*; [[ "$@" == "bar" ]] && echo YES || echo NO' -- foo bar – Tobia Nov 4 '15 at 11:55
  • 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 Dec 19 '16 at 20:23
  • 4
    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. – Scott May 22 '19 at 19:46
9

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

| improve this answer | |
  • 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 May 30 '17 at 13:46
  • Could you explain what the second suggestion (especially ${@#) does? – velop Nov 14 '17 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 '17 at 20:23
  • @velop Learn more about Bash string manipulation here: tldp.org/LDP/abs/html/string-manipulation.html – Rich Nov 14 '17 at 20:29
  • @Rich pretty neat ^^, thx for the explanation. – velop Nov 17 '17 at 10:21
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.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.