Question:
I have a file with this content:
Hola qué tal
Yo \t muy bien
Un placer.
I want to read it line by line, saving the value of each line in a variable for processing.
That is, I want:
- Read the first line.
- Assign the value "Hello how are you" to the variable $ line.
- Do things with
$linea
. - Read the second line.
- Assign the value "I \ t fine" to
$linea
.
And so on.
Answer:
Use while ... do ... done < fichero
with some nuance:
while IFS='' read -r linea || [[ -n "$linea" ]]; do
printf ">%s<\n" "$linea"
done < fichero
What is explained as:
-
IFS=''
(orIFS=
) prevents leading orIFS=
space characters (space or tab) from being removed. -
-r
prevents the backslash (\
) from being interpreted as a special character. -
|| [[ -n $linea ]]
prevents the last line from being ignored if it does not end with\n
(sinceread
returns a non-zero exit when it encounters an EOF). Theoretically, a line must end with\n
and is defined by POSIX : Sequence of zero or more characters other than a new line\n
followed by a new line character . However, it may be the case that a file has been written in which the last line does not contain it; with this addition, that line would also be processed.
Thus, in your file we would have this output:
$ while IFS= read -r linea || [[ -n "$linea" ]]; do printf ">%s<\n" "$linea"; done < fichero
>Hola qué tal<
>Yo \t muy bien<
> Un placer.<
Let's see what would happen if we removed any of the checks:
Without IFS=''
: leading and trailing spaces are removed.
$ while read -r linea || [[ -n "$linea" ]]; do printf ">%s<\n" "$linea"; done < a
>Hola qué tal<
>Yo \t muy bien<
>Un placer.< # ¡esto tenía espacios al principio!
Without -r
in read
: the backslash is interpreted.
$ while IFS= read linea || [[ -n "$linea" ]]; do printf ">%s<\n" "$linea"; done < a
>Hola qué tal<
>Yo t muy bien< # ¡aquí había un \t!
> Un placer.<
Without || [[ -n $linea ]]
: a hypothetical last line not ending in \n
not be read:
Given the previous file, if we add a new series of characters without a final \n
:
$ printf "ueee" >> fichero
We observe that its content appears in this way:
$ cat -vet fichero
Hola quM-CM-) tal$
Yo \t muy bien$
Un placer.$
ueee # se superpone al prompt
When we read, this last line is not processed:
$ while IFS= read -r linea; do printf ">%s<\n" "$linea"; done < fichero
>Hola qué tal<
>Yo \t muy bien<
> Un placer.< # la línae ueee" no se lee
Referencias:
- How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?
- What does IFS= do in this bash loop:
cat file | while IFS= read -r line; do … done
- Read a file line by line assigning the value to a variable