1

I've got a shell script with a section like this:

if [ "$condition" -eq 1 ]; then
    external_command -argument value1
else
    external_command -argument value1 -second_argument value2
fi

I was bugged by this repetition, so I tried this:

arg="-second_argument"
val="value2"
if [ "$condition" -eq 1 ]; then
    arg=""
    val=""
fi
external_command -argument value1 "$arg" "$val"

It didn't work because the external_command still gets the empty strings as distinct arguments and complains.

Is there a way to do this without building the command line repeatedly? In my actual code there are 4 different conditions, so there's a lot of needless repetition.

If I were using Bash, I'd follow Bash FAQ 050 and build an array for the command arguments, but I'm not.

4
  • 1
    If you are sure that val does not contain spaces, you could simply leave out the quotes, but in general, I don't think this can be done in Pure Posix shell, without reverting to dirty tricks using eval. Commented Apr 12, 2018 at 6:43
  • I definitely don’t want to look at eval! Sounds like I’m stuck with repeating myself. Or making a Bash script! Commented Apr 13, 2018 at 2:12
  • A wise choice. Another possibility is to switch to Zsh, where you can do it without the array trick which is necessary in bash. I personally prefer Zsh over bash, but I am aware that there are also many arguments in favour to bash. Commented Apr 13, 2018 at 6:46
  • If switching shells is an option, it doesn't really matter if you use bash or zsh, as both have arrays. Commented Apr 13, 2018 at 19:02

3 Answers 3

2

The standard workaround is to use the positional parameters in place of an array. Assuming you don't care about the current values:

shift $#  # Clear them; optional, as the next line overwrites whatever was there
set -- -argument value1
if [ "$condition" -eq 1 ]; then
  set -- "$@" "-second_argument"  "value2"
fi
external_command "$@"

If you do care about the current values, run the block in a subshell:

(
  set -- -argument value1
  if [ "$condition" -eq 1 ]; then
    set -- "$@" "-second_argument"  "value2"
  fi
  external_command "$@"
)

Or, define a function, which already has its own set of positional parameters.

run_external () {
  condition=$1
  set -- -argument value1
  if [ "$condition" -eq 1 ]; then
    set -- "$@" -second_argument value2
  fi
  external_command "$@"
}

run_external "$condition"
Sign up to request clarification or add additional context in comments.

3 Comments

So when I pass $@ as an argument to the command, it's actually seen as individual arguments?
Yes; that's the difference between $@ and $*. When quoted, "$@" expands to 0 or more distinct words, one per positional argument, while "$*" expands to exactly one (possibly empty) word.
To be clear, $@ itself isn't passed as an argument; its expansion produces 0 or more arguments that are passed.
1

If you populate a variable whenever the second condition should be given, and leave it unset otherwise, you have another POSIX-compliant option:

# have a variable set ONLY IF you want the extra arguments passed
if [ "$condition" -eq 1 ]; then
  unset want_extra_args
else
  want_extra_args=1
fi

# and put those arguments in a ${var+val} expansion
external_command -argument value1 ${want_extra_args+-second_argument value2}

You can use quotes and other expansions -- which is to say, the following is valid:

external_command ... ${want_extra_args+-second_argument "$value2"}

2 Comments

Nice, I thought a lot of those more esoteric expansions were Bash-specific. Apparently not!
A lot of them are; just got lucky that this particular one that you need isn't.
-1

Just try

second=
if [ "$condition" -ne 1 ]; then
    second="-second_argument value2"
fi
external_command -argument value1 $second

Note the double quotes / absence of them. Although user1934428 is right, if you have spaces in value of -second_argument, you might have problems.

3 Comments

Yes, there are spaces unfortunately!
Not just spaces, but anything that would contribute to word-splitting (based on the value of IFS) or anything that could be expanded as a pattern.
See BashFAQ #50 for a detailed explanation of why this practice is generally faulty, and various alternatives thereto.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.