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> 显然是函数接受的默认参数</li>
<li><code>${1:-x}
是对字符串 'x'
的 variable/brace 扩展 'fall back' 如果 </code> 否则为空(或未设置?)。在这种情况下回退到 STDIN 进程子。</li>
<li><code>$()
显然是一个进程命令替换
- 最后,
</dev/stdin
显然是来自标准输入的重定向,它允许管道工作。
这实质上是说如果 </code> 没有参数填充,则退回到使用 STDIN - 我在概念上很满意。</p>
<p>所以这是我的问题:</p>
<ol>
<li>我从未见过 <s>process</s> 命令替换中的重定向 (<code><
),前面没有实际命令(例如 $(cat < somefile.ext)
)。那么当 process 命令替换接收到没有其他命令可调用的重定向时,实际发生了什么(nitty-gritty)?
为什么有必要将 STDIN 重定向包装在 process 命令替换中? (实际上,当我写这篇文章时,我突然想到我没有测试过它,但我会保持简单)。
这安全吗?我已经将它与多行 STDIN 一起使用,到目前为止还没有损坏。这可能会掉在哪里(如果有的话?)。
$(
..)
:来自 bash 手册是命令替换而不是进程替换 <(
..)
。来自 Command substitution
The command substitution $(cat file) can be replaced by the equivalent but faster $(< file).
/dev/stdin
是 /proc/self/fd/0
的符号链接,在这里很方便,因为 $(<
..)
语法需要一个文件。
这可能会导致问题,因为命令可能会被阻止,直到标准输入关闭。这是安全的意思是多行输入将被保留,因为 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 开始读取文件并将结果放在标准输出上。
我最近遇到了这个非常简洁的语法,在创建 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> 显然是函数接受的默认参数</li> <li><code>${1:-x}
是对字符串'x'
的 variable/brace 扩展 'fall back' 如果</code> 否则为空(或未设置?)。在这种情况下回退到 STDIN 进程子。</li> <li><code>$()
显然是一个进程命令替换- 最后,
</dev/stdin
显然是来自标准输入的重定向,它允许管道工作。
这实质上是说如果 </code> 没有参数填充,则退回到使用 STDIN - 我在概念上很满意。</p>
<p>所以这是我的问题:</p>
<ol>
<li>我从未见过 <s>process</s> 命令替换中的重定向 (<code><
),前面没有实际命令(例如 $(cat < somefile.ext)
)。那么当 process 命令替换接收到没有其他命令可调用的重定向时,实际发生了什么(nitty-gritty)?
$(
..)
:来自 bash 手册是命令替换而不是进程替换<(
..)
。来自 Command substitution
The command substitution $(cat file) can be replaced by the equivalent but faster $(< file).
/dev/stdin
是/proc/self/fd/0
的符号链接,在这里很方便,因为$(<
..)
语法需要一个文件。这可能会导致问题,因为命令可能会被阻止,直到标准输入关闭。这是安全的意思是多行输入将被保留,因为 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 开始读取文件并将结果放在标准输出上。