3

I am trying to set a while read loop line to read an input text file line by line and pass two strings as variables on each line in the text file.

    while IFS= read -r line
do
    # Read and pass two path strings as variables.
    read path1 path2
    echo "$path1"
    echo "$path2"
done < "$1"

It seems to process the next line in the text file at read path1 path2 before it assigns variables for strings in each current line.

How can I pass strings as variables on each line before going to the next line?

3

The second read inside the body of the loop is incorrect here. It actually goes one line ahead than your first read call as part of the while loop. So for your requirement just read those variables as part of the first read

while read -r path1 path2; do
    echo "$path1"
    echo "$path2"
done < "$1"

As you see here, setting IFS= is also incorrect, when reading two variables because, resetting the field separator just picks up the line as a whole. By having its default value (white-space characters of space, tab, and newline) reading two variables will store the values from each line in a space separated list. This way we could have n-column delimited line and use n variables to read.

Now the values are available in those variables which you could pass to other commands as needed.

Let see how this works for a sample input file

foo bar
foo1 bar1
foo2 bar2

Running the first script in debug mode with -x set

$ bash -x script.sh
+ read -r path1 path2
+ echo 1
1
+ echo 2
2
+ read -r path1 path2
+ echo 3
3
+ echo 4
4
+ read -r path1 path2
+ echo abc
abc
+ echo def
def
+ read -r path1 path2
0

With the redirection of the file into the for loop, all reading from standard input in the whole loop will read from the same file. This means that the two read commands in the loop will read alternate lines from whatever file you pass as the first argument to your script.

It's a bit unclear to me where you want the data for path1 and path2 to come from. If these are to be read from the file, simply do that in the loop head:

while read -r path1 path2; do
    printf 'path1 = "%s"\n' "$path1"
    printf 'path2 = "%s"\n' "$path2"
done <"somefile"

The rest of this answer assumes that you want to read something from the file, and then, in the loop, from the terminal.

To make the read in the loop body read from the terminal:

exec 3<"somefile"

while IFS= read -r words <&3; do
   read -r word1 word2 junk
   printf 'Got words = "%s"\n' "$words"
   printf 'Got word1 = "%s"\n' "$word1"
   printf 'Got word2 = "%s"\n' "$word2"
done

This makes the input to the first read come from the file somefile while the standard input stream of the second read will read from the terminal (or wherever the standard input stream of the script comes from).

0

If you want the line intact and extract values from it, you can use a here-string:

while IFS= read -r line; do
    read -r path1 path1 <<< "$line"
    echo "$line"
    echo "$path1:$path2"
done < file

Indeed, not all shells support here-strings. However, all POSIX-type shells support here documents.

while IFS= read -r line; do
    read -r path1 path1 << _LINE_
$line
_LINE_
    echo "$line"
    echo "$path1:$path2"
done < file
  • 1
    If the shell in question supports here-strings... – Kusalananda Feb 10 '18 at 21: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.