124

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

6 Answers 6

98

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
  • 8
    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 Commented Jan 22, 2015 at 22:47
  • Here is a similar example with additional echo $1 | sed argument value processing. Commented 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 Commented May 25, 2022 at 10:22
95

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.

11
  • 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. Commented 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. Commented Jul 14, 2015 at 8:37
  • 3
    Not working. bash -c 'echo args=$*; [[ "$@" == "bar" ]] && echo YES || echo NO' -- foo bar Commented Nov 4, 2015 at 11:55
  • 9
    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. Commented May 22, 2019 at 19:46
  • 14
    Note for noobs like me: the YOURSTRING might be quoted, but the asterisks must be OUTSIDE the quotes, e.g. if [[ "$*" == *"my search string"* ]] Commented 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. Commented May 30, 2017 at 13:46
  • Could you explain what the second suggestion (especially ${@#) does? Commented 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! Commented Nov 14, 2017 at 20:23
  • @velop Learn more about Bash string manipulation here: tldp.org/LDP/abs/html/string-manipulation.html Commented Nov 14, 2017 at 20:29
  • @Rich pretty neat ^^, thx for the explanation. Commented Nov 17, 2017 at 10:21
8
[[ "$@" =~ '-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 Commented Dec 12, 2023 at 15:32
5
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.

0

If your arguments do not contain newlines:

printf '%s\n' "$@" | grep -Fqx -- "YOUR_STRING" 

Explanation:

  • The printf command will print the remaining arguments in a certain format. Specifically, delimiting the arguments with a newline \n.
  • grep goes line-by-line and searches for your string.
  • -F or --fixed-strings means treating the search pattern as a raw string (not a regex)
  • -q or --quiet means quiet searching. Only succeeds or fails but doesn't produce output.
  • -x or --line-regexp means that the search pattern is the whole line, so that you wouldn't find Barcelona when searching for on. (The name is confusing as we're not using regex's, yes, but it's correct.)

Example:

arr=(1 2 3 a b c) printf '%s\n' "${arr[@]}" 1 2 3 a b c printf '%s\n' "${arr[@]}" | grep -Fqx -- "1" # succeeds printf '%s\n' "${arr[@]}" | grep -Fqx -- "2" # succeeds printf '%s\n' "${arr[@]}" | grep -Fqx -- "3" # succeeds printf '%s\n' "${arr[@]}" | grep -Fqx -- "4" # fails printf '%s\n' "${arr[@]}" | grep -Fqx -- "a" # succeeds 

Credits:

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.