简体   繁体   中英

cat breaks the program, manual stdin input doesn't

I have this small program:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    int orig = 1;
    for (int i = 0; (i != 3) && orig; ++i) {
        orig = orig && fork();
    }
    if (orig) {
        for (int i = 0; i != 3; ++i) {
            wait(NULL);
        }
    } else {
        int num;
        scanf("%8d", &num);
        printf("%d\n", num*num);
    }
}

Which is supposed to simply square three numbers that it reads from stdin . And if I type the numbers in myself, it works:

akiiino$ ./out.out
12345678
12345678
12345678
260846532
260846532
260846532

but if I use cat for the same purpose, it does not work as expected:

akiiino$ cat in.txt
12345678
12345678
12345678

akiiino$ cat in.txt | ./out.out
260846532
0
0

What is the reason behind this weird behavior and is it fixable? I've always assumed cat ing files was no different from typing them into stdin .

The difference is that when you type the 3 numbers by hand , you are reading from a terminal and low level reads are terminated on newlines. Let's look what happens under the hood.

  1. manual input:

    • the 3 child have started and are waiting for input
    • you type one number and a new line
    • the underlying read call is terminated by the newline because you read from a terminal
    • first child (the one that got the read) has its scanf call decode the input an does its processing

    Ok, we iterate the same for the 2 other childs

  2. reading from a file or a pipe

    • the 3 child have started and are waiting for input
    • the 3 numbers and the newlines are immediately available
    • the underlying read of first child reads and consumes all the input in the stdio buffer
    • first child (the one that got the read) has its scanf call decode the (first part of the) input an does its processing

    But now the 2 other childs are reading from a file/pipe positioned at end of file. Their scanf returns 0 but you fail to control it, and they let the initial undeterminated value in num .

You can control that it is not a problem of race condition by adding a sleep(10) before the loop, starting the program an inputting by hand 3 numbers before first child begins to read. As the low level reads will be newline terminated (special tty case) you still get same output of the first case, even if the 3 lines are available before the first read.

Already said by @Some programmer dude, problem is with the fork command. If you try ./out.out < in.txt , it should work fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM