7

I wanted:

#!/bin/bash
cmd --options \
    option=value,\
    option=value,\
    option=value,\
    option=value

But running with bash -x I got:

cmd --options option=value, option=value, option=value, option=value

That causes an error.

How can I do this so bash doesn't automatically place this blank space?

1
  • I am sorry, but I cannot tell what you are trying to do. If "cmd" is a bash script, you can give it options as arguments, they will be called $1, $2, $3 by default and can be renamed. If "cmd " is an external program, it probably has its own way of assigning options.
    – Wastrel
    Commented Nov 17, 2023 at 14:54

4 Answers 4

30

Well, you do have whitespace at the beginning of each line. This is not auto-removed by bash, although it is squeezed into one single space.

In order to completely avoid it, write the script as

#!/bin/bash
command --options \
opt1=val1,\
opt2=val2,\
opt3=val3
7
  • Nice, your explanation was good and I understood. Is there a way to do this while keeping the tabs?
    – rhuanpk
    Commented Nov 16, 2023 at 13:53
  • 10
    The spacing is autoremoved. Those blank characters are part of the shell syntax to delimit arguments that are to be passed to the command, they are not included in those arguments. xtrace shows spaces between those argument because xtrace shows shell code that could be used to run the same command, and one space separator is the shortest and simplest way to do it. Commented Nov 16, 2023 at 16:55
  • 1
    @PeterCordes, there's no word splitting here, just syntax tokenisation, only the Bourne shell did word (IFS) splitting on (some) literal words. Other Bourne like shells only do it on the result of expansions ($param, $((arith)), $(cmd)) as required by POSIX (while POSIX prohibits the splitting behaviour of the Bourne shell). Commented Nov 18, 2023 at 10:57
  • 1
    @PeterCordes, To put Stéphane's point in an example, in heirloom-sh, IFS=l; echo hello prints he o and you need to quote "hello" to protect it from splitting. The distinction is also important wrt. other syntax elements, like quotes. People tend to try to put to quotes inside variables and then are surprised it doesn't do what they want. (e.g. s='"foo bar" doo'; printf "%s\n" $s prints three lines and literal double-quotes.) Of course it doesn't work because word splitting is not the same as syntax processing.
    – ilkkachu
    Commented Nov 18, 2023 at 13:24
  • 1
    @PeterCordes, that page says the same thing as I did above. There is no word splitting here, because there's none of the expansions that word splitting applies to. Word splitting is a separate step that is done long after the shell code is tokenised (on whitespace, ;, |...) and parsed (into simple commands, complex structures, etc...). Commented Nov 18, 2023 at 17:56
26

If you need to pass a string like opt1=val1,opt2=val2,... as a single argument to a command, but want to have it in multiple lines in the script for editability, you could use an array as an intermediate step and then join all the array elements when running the command.

E.g.

opts=(
    opt1=val1    # no commas here 
    opt2=val2    # but comments also work
    etc.
)
(IFS=,
 echo somecmd --options "${opts[*]}"   # echo for demonstration
)

That prints somecmd --options opt1=val1,opt2=val2,etc.

(The first pair of parenthesis is part of the array assignment syntax, the second starts a subshell to shield the rest of the script from the modified IFS. That subshell might not be necessary, esp. if the script doesn't do anything afterwards, or if you just reset IFS later or don't use it at all.)

Obviously that requires a feature-rich shell with support for arrays. In addition to Bash, zsh would be fine. In ksh, you'd need to quote the strings (or at least the equal sign), so it doesn't turn it into a struct-like compound variable.


If you require a POSIX sh compatible solution, the nearest equivalent to an array would be the list of positional parameters, but there's only one set of them, and since set acts more or less like a regular command, you still need to remember the backslashes (and can't use comments).

Instead, something like this should work:

args=$( <<EOF sed -e 's/^[[:space:]]*//' | tr '\n' ,
    opt1=val1
    opt2=val2
    etc.
EOF
)
args=${args%,}
echo somecmd --options "$args"  # echo for demonstration
0
10

Another option:

#!/bin/bash
cmd --options "$(printf "%s" \
    "option=value," \
    "option=value," \
    "option=value," \
    "option=value"
)"

printf "%s" prints the arguments without any whitespace, so it results in:

cmd --options option=value,option=value,option=value,option=value

Some commands are also able to remove whitespace entirely on their own, provided it's passed as a single argument. So simple quoting (to prevent word splitting) can be enough, for example ffmpeg seems to be fine with this:

ffmpeg -i somefile.mp4 -vf "
    hflip,
    vflip,
    hue=s=0
" result.mp4

But that depends on how the command you're using handles these options.

Yet another option may be using --options itself multiple times (if your command allows that), in case of ffmpeg that doesn't work, but sometimes it's fine.

9

If you want to be able to put each options in its own line, you may want to try another approach:

options="opt1=val1,"
options+="opt2=val2,"
options+="opt3=val3"
your_command --options "${options}"
# note: "command" being a bash builtin, I replaced it by "your_command"
# (command foo : bypasses any alias/function "foo" and instead launches "foo" from $PATH)
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .