如何使用 read() 系统调用将用户输入脚本化到程序中?

How to script user input into program using read() syscall?

我在 Linux 环境中使用几个二进制文件,我正在尝试编写用户输入的脚本。例如,第一个程序 prog1.c:

void get_input() {

    int i;
    char buffer[32];

    i = 0;

    while (i < 3) {
        memset(buffer, 0, 32);
        printf("Enter input: ");
        fgets(buffer, 31, stdin);
        printf("Your input: %s\n", buffer);
        i++;
    }
}

我可以 运行 prog1 并直接从控制台输入输入,或者我可以编写输入脚本并将其通过管道传输到程序中。

直接从控制台输入:

# ./prog1
Enter input: Stuff1
Your input: Stuff1

Enter input: Stuff2
Your input: Stuff2

Enter input: Stuff3
Your input: Stuff3

#

脚本输入:

# perl -e 'print "Stuff1\n" . "Stuff2\n" . "Stuff3\n"' | ./prog1
Enter input: Your input: Stuff1

Enter input: Your input: Stuff2

Enter input: Your input: Stuff3

#

虽然输出的格式有点乱,程序仍然显示预期的输出。

我的问题是第二个二进制文件 prog2.c:

void get_input() {

    int i;
    char buffer[32];

    i = 0;
    while (i < 3) {
        memset(buffer, 0, 32);
        printf("Enter input: ");
        read(0, buffer, 31);
        printf("Your input: %s\n", buffer);
        i++;
    }
}

当我可以直接从控制台输入时:

# ./prog2
Stuff1
Enter input: Your input: Stuff1

Stuff2
Enter input: Your input: Stuff2

Stuff3
Enter input: Your input: Stuff3

#

不仅输入提示在我输入之后才出现,而且我什至无法编写输入脚本:

# perl -e 'print "Stuff1\n" . "Stuff2\n" . "Stuff3\n"' | ./prog2
Enter input: Your input: Stuff1
Stuff2
Stuff3

Enter input: Your input:
Enter input: Your input:
#

我研究了 fgets()read() 之间的差异,发现:

  1. fgets() 是 C 函数,而 read() 是系统调用。
  2. fgets() 读取输入直到遇到换行符或 EOF,而换行符对 read() 系统调用没有相同的影响。
  3. read() 采用了某种 fgets() 没有的缓冲机制。

我认为第 2 点和第 3 点与手头的问题最相关,但它们没有提示我如何解决我的问题。

我需要给这些程序的用户输入涉及不可打印的十六进制字节,这就是为什么直接从控制台输入此类输入是不够的。我也无法修改二进制文件的源代码,因此没有解决方法只能使用 fgets() 来收集输入。

我的问题是是否有任何方法可以将输入提供给 prog2,以便我获得与 prog1 相同的行为。

谢谢。

read(2) 不缓冲,但 fgets/printf/etc<stdio.h> 做以获得更好的性能。

如果你想阅读台词,坚持使用fgets。但是如果你想读取不可打印的数据块(即二进制),使用 fread。在任何情况下,检查 return 值。

读取 'binary' 文件(可以包含 NUL 字节等)时使用 'read()' 或 'fread()'

读取文本文件时,使用'fgets()'

我找到了解决此问题的方法。我仍然无法按照我想要的方式与二进制文件交互 - 从控制台编写我的输入脚本 - 但我找到了一种以编程方式进行的方法。

我正在使用 Python 库套件作为 subprocess 模块的包装器,但您可以简单地编写自己的:

p = subprocess.Popen(prog2, stdout=subprocess.PIPE, stdin=subprocess.PIPE)

这会将二进制文件作为进程打开,然后您可以使用 Python 脚本中的发送和接收函数与其进行交互。

我希望这对遇到类似问题的其他人有所帮助。