Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between "$(cat file)", "$(<file)" and "read ... < file" for files with one line?

Tags:

file

bash

strace

I have an input file that contains only one line:

$ cat input
foo bar

I want to use this line in my script and there are 3 ways to get it that I know of:

line=$(cat input)
line=$(<input)
IFS= read -r line < input

For example, using command substitution means I spawn a subshell, whereas with read I do not, correct? What other differences are there and is one way preferred over the others? I also noticed (with strace) that only read triggers the syscall openat for some reason. How is it possible that the others don't?

$ strace ./script |& grep input
read(3, "#!/usr/bin/env bash\n\ncat > input"..., 80) = 80
read(255, "#!/usr/bin/env bash\n\ncat > input"..., 167) = 167
read(255, "\nline=$(cat input)\nline=$(<input"..., 167) = 60
read(255, "line=$(<input)\nIFS= read -r line"..., 167) = 41
read(255, "IFS= read -r line < input\n", 167) = 26
openat(AT_FDCWD, "input", O_RDONLY)     = 3
like image 704
mickp Avatar asked Aug 20 '18 19:08

mickp


People also ask

What is the difference between cat file and cat file?

There is no difference from a user point of view. These commands do the same thing. Technically the difference is in what program opens the file: the cat program or the shell that runs it.

How read file line by line in shell script and store each line in a variable?

We use the read command with -r argument to read the contents without escaping the backslash character. We read the content of each line and store that in the variable line and inside the while loop we echo with a formatted -e argument to use special characters like \n and print the contents of the line variable.


1 Answers

  • line=$(cat input) is the POSIX way of reading the entire file. It requires a fork.

  • line=$(< input) is a marginally more efficient Bashism for reading the entire file. It also forks, but doesn't have to execve.

  • Not mentioned but mapfile/readarray are significantly more efficient Bashisms for reading the entire file line-by-line into arrays. No forks.

  • IFS= read -r line < input is the POSIX way of reading a single line without a subshell. No forks.

The reason why you only see the latter opening the file is simply that the others do it in a subshell, and you haven't specified -f to trace child processes.

like image 96
that other guy Avatar answered Sep 22 '22 23:09

that other guy