21
#!/bin/sh
echo "Noise $1"
echo "Enhancement $2"
for snr in 0 5 10 15 20 25
do
  python evaluate.py --noise $1 --snr 25 --iterations 1250 --enhancement $2
done

If $2 is not specified, I don't want to pass the --enhancement $2 argument to my python script. How would I do that?

Shamoon
  • 455

5 Answers5

33

Modifying your original script:

#!/bin/sh
echo "Noise $1"
echo "Enhancement $2"
for snr in 0 5 10 15 20 25
do
  python evaluate.py --noise "$1" --snr "$snr" --iterations 1250 ${2:+--enhancement "$2"}
done

The standard parameter expansion ${var:+word} will expand to word if the variable var is set and not empty. In the code above, we use it to add --enhancement "$2" to the command if $2 is available and not empty.

I've also taken the liberty to assume that what you are giving to --snr as an option-argument should be the loop variable's value.


My personal touch on the code (mostly just using printf rather than echo, avoiding long lines, and giving the code a bit more air):

#!/bin/sh

printf 'Noise %s\n' "$1"
printf 'Enhancement %s\n' "$2"

for snr in 0 5 10 15 20 25; do
    python evaluate.py \
        --noise "$1" \
        --snr "$snr" \
        --iterations 1250 \
        ${2:+--enhancement "$2"}
done

As mosvy points out in comments below: If your /bin/sh happens to be the dash shell, or some other shell that does not properly reset IFS as a new shell session starts (this is required by POSIX), and if you have, for one reason or other, exported IFS and given it a non-default value, then you may want to use unset IFS at the top of the above script.

Do that whenever you have fixed all other issues that exporting IFS doubtlessly have raised (don't export IFS).

Kusalananda
  • 333,661
  • You should also make sure IFS is at its default value. But a gotcha of this is that it will treat an empty $2 argument just like no argument at all. And ${2+... "$2"} instead of ${2:+... "$2"} will not cope with that either (as I had foolishly assumed minutes ago) –  Jan 19 '20 at 13:34
  • @mosvy Thanks. The shell will set IFS to the correct value, and the script is not changing it. As for the issue with $2, I'm assuming that an empty value is not something the user will want to pass on with the --enhancement long option. – Kusalananda Jan 19 '20 at 13:57
  • "The shell will set IFS to the correct value" will it? IFS=, sh -c 'v=a,b; echo $v' will print a b with the default /bin/sh on debian. As a matter of practicality, the OP could ... do the whole thing in python instead of bash. –  Jan 19 '20 at 15:13
  • @mosvy I'd consider that a bug in dash as POSIX actually says "The shell shall set IFS to when it is invoked." – Kusalananda Jan 19 '20 at 15:21
  • It's fixed in newer versions of dash, but that doesn't matter: it will take a while until eg. debian 10 gets eol'ed. And people copy shell snippets around, without regard whether they're intended to work as a standalone script. As it stands, your answer is broken: it should both explicitly unset IFS and also mention the empty $2 gotcha. –  Jan 19 '20 at 17:26
  • @mosvy The thing about $2 is already mentioned. – Kusalananda Jan 19 '20 at 17:32
15

On a minimal POSIX shell, that does not support any arrays, you can just manipulate the positional argument list with set command. Just before the for loop you can do

if [ -z "$2" ]; then
    set -- --noise "$1" --snr 25 --iterations 1250
else
    set -- --noise "$1" --snr 25 --iterations 1250 --enhancement "$2"
fi

and now run your for loop as

for snr in 0 5 10 15 20 25; do
    python evaluate.py "$@"
done

If your shell supports array bourne again shell bash, zsh or other, you can just use an array

argv=(--noise "$1" --snr 25 --iterations 1250)

if [ ! -z "$2" ]; then
    argv+=( --enhancement "$2" )
fi

and now call the script as

python evaluate.py "${argv[@]}"

As pointed in the comments, you are not using the actual value of snr value from the for loop. Use the value as $snr where applicable in the script arguments, in which case you might need to move the logic of constructing the argument list inside the loop.

Inian
  • 12,807
2

Build the argument in a variable based on the input:

#!/bin/sh
echo "Noise $1"
echo "Enhancement $2"

if [ $# -eq 1 ]
then
   enhance=""
else
   enhance="--enhancement $2"
fi

for snr in 0 5 10 15 20 25
do
  python evaluate.py --noise $1 --snr "$snr" --iterations 1250 $enhance
done

(Also includes the correction of the loop variable in the body of the loop)

2

You can do it in a single line by using command substitution

  python evaluate.py --noise $1 --snr 25 --iterations 1250 $(test -n "$2" && echo --enhancement "$2")
woodengod
  • 493
  • 1
    This fails if the value of $2 can be subject to word-splitting/pathname expansion after the command substation completes. – chepner Jan 21 '20 at 14:01
0

The only way to make a variable expansion disappear is by having the expansion expand to an empty value when that expansion is unquoted.

$ unset a; b=''; c=set
$ printf '<%s> ' unquoted $a $b quoted "$a" "$b" val $c "$c"; echo
<unquoted> <quoted> <> <> <val> <s> <t> <set>

Both unset and empty values of a variable (a and b) disappear (as arguments) when unquoted. Both are retained when quoted.

Furthermore, even spaces (or any character) will disappear if included in IFS.

$ IFS=' '
$ unset a; b=' '; c='  set  '
$ printf '<%s> ' unquoted $a $b quoted "$a" "$b" val $c "$c"; echo
<unquoted> <quoted> <> < > <val> <set> <  set  >

In fact, an IFS that contains characters in the value to expand (unquoted) will separate arguments.

$ IFS='e '
$ $ printf '<%s> ' unquoted $a $b quoted "$a" "$b" val $c "$c"; echo
<unquoted> <quoted> <> < > <val> <s> <t> <  set  >

Note the two arguments <s> and <t> which resulted from expanding the value set when IFS contained e.

So, we need an unquoted expansion of unset or empty values which becomes a quoted expansion of the value when there is a value to expand:

${var:+"$var"}

Description:

${             # starts an un-quoted variable expansion
  var          # name of variable to expand
     :         # also replace if var is null
      +        # use what follows if is not unset (nor null)
       "$var"  # a quoted variable expansion.
             } # end of expansion.

# In short: expand "$var" if it has a value, $var otherwise.

We can then use $1 value to either disappear it or expand it

echo ${1:+"--noise"} ${1:+"$1"}

The line above will either print two separate arguments (not affected by IFS) if $1 has some (not null) value or nothing if $1 is empty or unset.

The script will become:

#!/bin/sh
echo "Noise $1"
echo "Enhancement $2"

for    snr in 0 5 10 15 20 25
do     python evaluate.py \
         ${1:+"--noise"} ${1:+"$1"} \
         --snr "$snr" \
         --iterations 1250 \
         ${2:+"--enhancement"} ${2:+"$2"}
done

This solution is not affected by the value of IFS