10

我最近在创建函数的上下文中遇到了这种非常简洁的语法,该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)}

  • ${1}显然是函数接受的默认参数
  • ${1:-x}'x'如果$1为空(或未设置?) ,则为字符串的变量/大括号扩展“回退” 。在这种情况下,回退到 STDIN 过程子。
  • $()显然是进程命令替换
  • 最后,</dev/stdin显然是来自标准输入的重定向,它允许管道工作。

这实质上是说如果$1没有由参数填充,则回退到使用 STDIN——我在概念上很满意。

所以这是我的问题:

  1. 我从未在进程<命令替换中看到重定向( ),而在它之前没有实际命令(例如)。那么,当进程命令替换接收到重定向而没有其他命令可以调用时,实际发生了什么(本质) ?$(cat < somefile.ext)
  2. 为什么有必要将 STDIN 重定向包装在进程命令替换中?(实际上,当我写这篇文章时,我突然想到我没有测试过它,但我会保持简单)。
  3. 安全吗?我已经将它与多行标准输入一起使用,到目前为止它还没有损坏。这可能会落在哪里(如果有的话?)。

  1. $(.. ):来自 bash 手册是命令替换而不是进程替换<(.. )。并从命令替换

命令替换 $(cat file) 可以替换为等效但更快的 $(< file)。

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

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

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

4

2 回答 2

7
  1. $(.. ):来自 bash 手册是命令替换而不是进程替换<(.. )。并从命令替换

命令替换 $(cat file) 可以替换为等效但更快的 $(< file)。

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

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

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

于 2019-01-30T09:49:49.363 回答
2

回答您的问题

  • 您混淆了使用采用语法的进程替换<(cmd)和简单的 shell重定向之间的语法< file。简单来说,<基本上是从标准输入读取并写入>标准输出。该语法是一种简写语法,用于将可用的内容放在可由命令读取的标准输入上。< filefile
  • 因此,当您运行时cat < file,它基本上会将file标准输入文件描述符中的内容放在比进程晚read的内容cat中。使用的好处$(<file)是外壳没有分叉外部进程cat,只使用自己的机制来读取文件内容。
  • $(..)是命令替换的语法,其中命令在子外壳环境中运行并$(..)替换为命令的标准输出。"$(<file)"对于没有任何命令且只涉及重定向的 ie的特殊情况,shell 不是从标准输入读取,而是从文件的开头开始读取并将结果放在标准输出上。
于 2019-01-30T10:27:46.007 回答