5

NB: I've read the answers to other similar questions (How to split the output and store it in an array?, How to split a string into an array in bash), but none of them answer the specific question I'm asking here.


Suppose that the output of some command foo is a single line consisting of 10 tab-separated fields. Then

tmpvar=$( foo )
fields=( ${(ps:\t:)tmpvar} )
echo $#fields
# 10

Q: How can I achieve the same splitting of foo's output on tabs without needing an intermediate assignment to tmpvar?


FWIW, this does not work:

fields=( ${(ps:\t:)$( foo )} )
echo $#fields
# 1
kjo
  • 15,339
  • 25
  • 73
  • 114

3 Answers3

4
$ printf '<%s>\n' "${(@ps:\t:)$(printf 'foo bar\t\tbaz')}"
<foo bar>
<>
<baz>

Or if you do want to suppress the empty elements like in your tempvar approach:

$ printf '<%s>\n' ${(ps:\t:)"$(printf 'foo bar\t\tbaz')"}
<foo bar>
<baz>

IOW, you need to quote the command substitution. Otherwise the s:\t: applies on the joining (with the first character of $IFS) of the fields resulting of the $IFS-splitting of the command substitution.

Alternatively, you could set $IFS to the empty string or to a single non-whitespace character.

$ (IFS=; printf '<%s>\n' ${(ps:\t:)$(printf 'foo bar\t\tbaz')})
<foo bar>
<baz>
4

You should quote the $( foo )

fields=( ${(ps:\t:)"$( foo )"} )
echo $#fields

You can find here some nice examples

Yaron
  • 4,289
0
IFS=" "; set -f
a=( $(echo -e "a b\tc d") )
printf '<%s>\n' "size=${#a[@]}" "${a[@]}"
  • 1
    set -f does something else in zsh (and command substitution doesn't undergo globbing there). In zsh like in ksh93, if you want word-splitting on TAB while preserving empty elements, you can use IFS=$'\t\t'. Using the s variable expansion flag is a lot better than changing a global setting IMO. Also note that the -e is implicit in zsh. – Stéphane Chazelas Apr 18 '17 at 13:14