74

I have an array of "options" of a command.

my_array=(option1 option2 option3)

I want to call this command in a bash script, using the values from array as options. So, command $(some magic here with my_array) "$1" becomes:

command -option1 -option2 -option3 "$1"

How can I do it? Is it possible?

4 Answers4

76

I would prefer a plain bash way:

command "${my_array[@]/#/-}" "$1"

One reason for this are the spaces. For example if you have:

my_array=(option1 'option2 with space' option3)

The sed based solutions will transform it in -option1 -option2 -with -space -option3 (length 5), but the above bash expansion will transform it into -option1 -option2 with space -option3 (length still 3). Rarely, but sometimes this is important, for example:

bash-4.2$ my_array=('Ffoo bar' 'vOFS=fiz baz')
bash-4.2$ echo 'one foo bar two foo bar three foo bar four' | awk "${my_array[@]/#/-}" '{print$2,$3}'
 two fiz baz three
manatwork
  • 31,277
  • If I understand correctly, this variable substitution can’t be wrapped into a function, nor be assigned to a variable, right? It needs to go directly into the command invocation. – Konrad Rudolph Feb 12 '19 at 19:00
  • 1
    @KonradRudolph, you can assign it to other variable as long as you do it as array assignment: somevar=("${my_array[@]/#/-}") (Note the parenthesis around the value.) Then of course you have to keep handling it as array when using it: echo "${somevar[@]}". Regarding the functions, there you are too limited by the language's possibilities. You can pass an array: somefunc "${my_array[@]/#/-}", then inside you will have it in $@: function somefunc() { echo "$@"; }. But same $@ will contain the other parameters too, if exists, and no other way to return the modified array than stdout. – manatwork Feb 13 '19 at 09:32
  • Odd enough, doesn't work with zsh. The first time I stumbled upon something that works in bash but not zsh. – Hi-Angel Aug 15 '19 at 14:19
  • @Hi-Angel, are you sure you used @ as array subscript? I gave it a test with zsh 5.5.1 and the only difference I noticed what zsh only prefixed each item when written as ${my_array[@]/#/-}, while bash did it for * subscript too. – manatwork Aug 15 '19 at 14:44
  • Oh, sorry, I might have screwed something, indeed works for me! – Hi-Angel Aug 15 '19 at 14:47
3

I would do something like this using a temp array in bash:

ARR=("option1" "option2" "option3"); ARR2=()
for str in "${ARR[@]}"; do 
   ARR2+=( -"$str" )
done

And then in the command-line:

command "${ARR2[@]}"
Kusalananda
  • 333,661
1

I didn't process that it was in an array and was thinking whitespace-separated in a string. This solution will work with that, but given that it's an array, go with manatwork's solution (@{my_array[@]/#/-}).


This isn't too bad with sed and a subshell. Just how easy the regex is depends on what you can guarantee about the options. If the options are all one "word" (a-zA-Z0-9 only), then a simple beginning word boundary (\<) will suffice:

command $(echo $my_array | sed 's/\</-/g') "$1"

If your options have other characters (most likely -), you'll need something a bit more complex:

command $(echo $my_array | sed 's/\(^\|[ \t]\)\</\1-/g') "$1"

^ matches the beginning of the line, [ \t] matches a space or tab, \| matches either side (^ or [ \t]), \( \) groups (for the \|) and stores the result, \< matches the start of a word. \1 begins the replacement by keeping the first match from the parens (\(\)), and - of course adds the dash we need.

These work with gnu sed, if they don't work with yours, let me know.

And if you'll be using the same thing multiple times, you may want to just calculate it once and store it:

opts="$(echo $my_array | sed 's/\(^\|[ \t]\)\</\1-/g')"
...
command $opts "$1"
command $opts "$2"
Kevin
  • 40,767
  • This didn't work with Mac OS X sed, so I needed to use gsed (I installed using homebrew). Another thing: instead of echo $my_array, I needed to do echo ${my_array[@]} to get all items from my_array: echo $my_array was giving me only the first element. But thanks for the answer and the explanation of the regex, specially about "word boundary", this is extremely useful. :) – Somebody still uses you MS-DOS Jan 20 '12 at 04:50
  • I would like to exit the script if the system's sed isn't compatible with this word boundary feature. How would I do it? Printing sed version and comparing it? From which version of sed this feature was added? – Somebody still uses you MS-DOS Jan 20 '12 at 05:00
  • 1
    @SomebodystillusesyouMS-DOS My first thought is to check directly whether it works - [ $(echo x | sed 's/\</y/') == "yx" ] || exit. – Kevin Jan 20 '12 at 05:09
  • This is great... we should test about capabilities and not about specific versions. This is great, thank you. – Somebody still uses you MS-DOS Jan 20 '12 at 05:17
  • 2
    This will break if any of the array elements contain special characters, most obviously and inexorably whitespace. – Gilles 'SO- stop being evil' Jan 20 '12 at 23:09
  • 1
    @SomebodystillusesyouMS-DOS Gilles is right, you should change your accept to manatwork. – Kevin Jan 21 '12 at 16:44
  • I did it, but thanks anyway for the suggestion and the functionality test. Your solution works, and in my scenario there wasn't elements with whitespace, but for the sake of other people searching this subject on internet, @manatwork's solution is easier, faster and only using bash. – Somebody still uses you MS-DOS Jan 23 '12 at 04:52
  • Will also break if my_array is an array, or if it's -n or -e. – Kusalananda Jul 07 '22 at 15:50
0
[srikanth@myhost ~]$ sh sample.sh 

-option1 -option2 -option3

[srikanth@myhost ~]$ cat sample.sh

#!/bin/bash

my_array=(option1 option2 option3)

echo ${my_array[@]} | sed 's/\</-/g'