是否可以仅使用 shell 重定向将命名管道重定向到标准输出?

问题描述 投票:0回答:2

我需要编写一个 shell 脚本,将其输出提供给另一个应用程序的标准输入。 shell脚本需要从命名管道中读取数据

pipe.fifo
.

一个简单的方法是使用

cat pipe.fifo

但是,由于使用了引入另一个管道的

cat
命令,这会引入轻微的开销。我想到了一种方法,仅使用
exec
和 shell 重定向,而不使用任何其他命令,将数据从命名管道重定向到标准输出。

exec 3<pipe.fifo 3>&1

这应该创建一个新的 fd 3,代表打开的命名管道

pipe.fifo
并且来自管道的数据应该被重定向到标准输出。当我运行此 shell 脚本时,它会阻塞,因为没有任何内容写入命名管道,正如预期的那样。但是,一旦我从另一个 shell 将某些内容打印到命名管道中,脚本就不会输出任何内容并停止阻塞。

我想问一下,这个命令可能有什么问题,如果这样的重定向是可能的。

编辑:为了进一步阐明具体用例,我想做一些类似于

myapp <temp.fifo
的事情,其中
myapp
直接读取命名管道而不是标准输入。但不幸的是,我不能直接打电话给
myapp
。相反,应用程序本身运行一个命令,其输出在应用程序中使用。这是一些最小的 C 代码,可用于表示
myapp
.

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    char command[100];
    char buffer[1024];
    FILE* pipe;

    if (argc != 2) {
        printf("Usage: %s command\n", argv[0]);
        exit(1);
    }

    snprintf(command, sizeof(command), "%s", argv[1]);

    /* Open the command as a pipe */
    pipe = popen(command, "r");

    if (pipe == NULL) {
        printf("Failed to run command\n");
        exit(1);
    }

    /* Read the output of the command into a buffer */
    while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
        printf("%s", buffer);
    }

    pclose(pipe);

    return 0;
}

它接受作为第一个命令行参数运行的命令,在本例中是提到的 shell 脚本。假设可执行文件和脚本都在同一文件夹中,我们可以将其运行为

./myapp ./script.sh

由于代码太复杂,我不想修改

myapp
的实际代码来解决问题。

bash sh stdout named-pipes io-redirection
2个回答
1
投票

但是,由于使用引入了另一个管道的 cat 命令,这会带来轻微的开销

没有开销。没有另一个管道。 Cat reads 数据并 writes 它们到 stout.

你可以在 bash 中读取数据,但是 bash 不能处理零字节,所以它不是很好,而且它是行缓冲的,与 cat 相比非常慢。您可以在 bash 中按 char

-n1
读取 char,但那样会非常慢。

while IFS= read -r l; do printf "%s\n" "$l"; done < pipe.fifo

Linux 上最快的方法是编写一个调用 splice 的 C 程序 https://man7.org/linux/man-pages/man2/splice.2.html。 github 上有 cat 程序可以做到这一点。然后将数据复制到/从内核空间的开销被删除。我怀疑你需要那种表现。

这个命令可能有什么问题,

3>&1 移除之前的重定向。现在 fd3 被重定向到 stdout 并且 pipe.fifo 被关闭并且不再使用。

重定向是链接,不是动作。如果你想重定向它,你仍然必须从 fd3 读取它。

exec 3<pipe.fifo
cat <&3

考虑研究什么是文件descriptor,它与文件description有何不同。它只是一个数字,打开的文件。

要读取内容并写入标准输出,您必须有一个实体来执行该操作。

cat
正是这方面无可争议的冠军,它肯定是为了在随身携带的同时具有最低的开销。


0
投票

如果 shell 脚本需要从 fifo 读取它的所有输入,就这样做

exec < pipe.fifo

如果 shell 只需要其他命令的数据,只需执行:

< pipe.fifo other-command

线

exec 3<pipe.fifo 3>&1
根本没有做你想要的。它只是指示 shell 进行 2 次重定向,第二次重定向覆盖第一次重定向。当 shell 评估该行时,它首先尝试从管道中复制 fd 3,这将阻塞直到其他进程正在写入管道。一旦另一个进程开始写入管道,重定向就会完成(解除阻塞)并且 shell 会立即尝试完成
3>&1
指定的重定向。执行该重定向会关闭 fifo,最终结果与该行完全相同
exec 3>&1
.

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