1

The following snippet behaves exactly as I want it to:

array=( "one" "" "three" ); echo "${array[@]}"; two=${array[1]}; echo two=$two

It returns:

one  three
two=

So the variable $two is assigned to be empty. Perfect. Now let's try to capture some output from another script, here, we'll say the "script" is a simple echo.

array=( $( echo "one" "" "three" ) ); echo "${array[@]}"; two=${array[1]}; echo two=$two

This returns:

one three
two=three

Uh oh. But this one is simple, echo is killing the quotes as it processes its arguments. Let's protect those:

array=( $( echo \"one\" \"\" \"three\" ) ); echo "${array[@]}"; two=${array[1]}; echo two=$two

Now we get:

"one" "" "three"
two=""

So instead of getting an empty variable, all the variables are wrapped in quotes even after the echo. These quotes are very annoying to remove (you can use eval or sed or something but speed is critical for this application).

So why does:

array=( "one" "" "three" ); echo "${array[@]}"; two=${array[1]}; echo two=$two

behave differently from: array=( $( echo \"one\" \"\" \"three\" ) ); echo "${array[@]}"; two=${array[1]}; echo two=$two

even though:

$( echo \"one\" \"\" \"three\" )

returns:

"one" "" "three"

Or, more generally, what is the appropriate output of a script such that it can be passed into an array? (Or should I just create a function within the "outer" script that calls the "inner" script as a function where it can easily get the array returns).

Thanks

  • Do you want this behaviour for a script, or a function within a script? – Benjamin W. Oct 16 '17 at 15:43
  • Is there a reason you're choosing this serialization format? The easiest way to serialize an arbitrary shell array (that doesn't require content escaping/unescaping or a delimiter character to be disallowed from use in content) is as a NUL-delimited stream. – Charles Duffy Oct 16 '17 at 19:41
  • "*echo is killing the quotes as it processes its arguments*" - this isn't really accurate - `echo` prints its arguments, separated by spaces. The existence of quotes is irrelevant (other than that it denotes an empty-stringed argument) as far as `echo` is concerned - it's Bash that converts `""` into an empty-string argument. That's also why escaping the quotes doesn't do what you want - now the second argument to `echo` is a literal `""` and will be printed as such, not parsed by Bash. – dimo414 Oct 16 '17 at 19:42

2 Answers2

0

It's the command-line parsing dynamics. Try it with an eval, this way -

 eval "array=( $( echo \"one\" \"\" \"three\" ) )"
 echo "${array[@]}"
 two=${array[1]}
 echo two=$two

This will evaluate the "" rather than assigning it as a string, and assigned the thus-evaluated ~empty~ into the array position.

Paul Hodges
  • 13,382
  • 1
  • 17
  • 36
0

You can try like that.

OLDIFS=$IFS;IFS=',';array=($(echo 'one','','three')); IFS=$OLDIFS;echo "${array[@]}"; two=${array[1]}; echo two=$two
ctac_
  • 2,413
  • 2
  • 7
  • 17
  • ...with the caveat that you can no longer have commas as a valid value within the strings in your list. – Charles Duffy Oct 16 '17 at 19:42
  • And *if* you wanted to read separated by commas, why not `IFS=, read -r -a array`? That avoids the bugs you've got here, where a `*` in your list gets replaced with a list of filenames in the current directory. – Charles Duffy Oct 16 '17 at 19:45
  • Ok, the answer is not perfect. It's to tell to op that the problem is'nt the array but the function or command $(...). If this one return 3 fields, they must be separated by something. – ctac_ Oct 16 '17 at 20:07
  • "Must" is too strong. It's *possible* to parse the original input without `eval` -- see the linked duplicate question. But if we're going to be teaching a practice (and teaching is what StackOverflow is here for), we should be either avoiding practices with caveats or calling them out. – Charles Duffy Oct 16 '17 at 20:09