66

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?

5
  • 1
    So you want to add - to the beginning of each word in my_array?
    – Kevin
    Jan 20, 2012 at 3:19
  • @Kevin: Yes, and execute it. All lines are inside the same bash script. I'm asking this because these same options are going to be used in a lot of places inside the script, so instead of copying all of them around, I thought about an array. Jan 20, 2012 at 3:43
  • 1
    It might seem pointless but if you are making big shell scripts maybe you should look into perl, this kind of thing is very easy to do there.
    – alfa64
    Jan 20, 2012 at 6:29
  • For background, see But what if …? Jul 8 at 3:05

4 Answers 4

68

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
5
  • 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. Feb 12, 2019 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, 2019 at 9: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, 2019 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, 2019 at 14:44
  • Oh, sorry, I might have screwed something, indeed works for me!
    – Hi-Angel
    Aug 15, 2019 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[@]}"
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"
8
  • 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. :) Jan 20, 2012 at 4: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? Jan 20, 2012 at 5:00
  • 1
    @SomebodystillusesyouMS-DOS My first thought is to check directly whether it works - [ $(echo x | sed 's/\</y/') == "yx" ] || exit.
    – Kevin
    Jan 20, 2012 at 5:09
  • 2
    This will break if any of the array elements contain special characters, most obviously and inexorably whitespace. Jan 20, 2012 at 23:09
  • 1
    @SomebodystillusesyouMS-DOS Gilles is right, you should change your accept to manatwork.
    – Kevin
    Jan 21, 2012 at 16:44
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'
1
  • Try with the first array element being n or e.
    – Kusalananda
    Jul 7 at 15:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.