Sta*_* Yu 14 shell bash io-redirection command-substitution
我试图了解 Bash 是如何处理以下行的:
$(< "$FILE")
Run Code Online (Sandbox Code Playgroud)
根据 Bash 手册页,这相当于:
$(cat "$FILE")
Run Code Online (Sandbox Code Playgroud)
我可以按照第二行的推理思路。Bash 对 执行变量扩展$FILE
,进入命令替换,传递$FILE
to的值cat
,cat 将内容$FILE
输出到标准输出,命令替换通过用内部命令产生的标准输出替换整行来完成,Bash 尝试执行它一个简单的命令。
但是,对于我上面提到的第一行,我理解为:Bash 在 上执行变量替换$FILE
,Bash 打开$FILE
以读取标准输入,以某种方式将标准输入复制到标准输出,命令替换完成,并且 Bash 尝试执行生成的标准输出。
有人可以向我解释如何$FILE
从标准输入到标准输出的内容吗?
Sté*_*las 17
$(<file)
(也适用于`<file`
) 是由zsh
和复制的 Korn shell 的特殊运算符bash
。它看起来很像命令替换,但实际上并非如此。
在 POSIX shell 中,一个简单的命令是:
< file var1=value1 > file2 cmd 2> file3 args 3> file4
Run Code Online (Sandbox Code Playgroud)
所有部分都是可选的,您可以仅重定向、仅命令、仅分配或组合。
如果有重定向但没有命令,则执行重定向(因此 a> file
将 open 和 truncate file
),但没有任何反应。所以
< file
Run Code Online (Sandbox Code Playgroud)
打开file
以供阅读,但由于没有命令,因此什么也没有发生。所以file
然后关闭,就是这样。如果$(< file)
是一个简单的命令替换,那么它将扩展为空。
在POSIX 规范中$(script)
,如果script
只包含重定向,则产生未指定的结果。这是为了允许 Korn shell 的这种特殊行为。
在 ksh 中(此处使用 测试ksh93u+
),如果脚本由一个且仅由一个简单命令组成(尽管前后允许注释)且仅由重定向(无命令,无赋值)组成,并且第一个重定向是标准输入(fd 0) 仅输入 ( <
,<<
或<<<
) 重定向,因此:
$(< file)
$(0< file)
$(<&3)
($(0>&3)
实际上也是同一个运营商)$(< file > foo 2> $(whatever))
但不是:
$(> foo < file)
$(0<> file)
$(< file; sleep 1)
$(< file; < file2)
然后
<&3
)减去尾随的换行符。好像使用$(cat < file)
除了那个
cat
$(<${file=foo.txt})
或$(<file$((++n)))
)在zsh
,它的不同之处在于,当只有一个文件输入重定向特殊行为时,才会触发相同(<file
或0< file
没有<&3
,<<<here
,< a < b
...)
但是,除了在模拟其他 shell 时,在:
< file
<&3
<<< here...
Run Code Online (Sandbox Code Playgroud)
也就是说,当只有没有命令的输入重定向时,在命令替换之外,zsh
运行$READNULLCMD
(默认情况下为寻呼机),并且当有输入和输出重定向时,$NULLCMD
(cat
默认情况下),因此即使$(<&3)
不被识别为特殊操作符,它仍然会像 in 一样工作,ksh
但通过调用寻呼机来完成它(寻呼机的行为就像cat
它的标准输出将是一个管道)。
然而,虽然ksh
's$(< a < b)
会扩展到a
, in的内容zsh
,它会扩展到a
and的内容b
(或者只是b
如果该multios
选项被禁用),$(< a > b)
会复制a
到b
并扩展为空等。
bash
有一个类似的操作符,但有一些不同:
允许评论之前但不允许评论:
echo "$(
# getting the content of file
< file)"
Run Code Online (Sandbox Code Playgroud)
有效但是:
echo "$(< file
# getting the content of file
)"
Run Code Online (Sandbox Code Playgroud)
扩展到无。
像 in 一样zsh
,只有一个文件 stdin 重定向,虽然没有回退到 a $READNULLCMD
,因此$(<&3)
,$(< a < b)
执行重定向但扩展为空。
bash
不调用cat
,但它仍然分叉一个进程,通过管道提供文件内容,使其比其他shell 中的优化少得多。它实际上就像一个$(cat < file)
wherecat
将是一个 builtin cat
。$(<${file=foo.txt})
例如,在上面提到的 中,该$file
分配随后丢失)。在bash
, IFS= read -rd '' var < file
(也适用于zsh
)是将文本文件的内容读入变量的更有效方法。它还具有保留尾随换行符的好处。另请参见$mapfile[file]
在zsh
(中zsh/mapfile
模块以及仅适用于常规文件),这也与二进制文件的工作。
请注意,ksh
与 ksh93 相比,基于 pdksh 的变体有一些变化。有趣的是,在mksh
(那些 pdksh 派生的 shell 之一)中,在
var=$(<<'EOF'
That's multi-line
test with *all* sorts of "special"
characters
EOF
)
Run Code Online (Sandbox Code Playgroud)
进行了优化,因为 here 文档的内容(没有尾随字符)在没有使用临时文件或管道的情况下扩展,就像 here 文档的情况一样,这使其成为有效的多行引用语法。
要可移植到ksh
,zsh
和 的所有版本bash
,最好限制为仅$(<file)
避免注释,并记住对其中所做的变量的修改可能会或可能不会保留。
因为bash
它是在内部为您执行的,所以扩展了文件名并将文件分类为标准输出,就像您要执行$(cat < filename)
. 这是一个 bash 功能,也许您需要查看bash
源代码以确切了解它是如何工作的。
这是处理此功能的函数(来自bash
源代码,文件builtins/evalstring.c
):
/* Handle a $( < file ) command substitution. This expands the filename,
returning errors as appropriate, then just cats the file to the standard
output. */
static int
cat_file (r)
REDIRECT *r;
{
char *fn;
int fd, rval;
if (r->instruction != r_input_direction)
return -1;
/* Get the filename. */
if (posixly_correct && !interactive_shell)
disallow_filename_globbing++;
fn = redirection_expand (r->redirectee.filename);
if (posixly_correct && !interactive_shell)
disallow_filename_globbing--;
if (fn == 0)
{
redirection_error (r, AMBIGUOUS_REDIRECT);
return -1;
}
fd = open(fn, O_RDONLY);
if (fd < 0)
{
file_error (fn);
free (fn);
return -1;
}
rval = zcatfd (fd, 1, fn);
free (fn);
close (fd);
return (rval);
}
Run Code Online (Sandbox Code Playgroud)
$(<filename)
不完全等同于的注释$(cat filename)
;如果文件名以破折号开头,后者将失败-
。
$(<filename)
最初来自ksh
,并添加到bash
来自Bash-2.02
。
Ada*_*atz -4
这<
并不直接是bash 命令替换的一个方面。它是一个重定向运算符(如管道),某些 shell 允许在没有命令的情况下使用它(POSIX 未指定此行为)。
也许用更多的空间会更清楚:
echo $( < $FILE )
Run Code Online (Sandbox Code Playgroud)
这实际上*与更 POSIX 安全的相同
echo $( cat $FILE )
Run Code Online (Sandbox Code Playgroud)
...这也有效*
echo $( cat < $FILE )
Run Code Online (Sandbox Code Playgroud)
让我们从最后一个版本开始。它cat
不带参数运行,这意味着它将从标准输入读取。 $FILE
由于 被重定向到标准输入<
,因此cat
将其内容放入标准输出。$(command)
然后,替换将的cat
输出推入 的参数中echo
。
在bash
(但不是在 POSIX 标准中),您可以<
不使用命令来使用。 bash
(andzsh
和ksh
but not dash
) 会将其解释为 if cat <
,但不会调用新的子流程。由于这是 shell 的本机功能,因此它比直接运行外部命令要快cat
。 *这就是为什么我说“实际上相同”。