我需要编写一个 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
的实际代码来解决问题。
但是,由于使用引入了另一个管道的 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
正是这方面无可争议的冠军,它肯定是为了在随身携带的同时具有最低的开销。
如果 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
.