My script takes arbitrary number of arguments and also few options:

I need to extract any options plus the first argument into one string, and any remaining arguments into a second string. ie:

./script.sh foo FILE1                     [s1="foo"  s2="FILE1]"
./script.sh foo FILE1 FILE2 FILE3         [s1="foo"  s2="FILE1 FILE2 FILE3]"

./script.sh -i -l foo FILE1               [s1="-i -l foo"  s2="FILE1]"
./script.sh -i -l foo FILE1 FILE2 FILE3   [s1="-i -l foo"  s2="FILE1 FILE2 FILE3]"

I just need to split $@ into these two strings. I do't need to process the arguments, ie with getopt.

What is the easiest way to do this?

EDIT: extracting into arrays arrays rather than strings is fine.

  • 2
    It would be easier to let getopts parse the options out. Also, you would quite possibly be much better served by extracting things into arrays rather than strings, especially if you want to actually use the individual options and/or arguments later. – Kusalananda Jan 13 at 6:47
set -o extendedglob

first=${(j[ ])first_set}

would store in $first the concatenation of all arguments up to the first that doesn't start with - with one space character in between.

Then for $second, you can get the rest:

second_set=("$@[$#first_set + 1, -1]")
second=${(j[ ])second_set}

In any case, note that $@ is not a string, it's a list of 0 or more strings.

For instance, if you invoke your script from a Bourne like shell using a command line such as:

script.sh -i   -l 'foo bar'   'File 1' File\ 2   "File 3"

That executes your script as:

execve("/path/to/script.sh", ["script.sh", "-i", "-l", "foo bar",
                              "File 1", "File 2", "File 3"], environ)

Which becomes (assuming the script starts with #! /bin/zsh -):

execve("/bin/zsh", ["/bin/zsh" /* or "script.sh" depending on the system*/,
                    "-", "/path/to/script.sh", "-i", "-l", "foo bar",
                    "File 1", "File 2", "File 3"], environ)

And in your script, $@ will contain all those strings in the argv[] argument to execve() that come after /path/to/script.sh.

Above we split that list into two sets ($first_set and $second_set array variables), and then join the arguments in those sets into two scalar variable ($first and $second). But after that joining is done, you can no longer get back to the original list of arguments. For instance, $second in that case will contain File 1 File 2 File 3 and there's no way to tell which of those space characters are the ones that delimit arguments and which ones were part of the arguments.

  • Without actually parsing the options, there is no way to divide the sets up in the case that the initial options are -l foo -i rather than -i -l foo (assuming -l takes an option argument). – Kusalananda Jan 13 at 8:19
  • @Stéphane Chazelas - that's perfect. thank you. – 400 the Cat Jan 13 at 8:35

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.