Fundamentally, pipes/files/sockets or whatever you have connected stdin/stdout/stderr are streams(*) of bytes. The relevant system calls are read()
and write()
, and the POSIX descriptions of those say:
The write() function shall attempt to write nbyte bytes from the buffer pointed to by buf to the file [...]
and
The read() function shall attempt to read nbyte bytes from the file associated with the open file descriptor, fildes, [...]
Also, POSIX defines the byte as being exactly equal to an octet, that is, a unit of eight bits.
So, an eight-bit byte is the smallest unit you can read or write at a time, so an "atom", if you will.
But what the various utilities do, is a different matter. read
by default reads a line, but so does the library function fgets()
. Depending on the shell, you may be able to ask read
to just read a fixed number of bytes instead, e.g. in Bash:
$ echo foo | ( read -n 1 a; echo "first: $a"; read -n 1 b; echo "second: $b" )
first: f
second: o
Though note that Bash's read
obeys the locale and takes the count as characters, which could be multi-byte. But that doesn't stop us from reading an individual byte instead:
$ echo äöä | (read -n 1 a; echo "first: $a"; LC_ALL=C read -n 1 b; echo "second: $b" )
first: ä
second: �
(* There are also datagram sockets, which still are byte-granular, but also hold boundaries between messages (of zero or more bytes) sent in the socket. You could probably plug a properly set up datagram socket to stdin/stdout/stderr, but hardly no-one ever does that.)