3

If the following code

ssize_t len = read(0, buf, BUF_SIZE);
perror("read()");
printf ("%i '%s'\n", (int) len, buf);

executed to read from the terminal, read() terminates input on a newline character (pressing Enter) without showing any error, i.e. resulting in line-by-line input. However, when I redirect it to input from a file using shell, it ignores the newline and continues reading the full buffer or until EOF.

The latter behaviour is more expected according to the documentation. So, why, was the terminal input terminated on newline? Does it mean that this input is non-blocking by default? What is the proper way to check whether EOF (Ctrl-D) was reached or some other condition resulted in returning an incomplete input?

Nick
  • 970
  • 10
  • 20
  • That's a lot of questions. I'll write a detailed answer when I can. But you can find all the answers in Ch:3 and Ch:18 of the APIUE book. – Harith Mar 11 '23 at 12:27

2 Answers2

2

Standard input is blocking in your case. read blocks until at least one byte is available and then can return a chunk of arbitrary size. You cannot make any assumptions about how much it will read, it may even always return you a single byte.

In most cases it will read as much as currently available and return. By default terminal is operating in so-called "canonical mode" and makes the input available line-by-line. You can read more about it in the termios(3) man page. So read cannot get even a byte until you press enter. Then the line becomes available, read gets the whole line and returns without waiting for more data.

If you want to disable canonical mode and receive bytes as soon as user types them in the terminal, there is a related question about it at How to read terminal's input buffer immediately after keypress

What is the proper way to check whether EOF (Ctrl-D) was reached or some other condition resulted in returning an incomplete input?

On EOF condition read returns 0, it is documented in the man page. If you want to read the whole file, you need to call read in a loop until it returns 0. If read returns -1, it indicates an error.

If you want to read the file line-by-line, you need to implement buffering in your application. Keep calling read in a loop until there is a newline in the buffer. Then process the line, keep the remaining part and start reading again. Alternatively, use stdio.h functions such as fgets which implement the buffering for you.

tla
  • 855
  • 1
  • 14
1

The read() system call reads input from a terminal in non-blocking mode by default, so it returns right away after reading any input that is there without waiting for more input. This is why a newline character is used to end input from a terminal.

While reading from a file, you can examine the return value of read() to see if EOF was reached. The end of the file (EOF) has been reached if read() returns 0. If read() provides a value that is less than the amount of bytes that were asked to be read, it signifies that another circumstance caused it to return an incomplete input. You can use perror() to print an error message that details the particular error that took place.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • 1
    perror() should only be called when read() returns -1, because it only sets errno in this case. If returned value is 0 (EOF) or positive (some bytes read, maybe less than asked for), there is no error. – tla Mar 12 '23 at 16:32