返回字符串并了解重定向和输出

返回字符串并了解重定向和输出

我试图弄清楚如何使用重定向来有选择地从 bash 函数输出字符串,该函数需要将多行打印到终端。我找到了一个答案StackOverflow 上的问题。下面的代码有效。但我不明白如何或为什么。

#!/bin/bash

exec 3>&1

returnString()
{
    exec 4>&1 >&3
    local s=$1
    s=${s:="some default string"}
    echo "writing to stdout"
    echo "writing to stderr" >&2
    exec >&4-
    echo "$s"
}

my_string=$(returnString "$*")
echo "my_string:  [$my_string]"

我知道 fd3 被重定向到stdout开头。然后fd4也是如此,1指向3,这基本上将其指向自身。但是,这对打印到终端的内容有何影响?为什么该输出没有显示在 中$my_string,但在摆脱 fd4 后的最后一个输出中却显示了?

我不明白 fd4 与什么有什么关系,因为没有任何内容明确发送到 fd4。

答案1

您上面展示的解决方案是一种通用解决方案,涵盖了很多案例。除非您知道自己有问题,否则您可能需要采取 YAGNI(您不需要它)姿势,并将所选行重定向到 /dev/tty。

如果您想避免这种情况,那么您需要了解正在发生的文件描述符(fd)杂耍。

3>&1调用上下文的标准输出复制到“保存单元”,以便该函数可以输出到调用上下文的标准输出。在本例中,它是/dev/tty。请记住,该函数将使用属于 ${} 替换子 shell 的 stdout 来调用。

创建exec 4>&1fd1 的副本,它是函数的标准输出。 fd4 被用作函数开始时的标准输出的保持单元。正在>&3将函数/子 shell 的标准输出设置为调用者的标准输出。因此,所有输出都将发送到调用者的标准输出。 UNTIL,最后将>&4-保存的标准输出移回其开始位置,允许最后一个echo "$s"位于函数(和子 shell)的标准输出中。

哇!

and>&3>&4-可以更清楚地写为1>&3and 1>&4-

手册中有关重定向的部分bash解释了所有的细节和术语。我很高兴也惊讶地发现实际上可以使用 {name} 形式将数字更改为单词。

“每个可能以文件描述符编号开头的重定向可能会以 {varname} 形式的单词开头。在这种情况下,对于除 >&- 和 <&- 之外的每个重定向运算符,shell 将分配一个文件描述符大于 10 并将其分配给 varname。如果 >&- 或 <&- 前面有 {varname},则 varname 的值定义要关闭的文件描述符。”

我没有对此进行测试,但它建议您可以将其编码为:

#!/bin/bash

exec {caller_stdio}>&1

returnString() {
  exec {func_stdio}>&1 1>&{caller_stdio}
  local s=$1
  s=${s:="some default string"}
  echo "writing to stdout"
  echo "writing to stderr" >&2
  exec 1>&{func_stdio}-
  echo "$s"
}

my_string=$(returnString "$*")
echo "my_string:  [$my_string]"

答案2

exec 3>&1

fd 3 现在是重复fd 1 (在您的示例中,您的终端)。正如手册页所述,这意味着 fd 1 和 fd 3 可以互换使用来引用相同的文件或设备。

shell 中 duping 的主要用途是保存 fd 的副本,以便以后恢复。

my_string=$(returnString "$*")

为了让 bash 从 的求值中获得标准输出returnString "$*",它创建了一个管道和叉子,孩子叫道杜普2移动管道的写入端(在本例中为 fd 5,但可能会有所不同)到 fd 1。

exec 4>&1 >&3

重定向是从左到右评估的。 fd 4 设置为管端的重复。然后,fd 1 被设置为引用您的终端的 fd 的重复。

echo "writing to stdout"

回显到 fd 1,您的终端。

exec >&4-

这会将 fd 4 移动(即执行 dup2)到 fd 1。fd 1 现在是管道末端。

echo "$s"

回声传至管端。最终,父 shell 从管道的另一端读取此值并用作$(returnString "$*")

相关内容