shell 中的 cat 命令在通过串行端口接收 EOT 时不会终止

shell 中的 cat 命令在通过串行端口接收 EOT 时不会终止

我有 shell/perl 脚本,它启动cat命令以接收来自外部设备的传入串行数据。

外部设备设计为在完成传输后发送 EOT 字符。但该cat命令在收到 EOT 后永远不会终止,事实上它会打印出所有串行数据,后面跟着带有 (0004) 的小框,这是 EOT 的十六进制值。

当我将所有这些数据通过管道传输到文件并在 VIM 中打开它时,我确实看到了^D.

我想知道为什么终端上的 cat 命令检测不到 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
-echoke

答案1

CTRL+D 在这里毫无意义——它只是另一个字节。这是因为您的串行终端未配置为处理它。具体来说,您实际上处于原始模式, 或者非规范输入模式。看到输出-icanon中的标志了stty -a吗?这样就可以了。以下是 POSIX 如何描述终端应考虑 EOF 字符:

  • EOF
    • 输入时的特殊字符,如果伊卡农标志已设置。当接收到时,所有等待读取的字节都会立即传递给进程,而无需等待换行符,并且EOF被丢弃。因此,如果没有字节等待(那就是EOF发生在一行的开头),应从 中返回字节计数为零read(),表示文件结束指示。如果伊卡农被设置后,EOF处理时字符应被丢弃。

但是您不是使用规范终端工作 - 您正在使用一个终端,只要有至少一个字节要推送,它就会将任何/所有数据推送到任何读取器(如果有要求)。终端不会按行缓冲输入 - 因此它不能用空读取替换 EOF 字节 - 相反,它只是推送该字节,这只会cat鼓励它继续读取。

  • 如果伊卡农设置后,应启用规范处理。这使得擦除编辑功能,以及将输入字符组装成由荷兰,EOF, 和停产,如中所述规范模式输入处理

  • 如果伊卡农未设置,读取请求应直接从输入队列满足。至少直到最小已接收字节或超时值时间字节之间过期。时间值表示十分之一秒。看非规范模式输入处理更多细节。

答案2

EOT 字符不标记文件结尾。文件可以包含任意字节。

打字Ctrl+D 在终端上使应用程序认为文件已结束。应用程序不读取 Ctrl+D (EOT) 字符,而是看到文件结束指示。 Ctrl+D 作为输入结束字符的解释是由内核中的终端驱动程序执行的;可以使用stty命令对其进行自定义(例如stty eof ^E更改字符或stty eof ^-禁用该功能)。这是特定于终端的,它不适用于常规文件、管道、非终端设备等。特别是,它不适用于串行端口。

没有标准的 shell 实用程序可以在 EOT 或可配置字符处停止读取并接受任意二进制输入。如果输入中没有空字节,则可以在将 EOT 与 EOL 交换后使用命令headread内置 shell 读取一行:

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

(额外的a是确保保留数据末尾的换行符。)

相关内容