Newlines get swapped out at some points because they are special characters. In order to keep them, you need to make sure they're always interpreted, by using quotes:
$ a="$(cat links.txt)"
$ echo "$a"
link1
link2
link3
Now, since I used quotes whenever I was manipulating the data, the newline characters (\n
) always got interpreted by the shell, and therefore remained. If you forget to use them at some point, these special characters will be lost.
The very same behaviour will occur if you use your loop on lines containing spaces. For instance, given the following file...
mypath1/file with spaces.txt
mypath2/filewithoutspaces.txt
The output will depend on whether or not you use quotes:
$ for i in $(cat links.txt); do echo $i; done
mypath1/file
with
spaces.txt
mypath2/filewithoutspaces.txt
$ for i in "$(cat links.txt)"; do echo "$i"; done
mypath1/file with spaces.txt
mypath2/filewithoutspaces.txt
Now, if you don't want to use quotes, there is a special shell variable which can be used to change the shell field separator (IFS
). If you set this separator to the newline character, you will get rid of most problems.
$ IFS=$'\n'; for i in $(cat links.txt); do echo $i; done
mypath1/file with spaces.txt
mypath2/filewithoutspaces.txt
For the sake of completeness, here is another example, which does not rely on command output substitution. After some time, I found out that this method was considered more reliable by most users due to the very behaviour of the read
utility.
$ cat links.txt | while read i; do echo $i; done
Here is an excerpt from read
's man page:
The read utility shall read a single line from standard input.
Since read
gets its input line by line, you're sure it won't break whenever a space shows up. Just pass it the output of cat
through a pipe, and it'll iterate over your lines just fine.
Edit: I can see from other answers and comments that people are quite reluctant when it comes to the use of cat
. As jasonwryan said in his comment, a more proper way to read a file in shell is to use stream redirection (<
), as you can see in val0x00ff's answer here. However, since the question isn't "how to read/process a file in shell programming", my answer focuses more on the quotes behaviour, and not the rest.