I have shell/perl script which kicks off cat commands to receive incoming serial data from an external device.

The external device is designed to send to send an EOT character when it's done transmitting. But the cat command never terminates upon receiving EOT, in fact it prints out the all the serial data, followed by the little box with (0004) which is hex value for EOT.

When I pipe all this data into file and open it up in VIM, I do see the EOT char represented by ^D.

I would like to why would the cat command on terminal not detect the EOT?

stty --all --file=(serial_port) yeilds:

speed 115200 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>;
swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V;
flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts -cdtrdsr
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany
-imaxbel -iutf8
-opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl
  • What happens when you say stty --all --file=(the name of your serial device)? Edit your question and post the information there (don't respond to may comment with another comment). Apr 3, 2015 at 5:22

2 Answers 2


CTRL+D is meaningless here - it's just another byte. This is because your serial terminal is not configured to handle it. Specifically, you're effectively in raw-mode, or Non-Canonical Input Mode. See the -icanon flag in your stty -a output? That cinches it. Here's how POSIX describes a terminal should consider an EOF character:

  • EOF
    • Special character on input, which is recognized if the ICANON flag is set. When received, all the bytes waiting to be read are immediately passed to the process without waiting for a newline, and the EOF is discarded. Thus, if there are no bytes waiting (that is, the EOF occurred at the beginning of a line), a byte count of zero shall be returned from the read(), representing an end-of-file indication. If ICANON is set, the EOF character shall be discarded when processed.

But you're not working w/ a canonical terminal - you're working with a terminal that will push any/all data to any reader if asked so long as there is at least one byte to push. The terminal doesn't buffer input by line - and so it cannot replace the EOF byte with an empty read - instead it just pushes the byte, which, to cat only encourages it to keep reading.

  • If ICANON is set, canonical processing shall be enabled. This enables the erase and kill edit functions, and the assembly of input characters into lines delimited by NL, EOF, and EOL, as described in Canonical Mode Input Processing.

  • If ICANON is not set, read requests shall be satisfied directly from the input queue. A read shall not be satisfied until at least MIN bytes have been received or the timeout value TIME expired between bytes. The time value represents tenths of a second. See Non-Canonical Mode Input Processing for more details.


The EOT character does not mark the end of a file. A file can contain arbitrary bytes.

Typing Ctrl+D on a terminal makes the application think that the end of the file has come. The application does not read a Ctrl+D (EOT) character, it sees an end-of-file indication. The interpretation of Ctrl+D as end-of-input character is performed by the terminal driver in the kernel; it can be customized with the stty command (e.g. stty eof ^E to change the character or stty eof ^- to disable the feature). This is specific to terminals, it does not apply to regular files, to pipes, to devices that are not terminals, etc. In particular, it doesn't apply to serial ports.

There's no standard shell utility that stops reading at EOT or at a configurable character and accepts arbitrary binary input. If there are no null bytes in your input, you can use the head command or the read shell builtin to read one line after exchanging EOT with EOL:

data=$(tr '\004\012' '\012\004` | head -n 1 | tr '\004\012' '\012\004`; echo a)

(The extra a is to ensure that newlines at the end of the data is preserved.)

  • Thanks for your response Gilles, but isn't the cat command supposed to terminate on ctrl+D. In fact when I run this command stand alone on the terminal, it always terminates on ctrl+D. But I'm not sure why wouldn't it when it receives through the serial port. A quick run of 'od -x' on the piped file yeilds 0001120 3030 3141 0a30 0004 0001127 , where 0004 should be representing EOT.
    – seek
    Apr 3, 2015 at 15:51
  • @seek As I explain in my answer, the cat command doesn't terminate because its input contains Ctrl+D. It terminates because it receives an end-of-file indication from the terminal. Pressing Ctrl+D (a configurable keybdinging) causes the terminal to send an end-of-file indication. I suggest playing around with this a bit — set stty eof Z and watch how cat reacts when you use Ctrl+D and when you use Z at the beginning or in the middle of a line. Apr 3, 2015 at 16:05
  • @seek Oh, and if you're using a serial terminal (e.g. /dev/ttyS0 or /dev/ttyUSB0) then you can have Ctrl+D indicate the end of the data, if your stty settings have the feature active (eof ^D). But in any case Ctrl+D only indicates the end of the input at the beginning of the line, so given your data it probably won't help you. What device are you using? Is the byte before Ctrl+D always a null byte? Apr 3, 2015 at 16:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .