将特定编号的Bash文件描述符流转换为变量

时间:2021-07-23 15:45:38

I am trying to stream a specific numbered file descriptor into a variable in Bash. I can do this from normal standard in using the following function, but, how do it do it from a specific file descriptor. I need to direct the FD into the sub-shell if I use the same approach. I could always do it reading line by line, but, if I can do it in a continuous stream then that would be massively preferable.

我正在尝试将特定编号的文件描述符流式传输到Bash中的变量中。我可以使用以下函数从正常标准执行此操作,但是,如何从特定文件描述符执行此操作。如果我使用相同的方法,我需要将FD引导到子shell中。我总是可以一行一行地阅读,但是,如果我能够连续地进行阅读,那么这将是非常可取的。

The function I have is:

我的功能是:

streamStdInTo ()
{
    local STORE_INvar="${1}" ; shift
    printf -v "${STORE_INvar}" '%s' "$( cat - )"
}

Yes, I know that this wouldn't work normally as the end of a pipeline would be lost (due to its execution in a sub-shell), however, either in the context of the Bash 4 set +m ; shopt -s lastpipe method of executing the end of a pipeline in the same shell as the start, or, by directing into this via a different file descriptor I am hoping to be able to use it.

是的,我知道这不会正常工作,因为管道的末端会丢失(由于它在子shell中执行),但是,在Bash 4 set + m的上下文中; shopt -s lastpipe在与开始相同的shell中执行管道结束的方法,或者,通过不同的文件描述符指向我,我希望能够使用它。

So, my question is, How do I use the above but with different file descriptors than the normal?

所以,我的问题是,我如何使用上述但使用不同于正常的文件描述符?

2 个解决方案

#1


It's not entirely clear what you mean, but perhaps you are looking for something like:

你的意思并不完全清楚,但也许你正在寻找类似的东西:

cat - <&4  # read from fd 4

Or, just call your current function with the redirect:

或者,只需使用重定向调用当前函数:

streamStdInTo foo <&4

edit: Addressing some questions from the comment, you can use a fifo:

编辑:从评论中解决一些问题,你可以使用fifo:

#!/bin/bash

trap 'rm -f $f' 0
f=$(mktemp xxx)
rm $f
mkfifo $f
echo foo > $f &
exec 4< $f
cat - <&4
wait 

#2


I think there's a lot of confusion about what exactly you're trying to do. If I understand correctly the end goal here is to run a pipeline and capture the output in a variable, right? Kind of like this:

我认为关于你究竟想做什么会有很多困惑。如果我理解正确,这里的最终目标是运行管道并捕获变量中的输出,对吧?有点像这样:

var=$(cmd1 | cmd2)

Except I guess the idea here is that the name of "$var" is stored in another variable:

除了我想这里的想法是“$ var”的名称存储在另一个变量中:

varname=var

You can do an end-run around Bash's usual job control situation by using process substitution. So instead of this normal pipeline (which would work in ksh or zsh, but not in bash unless you set lastpipe):

您可以通过使用进程替换来绕过Bash的常规作业控制情况进行最终运行。所以不是这个普通的管道(它可以在ksh或zsh中工作,但不能在bash中工作,除非你设置了lastpipe):

cmd1 | cmd2 | read "$varname"

You would use this command, which is equivalent apart from how the shell handles the job:

您将使用此命令,这与shell处理作业的方式相同:

read "$varname" < <(cmd1 | cmd2)

With process substitution, "read $varname" isn't run in a pipeline, so Bash doesn't fork to run it. (You could use your streamStdInTo() function there as well, of course)

使用进程替换时,“read $ varname”不会在管道中运行,因此Bash不会派生它来运行它。 (当然你也可以在那里使用你的streamStdInTo()函数)

As I understand it, you wanted to solve this problem by using numeric file descriptors:

据我了解,您想通过使用数字文件描述符来解决此问题:

cmd1 | cmd2 >&$fd1 &
read "$varname" <&$fd2

To create those file descriptors that connect the pipeline background job to the "read" command, what you need is called a pipe, or a fifo. These can be created without touching the file system (the shell does it all the time!) but the shell doesn't directly expose this functionality, which is why we need to resort to mkfifo to create a named pipe. A named pipe is a special file that exists on the filesystem, but the data you write to it doesn't go to the disk. It's a data queue stored in memory (a pipe). It doesn't need to stay on the filesystem after you've opened it, either, it can be deleted almost immediately:

要创建将管道后台作业连接到“读取”命令的文件描述符,您需要的是管道或fifo。这些可以在不触及文件系统的情况下创建(shell会一直执行它!)但是shell不会直接暴露这个功能,这就是为什么我们需要求助于mkfifo来创建命名管道。命名管道是文件系统上存在的特殊文件,但是您写入的数据不会进入磁盘。它是存储在内存(管道)中的数据队列。打开文件系统后,它不需要保留在文件系统上,也可以立即删除它:

pipedir=$(mktemp -d /tmp/pipe_maker_XXXX)
mkfifo ${pipedir}/pipe
exec {temp_fd}<>${pipedir}/pipe         # Open both ends of the pipe
exec {fd1}>${pipedir}/pipe
exec {fd2}<${pipedir}/pipe
exec {temp_fd}<&-                       # Close the read/write FD
rm -rf ${pipedir}                       # Don't need the named FIFO any more

One of the difficulties in working with named pipes in the shell is that attempting to open them just for reading, or just for writing causes the call to block until something opens the other end of the pipe. You can get around that by opening one end in a background job before trying to open the other end, or by opening both ends at once as I did above.

在shell中使用命名管道的一个困难是,尝试打开它们只是为了读取,或者只是为了写入而导致调用阻塞,直到某些东西打开管道的另一端。您可以通过在尝试打开另一端之前在后台作业中打开一端来解决这个问题,或者像我上面那样一次打开两端。

The "{fd}<..." syntax dynamically assigns an unused file descriptor number to the variable $fd and opens the file on that file descriptor. It's been around in ksh for ages (since 1993?), but in Bash I think it only goes back to 4.1 (from 2010).

“{fd} <...”语法动态地将未使用的文件描述符编号分配给变量$ fd,并在该文件描述符上打开该文件。它已经存在了ksh多年(自1993年以来?),但在Bash中我认为它只能回到4.1(从2010年开始)。

#1


It's not entirely clear what you mean, but perhaps you are looking for something like:

你的意思并不完全清楚,但也许你正在寻找类似的东西:

cat - <&4  # read from fd 4

Or, just call your current function with the redirect:

或者,只需使用重定向调用当前函数:

streamStdInTo foo <&4

edit: Addressing some questions from the comment, you can use a fifo:

编辑:从评论中解决一些问题,你可以使用fifo:

#!/bin/bash

trap 'rm -f $f' 0
f=$(mktemp xxx)
rm $f
mkfifo $f
echo foo > $f &
exec 4< $f
cat - <&4
wait 

#2


I think there's a lot of confusion about what exactly you're trying to do. If I understand correctly the end goal here is to run a pipeline and capture the output in a variable, right? Kind of like this:

我认为关于你究竟想做什么会有很多困惑。如果我理解正确,这里的最终目标是运行管道并捕获变量中的输出,对吧?有点像这样:

var=$(cmd1 | cmd2)

Except I guess the idea here is that the name of "$var" is stored in another variable:

除了我想这里的想法是“$ var”的名称存储在另一个变量中:

varname=var

You can do an end-run around Bash's usual job control situation by using process substitution. So instead of this normal pipeline (which would work in ksh or zsh, but not in bash unless you set lastpipe):

您可以通过使用进程替换来绕过Bash的常规作业控制情况进行最终运行。所以不是这个普通的管道(它可以在ksh或zsh中工作,但不能在bash中工作,除非你设置了lastpipe):

cmd1 | cmd2 | read "$varname"

You would use this command, which is equivalent apart from how the shell handles the job:

您将使用此命令,这与shell处理作业的方式相同:

read "$varname" < <(cmd1 | cmd2)

With process substitution, "read $varname" isn't run in a pipeline, so Bash doesn't fork to run it. (You could use your streamStdInTo() function there as well, of course)

使用进程替换时,“read $ varname”不会在管道中运行,因此Bash不会派生它来运行它。 (当然你也可以在那里使用你的streamStdInTo()函数)

As I understand it, you wanted to solve this problem by using numeric file descriptors:

据我了解,您想通过使用数字文件描述符来解决此问题:

cmd1 | cmd2 >&$fd1 &
read "$varname" <&$fd2

To create those file descriptors that connect the pipeline background job to the "read" command, what you need is called a pipe, or a fifo. These can be created without touching the file system (the shell does it all the time!) but the shell doesn't directly expose this functionality, which is why we need to resort to mkfifo to create a named pipe. A named pipe is a special file that exists on the filesystem, but the data you write to it doesn't go to the disk. It's a data queue stored in memory (a pipe). It doesn't need to stay on the filesystem after you've opened it, either, it can be deleted almost immediately:

要创建将管道后台作业连接到“读取”命令的文件描述符,您需要的是管道或fifo。这些可以在不触及文件系统的情况下创建(shell会一直执行它!)但是shell不会直接暴露这个功能,这就是为什么我们需要求助于mkfifo来创建命名管道。命名管道是文件系统上存在的特殊文件,但是您写入的数据不会进入磁盘。它是存储在内存(管道)中的数据队列。打开文件系统后,它不需要保留在文件系统上,也可以立即删除它:

pipedir=$(mktemp -d /tmp/pipe_maker_XXXX)
mkfifo ${pipedir}/pipe
exec {temp_fd}<>${pipedir}/pipe         # Open both ends of the pipe
exec {fd1}>${pipedir}/pipe
exec {fd2}<${pipedir}/pipe
exec {temp_fd}<&-                       # Close the read/write FD
rm -rf ${pipedir}                       # Don't need the named FIFO any more

One of the difficulties in working with named pipes in the shell is that attempting to open them just for reading, or just for writing causes the call to block until something opens the other end of the pipe. You can get around that by opening one end in a background job before trying to open the other end, or by opening both ends at once as I did above.

在shell中使用命名管道的一个困难是,尝试打开它们只是为了读取,或者只是为了写入而导致调用阻塞,直到某些东西打开管道的另一端。您可以通过在尝试打开另一端之前在后台作业中打开一端来解决这个问题,或者像我上面那样一次打开两端。

The "{fd}<..." syntax dynamically assigns an unused file descriptor number to the variable $fd and opens the file on that file descriptor. It's been around in ksh for ages (since 1993?), but in Bash I think it only goes back to 4.1 (from 2010).

“{fd} <...”语法动态地将未使用的文件描述符编号分配给变量$ fd,并在该文件描述符上打开该文件。它已经存在了ksh多年(自1993年以来?),但在Bash中我认为它只能回到4.1(从2010年开始)。