What is the difference between “$(cat file)”, “$(&

2019-01-24 12:22发布

问题:

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

回答1:

  • 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.



标签: bash file strace