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

在Linux中使用syscall Read读取STDIN:未使用的输入被发送到bash,c,linux,bash,assembly,system-calls,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

以下程序(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
一旦汇编

yasm -f elf64 -l hello.lst -o input.o input.asm
ld -o input input.o
如果它是按

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

echo "123456" | ./input
56
不会发送到bash

那么,如何防止未使用的输入被发送到bash?我是否需要一直使用它,直到遇到某种形式的EOF?这是意料之中的行为吗

在C程序中也会发生同样的情况:

#包括
int main()
{
char-buf[16];
读取(0,buf,4);
返回0;
}

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

是的,这是正常的行为。您不消费的任何东西都可用于下一个流程。你知道当你做一些慢的事情时,你可以提前键入,当慢的事情完成时,shell将运行你键入的内容吗?这里也一样

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

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

  • 您的程序是否像
    cat
    sed
    grep
    这样的过滤器?然后你应该阅读,直到你达到EOF

  • 您的程序是否完全不像
    echo
    gcc
    那样从stdin读取数据?然后您应该不使用stdin,不使用任何内容,将输入留给下一个程序


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

是的,这是正常行为。您不消费的任何东西都可用于下一个流程。你知道当你做一些慢的事情时,你可以提前键入,当慢的事情完成时,shell将运行你键入的内容吗?这里也一样

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

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

  • 您的程序是否像
    cat
    sed
    grep
    这样的过滤器?然后你应该阅读,直到你达到EOF

  • 您的程序是否完全不像
    echo
    gcc
    那样从stdin读取数据?然后您应该不使用stdin,不使用任何内容,将输入留给下一个程序

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

“被发送到”bash(或任何其他程序)是一种次优的思考方式,这可能会导致您的惊讶/困惑。“提供给”将是一个更准确的描述,无论您是指终端、管道还是连接到程序标准输入的任何其他输入源

当一个进程分叉另一个进程时,例如shell执行您输入的许多命令时,新进程将从其父进程继承许多属性。其中包括其开放文件描述符,尤其是父级标准流的描述符。在POSIX系统上,这是在没有重定向的情况下进程的标准流的来源,也是实现重定向的机制

因此,当不涉及I/O重定向时,父shell当然会读取它启动的程序未读的输入数据。如果父shell无法使用输入,则这些程序无法使用输入,因为两者都从同一源读取。这也是为什么您可以在同一终端中的前台和后台之间移动程序,并且每个程序在前台时都可以从终端读取数据

由于您特别提到了
read()
syscall,我怀疑您的惊讶可能还与看到使用stdio函数读取标准输入的程序的不同行为有关。这与以下事实有关:当标准输入连接到终端时,默认情况下,stdio函数以行缓冲模式读取它。也就是说,它们将数据从底层源传输到内部缓冲区,从而一次一行地将其从流中删除(有一些警告)

您可以使用
读取
来模拟这一点。最简单的方法是一次读取一个字节,直到看到换行符或文件结尾,但C库函数通过在标准输入实际上连接到终端时适当配置终端驱动程序来更有效地执行此操作。您也可以直接这样做,但它有点复杂。

“发送到”bash(或任何其他程序)是一种次优的思考方式,可能这会导致您的惊讶/困惑。“提供给”将是一个更准确的描述,无论你在说什么