56

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?

3
  • 1
    So you want to add - to the beginning of each word in my_array? – Kevin Jan 20 '12 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. – Somebody still uses you MS-DOS Jan 20 '12 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 '12 at 6:29
57

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. – 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 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 '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
2

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"
7
  • 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 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? – Somebody still uses you MS-DOS Jan 20 '12 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 '12 at 5:09
  • 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
1
[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'

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.