how to put all command arguments in one variable
Asked Answered
M

2

2

I want to execute a shell script that require 3 arguments.

The argument number 2 contains a string with space

I want to put all arguments in one variable like this:

Linux:~# kk="\"111\" \"222 222\" \"333\""
Linux:~# echo $kk
"111" "222 222" "333"

Now If I call a function:

func() {
  echo ---$1---
  echo ---$2---
  echo ---$3---
}

with the $kk variable in this way

func $kk

Then it will return

Linux:~# func $kk
---"111"---
---"222---
---222"---

And I was expecting to get this result

---111---
---222 222---
---333---

How to solve this issue without using eval ?

I know that the eval solve this issue but I do not want to use it (since it takes time if I execute many time a such call).

Mako answered 16/2, 2015 at 13:48 Comment(8)
You can't do this without eval. See mywiki.wooledge.org/BashFAQ/050 for details.Runoff
It can be done with arrays if targeting bash rather than POSIX (why is this tagged with two different shells?)Subassembly
BTW, performance is a relatively minor reason to avoid eval -- the potential security impact is a much larger concern.Subassembly
...even in ash, where there aren't proper arrays supported, this can be done using a function's command line argument as temporary safe array storage (or completely overriding the global "$@", if one doesn't need it and doesn't want to use a function for scoping).Subassembly
OP, you originally tagged the question bash. Do you want a bash answer or a POSIX shell solution?Ascension
Putting more than one argument in a single scalar variable, by the way, is impossible (without explicit parsing) for a simple reason: A scalar variable can store any value other than NUL. An argument can likewise store any value other than NUL. Thus, there's no way to tell if "111" "222 222" "333" is meant to be exactly one argument containing literal quotes, four arguments containing literal quotes but split on whitespace, or three arguments with no quotes -- but imposing parsing rules on expansion would be a rat's nest of security bugs, so Not Doing It is safer, thus shells behave thus.Subassembly
(indeed, the existing simple string-splitting and glob matching done on unquoted expansions are confusing enough, and it's understandable why zsh chooses to break POSIX by not doing them by default).Subassembly
"I want to put all arguments in one variable like this"... No, you don't. Really. What you want to do is redesign the assumptions being made elsewhere that make you think you want to do this...Monteith
A
3

OP originally tagged question , then removed the tag. Please refer to Charles Duffy's great answer for a POSIX shell solution! the solution below uses arrays and is only suitable for .


You should not put your data in a string like this, but in an array. By the way, you're missing some quotes in your function:

func() {
    echo "---$1---"
    echo "---$2---"
    echo "---$3---"
}

or better yet (printf is preferable to echo):

func() {
    printf -- '---%s---\n' "$1"
    printf -- '---%s---\n' "$2"
    printf -- '---%s---\n' "$3"
}

$ kk=( "111" "222 222" "333" )
$ func "${kk[@]}"

will happily print:

---111---
---222 222---
---333---

You can even include newlines and such in your arguments:

$ kk=( $'one\none' "222 222" $' *   three\nthree' )
$ func "${kk[@]}"
---one
one---
---222 222---
--- *   three
three---
Ascension answered 16/2, 2015 at 13:56 Comment(3)
Hey wait, the bash tag disappeared all of a sudden from the question... this works with bash!Ascension
yes, I removed. Sorry to put it at the beginning, but I was looking for solution for ashMako
The meat (up until the array demo) is perfectly portable to other Bourne-family shells.Kostman
S
3

If features (like arrays) didn't make bash more expressive than POSIX sh, there would have been no reason to add them. :)

That said, you can work around it by overriding "$@" for your needs:

set -- 111 "222 222" 333
printf '%s\n' "$@"

...will print...

111
222 222
333

If you need to build the arguments list step by step, you can make several calls to set with "$@" as the first arguments (make sure you observe the quotes!):

set -- 111
set -- "$@" "222 222"
set -- "$@" 333
printf '%s\n' "$@"

...will print...

111
222 222
333
Subassembly answered 16/2, 2015 at 14:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.