how to read tab separated line into variables

Viewed 162

I wrote this function in zsh

function test() {
  test="a\tb\tc"

  while IFS=$'\t' read -r user host key; do
    echo "user: $user"
    echo "host: $host"
    echo "key: $key"
  done <<< "$test"
}

The output was:

user: a b   c
host:
key:

if instead of

... IFS=$'\t' read -r ...

I change it to

... IFS='\t' read -r ...

the output is

user: a
host:
key: b  c

Just what is going on?

I would like to read the tab separated line and set my variables accordingly.

1 Answers

Changing the double-quotes to $'...' (single quotes preceded by a $) could rescue for the variable $test:

test=$'a\tb\tc'

Here is the zsh manual for QUOTING (double-quoting and $'...'):

QUOTING
...
A string enclosed between $' and ' is processed the same way as the string arguments of the print builtin
...
Inside double quotes (""), parameter and command substitution occur, and \ quotes the characters \, `, ", $, and the first character of $histchars (default !).

--- zshmisc(1), QUOTING

For example:

  • "\$" -> $, "\!" -> ! etc.
  • "\t" -> \t (zsh does not recognize as tab this case), "\a" -> \a etc.

It does not treat the escape sequence \t as tab when it is used inside double quotes, so "a\tb\tc" does not mean "atabbtabc". (But things are a little more complicated: builtin echo recognizes the escape sequence \t.)

(1) ... IFS=$'\t' read -r ... (the original form)

Because expanding "$test" dose not have any tab characters, so read assigns the whole line to $user:

user: a b   c
host:
key:

(But echo recognizes the escape sequence \t as the tab.)

(2) ... IFS='\t' read -r ...

Again, expanding "$test" does not have any tab characters, so read separate the field by \ and t according $IFS. a\t\b\tc splits into a (to $user), \(separator), `` (empty to $host), t(separator), and the rest of the line (b\tc to $key):

user: a
host:
key: b  c

(But again, echo recognizes the escape sequence \t as the tab.)


Here is the code changed from test="..." to test=$'...':

function test() {
  test=$'a\tb\tc'

  while IFS=$'\t' read -r user host key; do
    echo "user: $user"
    echo "host: $host"
    echo "key: $key"
  done <<< "$test"
}
test

The output is:

user: a
host: b
key: c

PS: it is worth reading POSIX's Quoting specification, which is simpler than zsh's (https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02)

Related