zsh 和 POSIX 相当于 bash 的 `{var}>&1`

zsh 和 POSIX 相当于 bash 的 `{var}>&1`

有相当于{var}>&1in的zsh吗?

bash手动的说:

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

其用法示例是STDERR从命令捕获:

{ error=$( { { ls -ld /XXXX /bin | tr o Z ; } 1>&$tmp ; } 2>&1); } {tmp}>&1

zsh在以及 POSIX 领域如何做同样的事情?

我正在寻找通用解决方案,我该如何:

  • 查找未使用的文件描述符
  • 模拟{var}>&1语法

答案1

同时根据开发人员的建议{var}>...添加了对 的支持ksh93。该运算符适用于,但不适用于复合命令。bashzshzsh{var}>...zsh

另请注意,在:

cmd 3>&1

fd 3 仅对cmd, 在中开放

cmd {var}>&1

动态分配的 fd(存储在 中)在和中返回$var后保持打开状态。该运算符主要设计为与 一起使用(另请参阅中以获得更简单的系统调用接口)。cmdzshbashexecsysopenzshopen()

因此,上面的代码缺少exec {tmp}>&-bash.

所以在这里你可以这样做:

if exec {tmp}>&1; then
   errors=$(exec 2>&1 >&"$tmp" {tmp}>&- && ls -ld /x /bin | tr o Z)
   exec {tmp}>&-
fi

这可以在 中工作bashzsh并且ksh93不会泄漏 fd。 (仅当其选项未启用时才$tmp需要引号)。请注意,在POSIX 模式下或当或处于 POSIX 模式时,失败会导致 shell 退出(此处失败可能是由于 stdout 被关闭或达到打开文件数量的限制或您可能想要退出的其他病态情况引起的)。bashposixksh93zshbashexecdup()

但在这里,您不需要动态分配的 fd,只需使用该代码中未使用的 fd 3 即可:

{ errors=$(exec 2>&1 >&3 3>&-; ls -ld /x /bin | tr o Z); } 3>&1

这可以在任何类似 Bourne 的 shell 中工作。

就像上面的动态 fd 方法一样,即使它不是那么明显,如果dup2()(in 3>&1) 失败,分配也不会运行,因此您可能需要确保errors之前已初始化(例如unset -v errors)。

请注意,fd 3 是否以其他方式打开或在脚本的其余部分中使用并不重要(原始 fd 如果打开将保持不变并在最后恢复),重要的是您嵌入的代码是否内部$(...)预计 fd 3 将打开。

只有 fd 0、1 和 2 预计会被应用程序打开,其他 fd 则不会。ls并且tr不要对 fd 抱有任何期望 3。您可能需要使用不同的 fd 的情况是当您的代码明确地使用该 fd 并期望它已预先打开,就像 if 而不是ls,您有cat /dev/fd/3其中 fd 3 预计已在脚本中较早的位置对某些资源打开。

为了回答如何在 POSIX shell 中分配第一个空闲 fd 的问题,我认为 POSIX shell 和实用程序 API 没有办法。这也可能没有意义。 shell 可以在内部使用任何 fd 执行它想要的操作,只要不妨碍它自己的 API。例如,您可能会发现 fd 11 现在是免费的,但稍后可能会被 shell 用于内部操作,并且您对其进行写入可能会影响其行为。另请注意,在 POSIX sh 中,您只能操作 fds 0 到 9。

相关内容