在 Linux 中使用 syscall READ 读取 STDIN:未消耗的输入被发送到 bash

mob*_*eng 1 c linux bash assembly system-calls

以下程序(64 位 YASM)从标准输入读取 4 个字节并退出:

    section .data
buf   db "               "      ; Just allocate 16 bytes for string
    section .text
    global _start
_start:
    mov rax, 0                  ; READ syscall
    mov rdi, 0                  ; STDIN
    mov rsi, buf                ; Address of the string
    mov rdx, 4                  ; How many bytes to read
    syscall
    ; Exit:
    mov rax, 60
    mov rdi, 0
    syscall
Run Code Online (Sandbox Code Playgroud)

一旦编译

yasm -f elf64 -l hello.lst -o input.o input.asm
ld -o input input.o
Run Code Online (Sandbox Code Playgroud)

如果它像这样运行

./input
Run Code Online (Sandbox Code Playgroud)

比如说,123456\n作为用户输入,它将消耗1234,但结束位56\n被发送到 bash。因此,bash 将尝试运行该命令56……幸好没有成功。但是想象一下,如果输入是1234rm -f *. 但是,如果我使用重定向或管道提供输入,例如,

echo "123456" | ./input
Run Code Online (Sandbox Code Playgroud)

56 不会发送到 bash。

那么,如何防止将未使用的输入发送到 bash?在遇到某种形式的 EOF 之前,我是否需要继续使用它?这甚至是预期的行为吗?

同样的事情发生在 C 程序中:

    section .data
buf   db "               "      ; Just allocate 16 bytes for string
    section .text
    global _start
_start:
    mov rax, 0                  ; READ syscall
    mov rdi, 0                  ; STDIN
    mov rsi, buf                ; Address of the string
    mov rdx, 4                  ; How many bytes to read
    syscall
    ; Exit:
    mov rax, 60
    mov rdi, 0
    syscall
Run Code Online (Sandbox Code Playgroud)

(我只是想知道 C 运行时是否以某种方式清除了 STDIN,但不,它没有)

Joh*_*ica 6

是的,这是正常行为。您不消耗的任何东西都可用于下一个过程。您知道如何在执行缓慢的操作时可以提前输入,而当执行缓慢的操作完成时,shell 将运行您输入的内容吗?这里也是一样。

没有一刀切的解决方案。这真的是关于用户的期望。他们期望您的程序消耗多少输入?这就是你应该阅读的内容。

  • 您的程序是否像单行提示一样read?然后,您应该通过下一个\n字符读取整行输入。不过度阅读的最简单方法是一次阅读 1 个字符。如果您进行批量读取,您可能会错误地消耗下一行的一部分。

  • 您的程序是否像过滤器一样catsedgrep?然后你应该阅读直到你到达 EOF。

  • 您的程序是否根本不从标准输入读取echogcc?然后你应该不理会 stdin 并且不消耗任何东西,将输入留给下一个程序。

正好消耗 4 个字节是不寻常的,但对于提示输入 4 位 PIN 且不需要用户按 的交互式程序来说可能是合理的行为Enter

  • 请注意,由于终端是行缓冲的(除非配置不同),因此在使用默认终端配置进行交互式输入时,精确读取 4 个字节从来都不是一个好主意。相反,读取大量缓冲区数据并处理您获得的任何内容。 (2认同)