35

I have a script running on Linux that accepts some parameters.
I would like to do something like:

if [[ $CONDITION == "true" ]]; then  
  script param1 --param2  
else
  script param1  
fi 

I would like to avoid the forking path of the if.
Is there a more optimal way to pass the second parameter?

terdon
  • 242,166
Jim
  • 1,391
  • 1
    Where is this if? In the script itself? In another script that calls the target script? Are these options (--param2) or arguments (param1)? Please [edit] your question and give us more details. – terdon May 30 '18 at 08:17
  • One other note: true is a shell builtin that returns a success exit status. If you use CONDITION=false when CONDITION is not true, then you could write: if "$CONDITION"; then echo yes; fi or "$CONDITION" && echo yes – glenn jackman May 30 '18 at 19:21
  • @glennjackman:The true is not the same as "true" right? – Jim May 30 '18 at 20:40
  • They are the same: https://www.gnu.org/software/bash/manual/bash.html#Quote-Removal -- you can execute the command "echo" "foo" with no difficulty because the shell will remove the quotes before executing the command. – glenn jackman May 30 '18 at 20:44
  • For example I often do valid=false if someCondition; then valid=true; fi; if ! $valid; then echo Invalid; exit 1; fi – glenn jackman May 30 '18 at 20:47
  • @glennjackman:Sorry I didn't understand. Is my condition always going to be true because of using the true? I just need a boolean indicator – Jim Jun 09 '18 at 15:01
  • true and false are commands built into the shell. Their only purpose is to return a success or failure exit status. So if $valid; then echo true; else echo false; fi where "valid" holds either "true" or "false" will execute the expected branch of the if statement. Therefore, the example I showed is a boolean indicator. – glenn jackman Jun 09 '18 at 15:10
  • @glennjackman: But when I test the if statement in my post it works. I mean my understanding is that it compares with the string "true" – Jim Jun 09 '18 at 20:58
  • Oh, I missed your point. Yes, your code is fine. I was just pointing out the true and false commands could be used. – glenn jackman Jun 09 '18 at 23:10

3 Answers3

57

The most expansible and robust way would probably be to use an array to hold the optional parameter(s):

params=()
if [[ $CONDITION == true ]]; then
    params+=(--param2)
fi
script param1 "${params[@]}"

Or in shorthand:

[[ $CONDITION == true ]] && params+=(--param2)
script param1 "${params[@]}"

That avoids repeating the constant part of the command and you can put more than one argument in the array, even the whole command.

Note that it's important to do this with an array: if you replace the array with a regular variable (params="--param2"; script param1 $params) you'll either have to expand the variable unquoted, with all the problems that brings, or expand it quoted, in which case you'll pass an empty string as argument if the variable is empty.

In a simple case like this, the "alternate value" expansion can also be used:

cond=x
p2="--param2"
script param1 ${cond:+"$p2"}

Here, if cond is nonempty (regardless of if it's cond=false or cond=0 instead of cond=true), the value of p2 is expanded. This may be seen as less ugly than arrays, but be careful with the placement of the quotes.

See also:

ilkkachu
  • 138,973
  • it should be noted that this is passing in part the literal string --, and not the unary -- operator – Zombo May 30 '18 at 18:22
  • 1
    @StevenPenny, well, the whole idea of a -- operator only exists in the shell within arithmetic expansions, and there aren't any here. I can't think of any special meaning for the dash in the shell outside of them, either. – ilkkachu May 30 '18 at 18:39
  • @ilkkachu how can you do this in one line? script param1 ${[[ $CONDITION == true ]] && params+=(--param2)} ? – Turnipdabeets Apr 15 '20 at 16:37
  • @Turnipdabeets, do you mean something like this? par="x y"; printf ":%s\n" a b ${par+"$par"} It should expand par as another argument after b, if par is set (i.e. unset par; printf ":%s\n" a b ${par+"$par"} there would not be an empty argument after b). Or you could have ${cond:+"$par"} to expand par if cond is non-empty. – ilkkachu Apr 17 '20 at 17:40
  • Is there a way to make it work with set -o nounset when the params variable is empty ? I'm getting "unbound variable" errors. EDIT : use "${params[@]-}" with a dash at the end prevents the problem – Cyril Duchon-Doris Nov 29 '22 at 13:10
9

A versatile way to do this is to set the arguments in an array. The most basic array is the list of positional parameters defined with set. You can build the list of parameters in sequence.

set -- param1
if [[ $CONDITION == "true" ]]; then  
    set -- "$@" --param2  
fi 

command "$@"  

Which could be reduced to:

set -- param1
[[ $CONDITION == "true" ]] && set -- "$@" --param2
command "$@"  

If the list of positional parameters needs to be preserved, then either:

  • Use a function:

    callcommand(){     set -- param1
                       [[ $CONDITION == "true" ]] && set -- "$@" --param2
                       command "$@"       
                 }
    callcommand
    
  • Or use some other array variable:

    paramArray=()
    paramArray+=(param1)
    [[ $CONDITION == "true" ]] && paramArray+=( "--param2" )
    command "${paramArray[@]}"
    
  • I don't really understand this. param1 is already a parameter. Does this change the parameter array of the script? – Jim May 31 '18 at 20:48
  • @Jim No, param1 is not an argument of the script code above, it become a parameter of the command called. Yes, the positional parameters are changed (as they are used). See some alternatives in the edited answer. –  Jun 01 '18 at 06:53
-1
PARAMS+=" param1"
if [[ $CONDITION == "true" ]]; then  
  PARAMS+=" --param2"
fi
script ${PARAMS}
John Doe
  • 423