I have a command that outputs 2 lines,

% ./run

so the output is one\ntwo\n

How can I assign variables first and second to the first and second lines respectively?

I know how I could do both by running the command twice:

% first="$(./run | head -n 1)"
% second="$(./run | tail -n 1)"

I don't want to run ./run twice, I only want to call it once (output might be different each time)

I could just store ./run's output in a variable, then operate on the variable, but can I avoid having this temporary variable?

More and more high-level languages are adding destructuring assignments:

first, second = (./run).split('\n')

Can I accomplish something similar in zsh?

1 Answer 1


Read each line with a separate call to read.

IFS= read -r first
IFS= read -r second

For example:

$ unset first second
$ printf 'one\ntwo\n' | { IFS= read -r first; IFS= read -r second; }
$ print $first
$ print $second

The above works only because zsh does not run the right hand side of the pipeline in a separate subshell.

Or, with a process substitution:

$ unset first second
$ { IFS= read -r first; IFS= read -r second; } < <( printf 'one\ntwo\n' )
$ print $first
$ print $second

See also Understanding "IFS= read -r line"

  • How bad is this code? I'm learning zsh: vars=(first second);c=1;for i in $(./cmd); do typeset "${vars[$c]}"="$i"; ((c++)); done. Feb 8, 2021 at 20:04
  • 1
    @schrodigerscatcuriosity I would call it mostly unreadable, and therefore unmaintainable, at least in a professional setting. I can see what it does, but I can immediately say that it's correct. Seeing as we loop over read all the time (while IFS= read -r line; do ...; done), I see no reason to not do an "unrolled loop" of two read calls when we know that we want to read exactly two values.
    – Kusalananda
    Feb 8, 2021 at 20:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.