6

I wanted to extract 3 words from a variable in 3 different variables in ksh script. I used this line on ksh93 and it worked fine:

read A B C <<< $line

Got this error for the above command while running it on ksh88:

syntax error at line ## : '<' unexpected

How to perform the same action on ksh88?

13

No, here strings are not available on ksh88 and pdksh. On the more recent ksh93 (original AT&T Korn Shell) and mksh (currently actively developed pdksh derivative) it is, however, available.

<<< is one of the “modern” shell extensions shared between ksh93, mksh, GNU bash and zsh.

Your specific problem…

read A B C <<< $line

… can be worked around with this (Korn shell):

print -r -- $line |&
read -p A B C

You can also use this (POSIX shell), it has tmpfile performance penalty though (on the other hand, <<< likely also has that):

read A B C <<EOF
$line
EOF

If you just want to split words, though:

set -A arrname -- $line

Then use ${arrname[0]} instead of $A and ${arrname[1]} instead of $B. Only it will not stop at splitting at three elements, so if $line is "foo bar baz bla", $C would contain “baz bla”, whereas ${arrname[2]} has “baz” and ${arrname[3]} has “bla”.

If you don’t need your positional parameters, though, you can do

set -- $line
A=$1; shift
B=$1; shift
C=$*

The shift will cause errors if $line has fewer than three words, though (check $# if you’re not sure, or use [[ $line = *' '*' '[! ] ]] (likely slower though) to check first).

Mind that set … $line will also do globbing (thanks Stéphane for reminding us), so you need set -o noglob before (and possibly restore the previous state afterwards, usually set +o noglob).

Full disclosure: I’m the mksh developer.

  • 4
    set -- $line does globbing in addition to splitting though. Use set -o noglob to disable it. – Stéphane Chazelas Jun 18 '18 at 12:49
  • 1
    Doesn't <<< have tmpfile performance penalty in quite a few shells, too? (And isn't it still smaller than the fork penalty of |&?) – grawity Jun 18 '18 at 16:12
  • Was going to say -- I'm pretty sure <<< has the tempfile penalty in bash (and that, there, it is indeed cheaper than the cost of a pipe) – Charles Duffy Jun 18 '18 at 16:22
  • Thanks @StéphaneChazelas, added that. @ the other two: probably, yes (tmpfile may be more or less than the cost of a pipe, but that depends more on the OS than on the shell, and the filesystem used for $TMPDIR). – mirabilos Jun 18 '18 at 21:32
  • I tried the tmpfile way of doing this. But I am getting this error syntax error at line ##: '<<' unmatched – Pratik Mayekar Jun 20 '18 at 8:00
8

No, here-strings come from zsh in 2.0 in 1991 (and/or the Unix port of rc, their respective author exchanging ideas around that time, it's not clear which of the two had the idea or included it in his shell first).

It was added to bash in 2.05b (2002), ksh93 in m+ (2002), mksh in R33 (2008), yash in 2.7 (2009).

ksh88 is not getting any new features.

Here documents (<<), themselves come from the Bourne-shell in the late 70s.

read A B C reads one logical line into the A, B and C variables in a very special way (a bit less special with the default value of $IFS that contains only IFS-white-space characters), with backslash acting as an escaping and line-continuation character and what's going into C being quite complex as well. Doing the same thing without read would quite difficult.

Here anyway, <<< is just the same as <<, with only syntactic differences.

read A B C << EOF
$var
EOF

is exactly the same as

read A B C <<< "$var" # note that some versions of bash need the quotes

in all shells. For both, the shell creates a deleted temporary file with the content and an additional newline (though some shells use pipes instead). Then read reads one logical line from it (possibly on several physical lines continued with backslash) and fills in the variables using its complex rules.

It only assigns the first 3 words (SPC/TAB delimited) of $var to $A, $B and $C if

  • $IFS still contains its default value
  • the variable contains no backslash and no newlines
  • the variable contains only 3 words.

For the first three words, you could do:

function split_into {
  typeset words IFS v i=0
  set -o noglob

  set -A words -- $1; shift
  for v do
    eval "$v=\${words[i]}"
    ((i += 1))
  done
}

split_into "$var" A B C
  • I tried the << EOF way of doing this. But I am getting this error: syntax error at line ##: '<<' unmatched – Pratik Mayekar Jun 20 '18 at 8:00
  • @PratikMayekar, the EOF terminator has to be the only text on the line, not space before or after. Also make sure you don't have Microsoft line endings. – Stéphane Chazelas Jun 20 '18 at 8:20

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.