2

In Bash, I'm reading out fields from a line into an array. Let me stress that performance is an issue, so I can't afford anything that spawns subprocesses.

To make the code more readable, I want the fields into variables: $width is more readable than ${array[0]}. I have to manually set each and every variable, like this, which is a lot of repetition:

while read line; do

  array=($line)
  width=${array[0]}
  height=${array[1]}
  size=${array[2]}
  date=${array[3]}
  time=${array[4]}

  # use $width, $height, etc. in script

done < file

Is there any compact way to do it, like the list directive in PHP?

list($width, $height, $size, $date, $time) = $array;
forthrin
  • 2,289

1 Answers1

15

Yes:

while read -r width height size thedate thetime; do
    # use variables here
done <file

This will read from standard input and split the data on blanks (spaces or tabs). The last variable will get any data that is "left over" (if there are more fields than variables read). This is instead of reading into the variable line.

I've used variable names thedate and thetime instead of date and time that are names of utilities.

To split the line on only tabs, set IFS to a tab for read:

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

See also:

Kusalananda
  • 333,661
  • Performance is a factor for me. Does read spawn a subprocess, or is it about as fast as direct variable assignment? – forthrin Apr 05 '18 at 17:32
  • @forthrin read does not spawn a subprocess (if it did, it would not be able to set the variables for the rest of the script). It is not slower than splitting the $line and assigning the variables separately. – Kusalananda Apr 05 '18 at 17:34
  • @forthrin If the loop could be done as an awk script, that would probably be faster still. – Kusalananda Apr 05 '18 at 17:36
  • Ah! Brilliant! I actually knew you could do read -r, but I just didn't think of doing that directly in the while! – forthrin Apr 05 '18 at 17:37
  • @forthrin The -r is just to properly read backslashes. It's something I add as a reflex. – Kusalananda Apr 05 '18 at 17:38
  • You're right. So simply by putting multiple variables, each line is split into those? I'll surely use this in the future. – forthrin Apr 05 '18 at 17:41
  • @forthrin Correct. The input is split on the values in $IFS. – Kusalananda Apr 05 '18 at 17:43
  • If you actually ever need the values in an array, read -a will do that, FWIW. – Will Crawford Apr 05 '18 at 23:54
  • Since OP asked about TAB delimited data, be aware that empty columns make read assign data to the wrong variables. E.g.: echo $'1\t\t3'|while IFS=$'\t' read -r a b c ; do echo "$a/$b/$c"; done One way around this with read is to use arrays, indeed. E.g.: echo $'1\t\t3'|while read -r ; do readarray -d$'\t' -t array < <(printf %s "$REPLY"); echo "${array[0]}/${array[1]}/${array[2]}"; done – xebeche Oct 09 '23 at 14:33