17

Suppose I have a graphical program named app. Usage example: app -t 'first tab' -t 'second tab' opens two 'tabs' in that program.

The question is: how can I execute the command (i.e. app) from within a bash script if the number of arguments can vary?

Consider this:

#!/bin/bash tabs=( 'first tab' 'second tab' ) # Open the app (starting with some tabs). app  # ... How to get `app -t 'first tab' -t 'second tab'`? 

I would like the above script to have an effect equivalent to app -t 'first tab' -t 'second tab'. How can such a bash script be written?

Edit: note that the question is asking about composing command line arguments on the fly using an array of arguments.

    4 Answers 4

    19

    Giving the arguments from an array is easy, "${array[@]}" expands to the array entries as distinct words (arguments). We just need to add the -t flags. To do that, we can loop over the first array, and build another array for the full list of arguments, adding the -t flags as we go:

    #!/bin/bash tabs=("first tab" "second tab") args=() for t in "${tabs[@]}" ; do args+=(-t "$t") done app "${args[@]}" 

    Use "$@" instead of "${tabs[@]}" to take the command line arguments of the script instead of a hard coded list.

    Related: How can we run a command stored in a variable?

    2
    • 1
      This won't quote args that have spaces. It's not the same.
      – ygoe
      CommentedOct 24, 2019 at 20:27
    • 2
      @ygoe, I'm not sure what you're comparing against. Quoting really only means anything when a shell command line is first processed. After that, it's better to think of shell "words" or the arguments that go to the command the shell runs (shell "words" can contain whitespace, so it's a somewhat confusing term). The array expansion like "${args[@]}" expands each element of the array to a separate word/argument, so the above works like app -t "first tab" -t "second tab". If there's some corner case where it fails, please do tell.
      – ilkkachu
      CommentedOct 24, 2019 at 20:54
    6
    tabs=("-t" "one tab" "-t" "second tab") echo app "${tabs[@]}" app -t one tab -t second tab 

    So now you should convert your original array to array with "-t" flags. Hope it's not a problem at all.

      3

      It's easier with zsh:

      #!/bin/zsh - tabs=( 'first tab' 'second tab' ) app -t$^tabs 

      That calls app as if you had entered:

      app -t'first tab' -t'second tab' 

      rc, es and fish do that implicitly (zsh's $^array is actually inspired from rc's ^):

      In those shells,

      app -t$tabs 

      would do that as well. In zsh, you can get that behaviour as well by default by turning the rcexpandparam option on with set -o rcexpandparam.

      In ksh93, bash and zsh, you can also use the ${array[@]/pattern/replacement} operator from ksh93:

      #! /bin/bash - tabs=( 'first tab' 'second tab' ) app "${tabs[@]/#/-t}" 

      Where we replace the (zero-width) start of each element with -t, so in effect prepending -t to each.

      To call app as if with

      app -t 'first tab' -t 'second tab' 

      as opposed to

      app -t'first tab' -t'second tab' 

      That is where -t and first tab are two different arguments to app, with zsh:

      app ${${:--t}:^^tabs} 

      using the ${array1:^^array2}array-zipping operator where ${:--t} (minimal form of ${var:-default}) is used to inline the first array.

      2
      • Downvoted because the question is explicitely about Bash, so "it’s easier with [something that’s not Bash]" is not a valid answer.
        – bfontaine
        CommentedFeb 22, 2024 at 18:35
      • 1
        @bfontaine, I've now added an approach which also works in bash for those people coming here and are constrained to using bash. In any case, note that answers here are not intended only for the OP, but anyone with similar requirement and not everyone is stuck with bash, you'll find that giving alternatives is generally encouraged rather than frowned upon.CommentedFeb 23, 2024 at 7:53
      -3

      Basically the following bash script will read the file which include sites line by line and open each site in a new tab.

      #!/bin/bash while read line do xdg-open "$line" done < /root/file 

      Usage

      ./script.sh sites

      2
      • 1
        Ah but you are running the command (xdg-open in your case) multiple times. I would like the command to be run only once.
        – Flux
        CommentedDec 23, 2017 at 8:44
      • 1
        The question is about getting the arguments from an array, not a file.
        – Barmar
        CommentedDec 23, 2017 at 11:43

      You must log in to answer this question.

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.