shell 语法的机制:${1:-$(</dev/stdin)}

The mechanics of this shell syntax: ${1:-$(</dev/stdin)}

我最近遇到了这个非常简洁的语法,在创建 bash 函数的上下文中,它可以接受来自 STDIN 的参数或流(即可以通过管道传输到)。从表面上看,我明白这里发生了什么,但我想要更多地解释一下它是如何工作的实际 机制

语法如下(根据标题):${1:-$(</dev/stdin)}

在上下文中,可以将其用作:

log(){
 echo -e >&1 "INFO: ${1:-$(</dev/stdin)}"
}

允许以下用法:

$ log foo
INFO: foo

或者,您也可以这样做

mv -v foo.ext bar.ext | log
INFO: renamed 'foo.ext' -> 'bar.ext'

这很棒,因为它是我见过的使用 bash 函数启用参数和管道功能的最简洁的方法(不幸的是,我忘记了我现在在哪里遇到它)。

现在,我理解(或者我认为我理解)这里发生的大部分事情至少是表面上的,但我希望能有更深入的理解。这是我的解释,然后是我剩下的问题:

${1:-$(</dev/stdin)}

这实质上是说如果 </code> 没有参数填充,则退回到使用 STDIN - 我在概念上很满意。</p> <p>所以这是我的问题:</p> <ol> <li>我从未见过 <s>process</s> 命令替换中的重定向 (<code><),前面没有实际命令(例如 $(cat < somefile.ext))。那么当 process 命令替换接收到没有其他命令可调用的重定向时,实际发生了什么(nitty-gritty)?

  • 为什么有必要将 STDIN 重定向包装在 process 命令替换中? (实际上,当我写这篇文章时,我突然想到我没有测试过它,但我会保持简单)。
  • 安全吗?我已经将它与多行 STDIN 一起使用,到目前为止还没有损坏。这可能会掉在哪里(如果有的话?)。
    1. $(..):来自 bash 手册是命令替换而不是进程替换 <(..)。来自 Command substitution

    The command substitution $(cat file) can be replaced by the equivalent but faster $(< file).

    1. /dev/stdin/proc/self/fd/0 的符号链接,在这里很方便,因为 $(<..) 语法需要一个文件。

    2. 这可能会导致问题,因为命令可能会被阻止,直到标准输入关闭。这是安全的意思是多行输入将被保留,因为 au 双引号。

    最终为每个日志命令创建管道并分叉进程(如 mv -v foo.ext bar.ext | log)可能效率低下。

    回答您的问题

    • 您混淆了使用采用 <(cmd) 语法的进程替换和简单 shell 重定向 < file 之间的语法。在简单的形式中,< 基本上是从标准输入 read>write 到标准输出。语法 < file 是一种简写语法,用于将 file 的内容放在标准输入上可用,可以通过命令 read
    • 所以当你 运行 cat < file 时,它基本上把 file 的内容放在标准输入文件描述符中,它比 read 晚了 [=19] =] 过程。使用 $(<file) 的优点是 shell 没有 fork 外部进程 cat 并且只使用它自己的机制来读取文件内容。
    • $(..) 是命令替换的语法,其中命令在子 shell 环境中是 运行 并且 $(..) 被替换为标准输出命令。对于 "$(<file)" 的特殊情况,即没有任何命令,只涉及重定向,shell 不是从标准输入读取,而是从 start 开始读取文件并将结果放在标准输出上。