Bash:流程替换的范围是什么?

问题描述 投票:4回答:1

据我所知,进程替换(...)创建了fd

并将命令的输出存储在生成的fd中的括号中。

因此,这两个命令是等效的

$ ls -al
$ cat <(ls -al)

这里,我的问题是,生成的文件描述符保留多长时间?

我已经读过article,但似乎我的理解是错误的。

[如果将进程替换扩展为函数的参数,在调用函数期间扩展为环境变量,或者扩展为函数内的任何赋值,则进程替换将“保持打开”状态,以供内部的任何命令使用函数或其被调用者,直到返回设置该函数的函数为止。如果在被调用方中再次设置了相同的变量,除非新变量是本地变量,否则先前的进程替换将关闭,并且当被调用方返回时,调用方将无法使用它。

本质上,扩展到函数内变量的进程替换保持打开状态,直到发生进程替换的函数返回为止-即使将其分配给由函数调用者设置的局部变量也是如此。动态范围不能防止它们关闭。

阅读后,我的最佳猜测是所创建的fd在使用前不会关闭。

由此,我编写了一个非常愚蠢的代码,如下所示

#!/bin/bash

test_subs () {
  echo "Inside a function"
  FD2=<(ls -al)

  cat $FD1
  cat $FD2
}
FD1=<(ls -al)
test_subs

Result======================================
Inside a function
cat: /dev/fd/63: No such file or directory
cat: /dev/fd/63: No such file or directory

似乎新打开的fd在命令运行后立即关闭。

生成的fd会保留多长时间,然后替换进程的范围是什么?

bash process-substitution
1个回答
0
投票

TL; DR

似乎没有文档,因此不能保证过程替代<(...)的范围。根据我的实验,如果满足以下所有条件,则可以进行过程替换a=<(...)

  • 变量a在范围内。
  • <(...)创建的命名管道尚未关闭或尚未完全读取。
  • 如果在交互式终端✱中键入命令,则分配a=<(...)和访问$a将在同一提示符下。也就是说,在键入命令期间,bash将<< not >>提示。[在许多情况下,按Enter不会引起提示,例如在子外壳(...),上下文{...}或诸如whileforif之类的块语句中。 在创建<(...)和使用$a之间,bash内置程序
  • not
  • 用于与进程替换的内容进行交互。提到的内置函数包括readmapfilereadarray在某些情况下,您可以包装这些读物。但是,我不知道为什么在$(...)无效的情况下使用(...)进行包装是有效的。✱如果您在函数之外,请完全按照脚本中的说明键入它们。单行a=<(...); cat "$a"与两行a=<(...)cat "$a"之间有所不同。如果在函数内部,请用

    Enter

    替换所有未引用的;长篇故事

    我像您一样解释了Bash Hackers Wiki上引用的文章。同样,我的实验表明,得出的结论是相同的:在函数内部声明用于进程替代的变量确实不能保证它们保持打开状态。但是,还有许多其他方法可以使它们保持打开状态,尤其是对于command groups,例如子外壳(...)和上下文{...}

除了链接的Bash Hackers Wiki中的错误注释外,我找不到任何相关文档。甚至bash's manual都没有提及进程替换的范围。因此,我们只能进行实验(或阅读bash的源代码,而我没有)。以下脚本显示了某些情况下,进程替换<(...)仍在范围内。请注意,有非常细微的差异。例如:使用;在同一行中编写两个命令还是在自己的行中编写每个命令,都会有所不同。当然,这个清单并不完整。随意扩展它。

#! /usr/bin/env bash echo 'define, use' a=<(echo ok); cat "$a"; unset a echo 'define and use in same line' a=<(echo ok); cat "$a"; unset a echo 'define and use in subshell' (a=<(echo ok); cat "$a") echo 'define and use in context' { a=<(echo ok) cat "$a"; }; unset a echo 'define and use in && chain' a=<(echo ok) && cat "$a"; unset a echo 'define in context and use in || chain' { a=<(echo ok); false; } || cat "$a"; unset a echo 'define and use in for loop body' for i in 1; do a=<(echo ok) cat "$a" done echo 'define and use in while loop head' while a=<(echo ok) cat "$a" false do true; done; unset a echo 'define and use in same case' case x in x) a=<(echo ok) cat "$a" ;; esac; unset a echo 'define in case, use in fall-through' case x in x) a=<(echo ok) ;& y) cat "$a" ;; esac; unset a echo 'define and use inside function in same line' f() { a=<(echo ok); cat "$a"; }; f; unset a f echo 'define local and use inside function in same line' f() { local a=<(echo ok); cat "$a"; }; f; unset a f echo 'define, use as function argument' f() { cat "$1"; }; a=<(echo ok) f "$a"; unset a f echo 'define, use as function argument in same line' f() { cat "$1"; }; a=<(echo ok); f "$a"; unset a f echo 'on-the-fly export, use in different shell' a=<(echo ok) dash -c 'cat "$a"' echo 'export, use in different shell' export a=<(echo ok) dash -c 'cat "$a"'; unset a echo 'define in command substitution, use in parent in same line' a=$(echo <(echo ok)); cat "$a"; unset a echo 'read from here-string, use in parent in same line' read a <<< <(echo ok); cat "$a"; unset a echo 'read from process substitution, use in parent in same line' read a < <(echo <(echo ok)); cat $a; unset a echo 'read from pipe and use in same line' shopt -s lastpipe; # TODO add `set +m` when running interactively echo <(echo ok) | read -r a; cat "$a" shopt -u lastpipe; unset a echo 'define, unrelated read from file, use in same line' a=<(echo ok); read < /etc/passwd; cat "$a"; unset a echo 'define, unrelated read from process substitution, use in same line' a=<(echo ok); read < <(echo unused); cat "$a"; unset a echo 'define, unrelated cat from process substitution, use in same line' a=<(echo ok); cat <(echo unused) > /dev/null; cat "$a"; unset a echo 'define, unrelated read ... in subshell, use in same line' a=<(echo ok); (read < <(echo unused)); cat "$a"; unset a b echo 'define, unrelated read ... in command substitution, use in same line' a=<(echo ok); b=$(read < <(echo unused)); cat "$a"; unset a b # output can be prettified using # ./script 2> /dev/null | awk 'p!="ok"{if($0=="ok")print "yes " p;else print "no " p}{p=$0}'

这是我系统的输出(美化)(WSL 1中Debian 10 Buster上的bash 5.0.3)。

,-- in scope?
V
no    define, use
yes   define and use in same line
yes   define and use in subshell
yes   define and use in context
yes   define and use in && chain
yes   define in context and use in || chain
yes   define and use in for loop body
yes   define and use in while loop head
yes   define and use in same case
yes   define in case, use in fall-through
no    define and use inside function in same line
no    define local and use inside function in same line
no    define, use as function argument
yes   define, use as function argument in same line
yes   on-the-fly export, use in different shell
no    export, use in different shell
no    define in command substitution, use in parent in same line
no    read from here-string, use in parent in same line
no    read from process substitution, use in parent in same line
no    read from pipe and use in same line
yes   define, unrelated read from file, use in same line
no    define, unrelated read from process substitution, use in same line
yes   define, unrelated cat from process substitution, use in same line
no    define, unrelated read ... in subshell, use in same line
yes   define, unrelated read ... in command substitution, use in same line

对于我对这些结果的解释,请参阅本答案开头的TL; DR。

© www.soinside.com 2019 - 2024. All rights reserved.