Bash – Capture in shell variables the arguments after a command (between brackets)

bashgrepregular expressionsed

Suppose I have a file that contains, among many other things,

\command{arg1,arg2,arg3}

(arguments been paths, expressed with /, ., characters and numbers)

But that a user can as well call it with

\command{arg1,
arg2 ,
arg3
}

That is, on several lines and with superfluous spaces.

I'd like to find a regular pattern to include in a shell script so that n variables will contain the n arguments.
How to proceed ?


I managed to write

echo "\command{arg1,
    arg2 ,
    arg3
    }" | sed -n -e 's/\\command//p' | sed 's/,/\n/' | sed 's/{\|}//'

but that only outputs arg1, and I'm not even sure on how to store it in a variable.

Related:

But I was not able to combine all those ingredients to get what I want.

Best Answer

I'd like to find a regular pattern to include in a shell script so that n variables will contain the n arguments

The following creates a shell array arglist that contains each of the arguments:

$ readarray -t arglist < <(echo "\command{arg1,
    arg2 ,
    arg3
    }" | sed -n '/\\command/{ :a;/}/!{N;b a}; s/\\command{//; s/[ \n}]//g; s/,/\n/g; p}')

By using the declare statement, we can see that it worked:

$ declare -p arglist
declare -a arglist='([0]="arg1" [1]="arg2" [2]="arg3")'

Here is another example with the arguments on one line:

$ readarray -t arglist < <(echo "\command{arg1, arg2, arg3, arg4}"  | sed -n '/\\command/{ :a;/}/!{N;b a}; s/\\command{//; s/[ \n}]//g; s/,/\n/g; p}')

Again, it works:

$ declare -p arglist
declare -a arglist='([0]="arg1" [1]="arg2" [2]="arg3" [3]="arg4")'

Note that the space in < <( is essential. We are redirecting input from a process substitution. Without the space, bash will try something else entirely.

How it works

The sed command is a bit subtle. Let's look at it a piece at a time:

  • -n

    Don't print lines unless explicitly asked.

  • /\\command/{...}

    If we find a line that contains \command, then perform the commands found in the braces which are as follows:

  • :a;/}/!{N;b a}

    This reads lines into the pattern buffer until we find a line that contains }. This way, we get the whole command in at once.

  • s/\\command{//

    Remove the \command{ string.

  • s/[ \n}]//g

    Remove all spaces, closing braces, and newlines.

  • s/,/\n/g

    Replace commas with newlines. When this is done, each argument is on a separate line which is what readarray wants.

  • p

    Print.

Related Question