Bash:流程替代的范围是什么? [英] Bash: What is the scope of the process substitution?

查看:68
本文介绍了Bash:流程替代的范围是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

As far as I know, process substitution <(...) / >(...) creates the fd

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

and stores the output of commands in parentheses into the generated fd.

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

Therefore, these two commands are equivalent

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

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

Here, my question is, how long the generated file descriptors remain?

我已阅读

I've read this article, but seems my understanding is wrong.

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

If a process substitution is expanded as an argument to a function, expanded to an environment variable during calling of a function, or expanded to any assignment within a function, the process substitution will be "held open" for use by any command within the function or its callees, until the function in which it was set returns. If the same variable is set again within a callee, unless the new variable is local, the previous process substitution is closed and will be unavailable to the caller when the callee returns.

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

In essence, process substitutions expanded to variables within functions remain open until the function in which the process substitution occured returns - even when assigned to locals that were set by a function's caller. Dynamic scope doesn't protect them from closing.

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

My best guess, after reading it, was that the created fd will not be closed until it is used.

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

From this, I wrote a very dumb code like below

#!/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恰好在运行一行命令后立即关闭.

It seems that the newly opened fd close just right after one line of command run.

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

How long does the generated fd maintained, and then what is the scope of process substitution?

推荐答案

TL; DR

似乎没有文档,因此不能保证过程替换<(...)的范围.我假设将进程替换保持在范围内的唯一安全方法是将它们直接定义为参数 cmd<(...),动态导出的变量 VAR =<(...)cmd 或重定向 cmd<<(...).在 cmd 运行时,以这种方式定义的进程替换仍在作用域内.

TL;DR

There seems to be no documentation and therefore no guarantee on the scope of process substitution <(...). I assume the only safe way to keep process substitutions in scope is to define them directly as arguments cmd <(...), on-the-fly exported variables VAR=<(...) cmd, or redirection cmd < <(...). Process substitution defined in this manner remains in scope while cmd is running.

我像您一样解释了Bash Hackers Wiki上引用的文章.同样,我得出的结论是,在函数内部声明用于进程替换的变量并不能保证它们保持打开状态.在某些系统上,还有许多其他方法可以使它们保持打开状态,尤其是使用

I interpreted the quoted article from Bash Hackers Wiki like you did. Likewise, I came to the same conclusion that declaring variables for process substitution inside a function does not guarantee that they stay open. On some systems there are many other ways to keep them open, especially with command groups like subshells (...) and contextes {...}. However, these tricks still fail on some systems.

除了链接的Bash Hackers Wiki中的错误注释外,我找不到任何相关文档.甚至 bash手册都没有提及流程替代.因此,我们只能进行实验(或阅读 bash 的源代码,而我没有).

I could not find any documentation of this apart from the wrong comments in the linked Bash Hackers Wiki. Even bash's manual does not talk about the scope of process substitution. So we are stuck with experimenting (or reading bash's source code, which I did not).

以下脚本创建了一些方案,以检查进程替换<(...)是否保留在范围内.请注意,两者之间存在非常细微的差异.例如:使用; 在同一行中编写两个命令还是在自己的行中编写每个命令,都会有所不同.当然,这个清单并不完整.随时扩展它.

The following script creates some scenarios to check when process substitution <(...) remains in scope. Note that there are very subtle differences. For instance: It makes a difference whether you write two commands in the same line using ; or each command in its own line. Of course this list is not complete. Feel free to extend it.

#! /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}'

这些是我系统的(漂亮的)输出

These are the (prettyfied) outputs for my systems

In scope on bash 5.0.17 on Arch Linux (kernel 5.6.15-arch1-1)
 |  In scope on bash 5.0.3 on Debian 10 Buster inside WSL 1
 |   |  In scope on bash 4.3.48 on Ubuntu 16.04.6 LTS
 ↓   ↓   ↓
 no  no  no   define, use
yes yes  no   define and use in same line
yes yes  no   define and use in subshell
yes yes  no   define and use in context
yes yes  no   define and use in && chain
yes yes  no   define in context and use in || chain
yes yes  no   define and use in for loop body
yes yes  no   define and use in while loop head
yes yes  no   define and use in same case
yes yes  no   define in case, use in fall-through
 no  no  no   define and use inside function in same line
 no  no  no   define local and use inside function in same line
 no  no  no   define, use as function argument
yes yes  no   define, use as function argument in same line
yes yes yes   on-the-fly export, use in different shell
 no  no  no   export, use in different shell
 no  no  no   define in command substitution, use in parent in same line
 no  no  no   read from here-string, use in parent in same line
 no  no  no   read from process substitution, use in parent in same line
 no  no  no   read from pipe and use in same line
yes yes  no   define, unrelated read from file, use in same line
yes  no  no   define, unrelated read from process substitution, use in same line
yes yes  no   define, unrelated cat from process substitution, use in same line
 no  no  no   define, unrelated read ... in subshell, use in same line
yes yes  no   define, unrelated read ... in command substitution, use in same line

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

For my interpretation of these results see the TL;DR at the beginning of this answer.

这篇关于Bash:流程替代的范围是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆