zsh 和 POSIX 等价于 bash 的 `{var}>&1`

Tom*_*ale 4 shell bash zsh posix

有相当于{var}>&1inzsh吗?

bash手册说:

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

一个示例用法是STDERR从命令中捕获:

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

zsh在 POSIX-land 和 POSIX-land 中如何做同样的事情?

我追求的是通用解决方案,我该怎么做:

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

Sté*_*las 12

支持{var}>...加入ksh93bashzsh在同一时间上的建议zsh开发商。该{var}>...运营商的工作zsh,而不是复合命令。

另请注意,在:

cmd 3>&1
Run Code Online (Sandbox Code Playgroud)

fd 3 仅对cmd, in

cmd {var}>&1
Run Code Online (Sandbox Code Playgroud)

动态分配的 fd(存储在 中$varcmdzsh和 中返回后保持打开状态bash。该运算符主要设计用于与exec(另请参阅sysopeninzsh以获取更直接的open()系统调用接口)。

所以你上面的代码丢失exec {tmp}>&-了之后在bash.

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

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

这将在工作bashzshksh93和不漏一个FD。($tmpbash当其posix选项未启用时才需要周围的引号)。请注意,在POSIX 模式下ksh93或当zshbash处于 POSIX 模式时,失败exec会导致 shell 退出(dup()此处失败可能是由于标准输出被关闭或打开文件数量受到限制或其他病理情况下您可能想要退出)。

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

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

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

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

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

只有 fds 0、1 和 2 预计会被应用程序打开,其他 fds 不会。lstr不要指望约FD 3例东西,你可能需要使用不同的fd是当你的代码明确利用该FD,并期望它一直开着事先一样,如果不是的ls,你就cat /dev/fd/3在那里FD 3预期已对脚本中较早位置的某些资源开放。

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