对于有一行的文件,"$(cat file)","$(<file)"和"read ... <file"有什么区别?

mic*_*ckp 8 bash file strace

我有一个只包含一行的输入文件:

$ cat input
foo bar
Run Code Online (Sandbox Code Playgroud)

我想在我的脚本中使用这一行,有三种方法可以让我知道:

line=$(cat input)
Run Code Online (Sandbox Code Playgroud)
line=$(<input)
Run Code Online (Sandbox Code Playgroud)
IFS= read -r line < input
Run Code Online (Sandbox Code Playgroud)

例如,使用命令替换意味着我会生成一个子shell,而read我不这样做,对吗?还有什么其他的差异,并且有一种方式优于其他方式?我也注意到(有strace)只是因为某种原因read触发了系统调用openat.其他人怎么可能不这样做?

$ 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
Run Code Online (Sandbox Code Playgroud)

tha*_*guy 10

  • line=$(cat input)是POSIX读取整个文件的方式.它需要一个分叉.

  • line=$(< input)读取整个文件是一种稍微高效的Bashism.它也有分叉,但不必执行.

  • 没有提到但是mapfile/ readarray显着更高效Bashisms用于逐行读取整个文件到数组中.没有叉子.

  • IFS= read -r line < input是没有子shell读取单行的POSIX方法.没有叉子.

您只看到后者打开文件的原因很简单,其他人在子shell中执行此操作,并且您没有指定-f跟踪子进程.

  • 嘿.我错误地认为`$(<...)`都在进行中,但是你的分叉是完全正确的.也就是说,未来的bash版本肯定有可能不再这样做,从而提供更高效的实现 - 使用POSIX语法因此将一个锁定到`exec`而不是`fork`,而`$(<...)`可以改进,将来也不做. (2认同)