Ross MacArthur
Ross MacArthur

Reputation: 5449

Zsh read output of command into array splitting on newline

I have a command that outputs a bunch of stuff e.g. running mycmd will give:

foobar
derp derp
like so
etc

Some of these lines will have spaces in them.

How do I read these into an array in zsh such that ${arr[1]} gives foobar, ${arr[2]} gives derp derp etc.

I have tried something like but it seems to split the array on chars not newlines.

IFS=$'\n' read -d '' -r arr <<< "$(mycmd)"

i.e. ${arr[1]} gives f when it should give foobar

Upvotes: 11

Views: 9001

Answers (2)

shadowtalker
shadowtalker

Reputation: 13853

I'm not sure exactly why the read usage in the original question didn't work. It's possibly related to mixing <<< and $(). Or maybe the user just had a messed up shell session. Or maybe it was a bug in an older version of Zsh.

In any case, it has nothing to do with the behavior of the read builtin, and the original proposal was very close to correct. The only problem was using <<< $(...) instead of a plain pipe, which should just be a stylistic goof (rather than an error).

The following works perfectly fine in Zsh 5.8.1 and 5.9:

function mycmd {
  print foobar
  print derp derp
  print like so
  print etc
}

typeset -a lines
mycmd | IFS=$'\n' read -r -d '' -A lines

echo ${(F)lines}

You should see:

foobar
derp derp
like so
etc

I prefer this style, instead of ( $(...) ). Not requiring a subshell is useful in many cases, and the quoting/escaping situation is a lot simpler.

Note that -d '' is required to prevent read from terminating at the first newline.

You can wrap this up in a function easily:

function read-lines {
    if (( $# != 1 )); then
        print -u2 'Exactly 1 argument is required.'
        return 2
    fi

    local array="${1:-}"
    read -r -d '' "$array"
}

Upvotes: 1

Ross MacArthur
Ross MacArthur

Reputation: 5449

Okay its actually very simple:

IFS=$'\n' arr=($(mycmd))

Upvotes: 19

Related Questions