终端特殊输入字符
程序员文章站
2022-07-01 09:17:30
...
终端支持下表所示的特殊输入字符。
为了更改,只需要修改 termios 结构(见终端 I/O 综述一节)中 c_cc 数组的相应项,该数组中的元素都用上表中第 3 列中的名字作为下标进行引用。若将 c_cc 数组中的某项设置为 _POSIX_VDISABLE 值,则禁止使用相应的特殊字符。
下面是一个更改特殊字符的示例程序,它会禁用中断字符,并将文件结束符设置为 Ctrl+B。
对此程序要说明以下几点:
1、仅当标准输入是终端设备时才能修改其终端特殊字符。使用 isatty 函数可对此进行检测。
2、使用了 fpathconf 函数获取 _POSIX_VDISABLE 的值,以免在系统不支持时直接赋值该值报错。
3、先后分别使用 gcgetattr 和 tcsetattr 函数来确保只修改希望修改的属性,而其他属性保持不变。
4、禁用中断键与忽略中断信号是不同的。禁用中断键只是禁用了使终端驱动程序产生 SIGINT 信号的特殊字符,但仍可使用 kill 函数来将该信号发送至进程。
下面将详细说明各特殊输入字符,但要注意其中 STOP 和 START(Ctrl+S 和 Ctrl+Q)在输出时也要进行特殊处理。另外,其中的大多数字符在被终端驱动程序识别并进行特殊处理后就会被丢弃,并不将它们返回给执行读终端操作的进程。返回给读进程的例外字符是换行符(NL、EOL、EOL2)和回车符(CR)。
* CR:回车符。以规范模式进行输入时识别此字符。在已设置 ICANON(规范模式) 和 ICRNL(将 CR 映射为 NL)但并未设置 IGNCR(忽略 CR)时,CR 字符会被转换成 NL。此字符会返回给读进程(很可能是在转换为 NL 后)。
* DISCARD:丢弃符。在扩充模式(IEXTEN)下进行输入时识别。在输入另一个 DISCARD 之前或在丢弃条件被清除之前(见 FLUSHO 选项),此字符使后续输出都被丢弃。
* DSUSP:延迟挂起作业控制符(delayed-suspend job-control character)。在扩充模式(IEXTEN)下,若支持作业控制,且已设置 ISIG 标志,则在输入时识别此字符。它与 SUSP 字符的相同之处是:产生 SIGTSTP 信号,并将该信号发送至前台进程组中的所有进程。但是,信号产生的时间并不是在键入延迟挂起字符之时,而是在某个进程从控制终端读到此字符时才产生。
* EOF:文件结束符。以规范模式(ICANON)进行输入时识别此字符。当键入此字符时,等待被读的所有字节都被立即传送给读进程。如果没有字节等待读就返回 0。
* EOL/EOL2:附加的行定界符。作用同 NL。
* ERASE/ERASE2:向前擦除字符(退格)。以规范模式(ICANON)输入时识别此字符。
* INTR:中断字符。若已设置 ISIG 标志,则在输入中识别此字符。它将产生的 SIGINT 信号发送至前台进程组中的所有进程。
* KILL:行擦除符。以规范模式(ICANON)输入时识别。它擦除一整行。
* LNEXT:下一个字符的字面值(literal-next character)。以扩充模式(IEXTEN)输入时识别。它使下一个字符的任何特殊含义都被忽略。使用该字符可向程序键入任何字符。它在处理后即被丢弃,但输入的下一个字符会被传送给读进程。
* NL:换行符,也称为行定界符。以规范模式(ICANON)输入时识别。
* QUIT:退出字符。若已设置 ISIG 标志,则在输入中识别此字符。它将产生的 SIGQUIT 信号发送至前台进程组中的所有进程。它与 INTR 的区别是:它不仅按默认规则终止进程,而且还产生一个 core 文件。
* REPRINT:再打印字符。以扩充规范模式(设置了 IEXTEN 和 ICANON 标志)进行输入时识别。它使所有未读的输入被输出(再回显)。
* START:启动字符。若已设置 IXON 标志,则在输入中识别;若已设置 IXOFF 标志,则自动产生此字符作为输出。已设置 IXON 时,接收到的 START 使停止的输出(由以前输入的 STOP 字符造成)重新启动。此种情形下,此字符在处理后即被丢弃。已设置 IXOFF 时,若新的输入不会使输入缓冲区溢出,则终端驱动程序自动产生一个 START 字符来恢复以前被停止的输入。
* STATUS:BSD 的状态请求字符。以扩充规范模式(设置了 IEXTEN 和 ICANON)进行输入时识别。它将产生的 SIGINFO 信号发送至前台进程组中的所有进程。另外,如果没有设置 NOKERNINFO 标志,则有关前台进程组的状态信息也显示在终端上。
* STOP:停止字符。若已设置 IXON 标志,则在输入中识别;若已设置 IXOFF 标志,则自动产生此字符作为输出。已设置 IXON 时,接收到 STOP 会停止输出。此种情形下,此字符在处理后即被丢弃。已设置 IXOFF 时,终端驱动程序自动产生一个 STOP 字符以防止输入缓冲区溢出。
* SUSP:挂起作业控制字符。若支持作业控制并且已设置 ISIG 标志,则在输入中识别。它将产生的 SIGTSTP 信号发送至前台进程组中所有进程。
* WERASE:字擦除字符。以扩充规范模式(设置了 IEXTEN 和 ICANON)进行输入时识别。它使前一个字被擦除。通常,前一个记号在碰到一个空白符时即终止,但可通过设置 ALTWERASE 标志(见终端 I/O 综述中的 c_lflag 标志表)来使前一个记号在碰到第一个非字母、非数字字符时即终止。
另外,需要为终端设备定义的另一个“字符”是 BREAK。BREAK 实际上并不是一个字符,而是在异步串行数据传送时发生的一个条件。根据串行接口的不同,可以有多种方式通知设备驱动程序发生了 BREAK 条件。对于异步串行数据传送,BREAK 是一个 0 值的位序列,其持续时间长于要求发送一个字节的时间。整个 0 值位序列被视为是一个 BREAK。可以使用函数 tcsendbreak 来发送一个 BREAK。
为了更改,只需要修改 termios 结构(见终端 I/O 综述一节)中 c_cc 数组的相应项,该数组中的元素都用上表中第 3 列中的名字作为下标进行引用。若将 c_cc 数组中的某项设置为 _POSIX_VDISABLE 值,则禁止使用相应的特殊字符。
下面是一个更改特殊字符的示例程序,它会禁用中断字符,并将文件结束符设置为 Ctrl+B。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <termios.h> int main(void){ if(isatty(STDIN_FILENO) == 0){ printf("standard input is not a terminal device\n"); exit(1); } long vdisable = fpathconf(STDIN_FILENO, _PC_VDISABLE); if(vdisable < 0){ printf("fpathconf error or _POSIX_VDISABLE not in effect\n"); exit(1); } struct termios term; if(tcgetattr(STDIN_FILENO, &term) < 0){ // fetch tty state printf("tcgetattr error\n"); exit(1); } term.c_cc[VINTR] = vdisable; // disable INTR character term.c_cc[VEOF] = 2; // EOF now is Control-B if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &term) < 0){ printf("tcsetattr error\n"); exit(1); } exit(0); }
对此程序要说明以下几点:
1、仅当标准输入是终端设备时才能修改其终端特殊字符。使用 isatty 函数可对此进行检测。
2、使用了 fpathconf 函数获取 _POSIX_VDISABLE 的值,以免在系统不支持时直接赋值该值报错。
3、先后分别使用 gcgetattr 和 tcsetattr 函数来确保只修改希望修改的属性,而其他属性保持不变。
4、禁用中断键与忽略中断信号是不同的。禁用中断键只是禁用了使终端驱动程序产生 SIGINT 信号的特殊字符,但仍可使用 kill 函数来将该信号发送至进程。
下面将详细说明各特殊输入字符,但要注意其中 STOP 和 START(Ctrl+S 和 Ctrl+Q)在输出时也要进行特殊处理。另外,其中的大多数字符在被终端驱动程序识别并进行特殊处理后就会被丢弃,并不将它们返回给执行读终端操作的进程。返回给读进程的例外字符是换行符(NL、EOL、EOL2)和回车符(CR)。
* CR:回车符。以规范模式进行输入时识别此字符。在已设置 ICANON(规范模式) 和 ICRNL(将 CR 映射为 NL)但并未设置 IGNCR(忽略 CR)时,CR 字符会被转换成 NL。此字符会返回给读进程(很可能是在转换为 NL 后)。
* DISCARD:丢弃符。在扩充模式(IEXTEN)下进行输入时识别。在输入另一个 DISCARD 之前或在丢弃条件被清除之前(见 FLUSHO 选项),此字符使后续输出都被丢弃。
* DSUSP:延迟挂起作业控制符(delayed-suspend job-control character)。在扩充模式(IEXTEN)下,若支持作业控制,且已设置 ISIG 标志,则在输入时识别此字符。它与 SUSP 字符的相同之处是:产生 SIGTSTP 信号,并将该信号发送至前台进程组中的所有进程。但是,信号产生的时间并不是在键入延迟挂起字符之时,而是在某个进程从控制终端读到此字符时才产生。
* EOF:文件结束符。以规范模式(ICANON)进行输入时识别此字符。当键入此字符时,等待被读的所有字节都被立即传送给读进程。如果没有字节等待读就返回 0。
* EOL/EOL2:附加的行定界符。作用同 NL。
* ERASE/ERASE2:向前擦除字符(退格)。以规范模式(ICANON)输入时识别此字符。
* INTR:中断字符。若已设置 ISIG 标志,则在输入中识别此字符。它将产生的 SIGINT 信号发送至前台进程组中的所有进程。
* KILL:行擦除符。以规范模式(ICANON)输入时识别。它擦除一整行。
* LNEXT:下一个字符的字面值(literal-next character)。以扩充模式(IEXTEN)输入时识别。它使下一个字符的任何特殊含义都被忽略。使用该字符可向程序键入任何字符。它在处理后即被丢弃,但输入的下一个字符会被传送给读进程。
* NL:换行符,也称为行定界符。以规范模式(ICANON)输入时识别。
* QUIT:退出字符。若已设置 ISIG 标志,则在输入中识别此字符。它将产生的 SIGQUIT 信号发送至前台进程组中的所有进程。它与 INTR 的区别是:它不仅按默认规则终止进程,而且还产生一个 core 文件。
* REPRINT:再打印字符。以扩充规范模式(设置了 IEXTEN 和 ICANON 标志)进行输入时识别。它使所有未读的输入被输出(再回显)。
* START:启动字符。若已设置 IXON 标志,则在输入中识别;若已设置 IXOFF 标志,则自动产生此字符作为输出。已设置 IXON 时,接收到的 START 使停止的输出(由以前输入的 STOP 字符造成)重新启动。此种情形下,此字符在处理后即被丢弃。已设置 IXOFF 时,若新的输入不会使输入缓冲区溢出,则终端驱动程序自动产生一个 START 字符来恢复以前被停止的输入。
* STATUS:BSD 的状态请求字符。以扩充规范模式(设置了 IEXTEN 和 ICANON)进行输入时识别。它将产生的 SIGINFO 信号发送至前台进程组中的所有进程。另外,如果没有设置 NOKERNINFO 标志,则有关前台进程组的状态信息也显示在终端上。
* STOP:停止字符。若已设置 IXON 标志,则在输入中识别;若已设置 IXOFF 标志,则自动产生此字符作为输出。已设置 IXON 时,接收到 STOP 会停止输出。此种情形下,此字符在处理后即被丢弃。已设置 IXOFF 时,终端驱动程序自动产生一个 STOP 字符以防止输入缓冲区溢出。
* SUSP:挂起作业控制字符。若支持作业控制并且已设置 ISIG 标志,则在输入中识别。它将产生的 SIGTSTP 信号发送至前台进程组中所有进程。
* WERASE:字擦除字符。以扩充规范模式(设置了 IEXTEN 和 ICANON)进行输入时识别。它使前一个字被擦除。通常,前一个记号在碰到一个空白符时即终止,但可通过设置 ALTWERASE 标志(见终端 I/O 综述中的 c_lflag 标志表)来使前一个记号在碰到第一个非字母、非数字字符时即终止。
另外,需要为终端设备定义的另一个“字符”是 BREAK。BREAK 实际上并不是一个字符,而是在异步串行数据传送时发生的一个条件。根据串行接口的不同,可以有多种方式通知设备驱动程序发生了 BREAK 条件。对于异步串行数据传送,BREAK 是一个 0 值的位序列,其持续时间长于要求发送一个字节的时间。整个 0 值位序列被视为是一个 BREAK。可以使用函数 tcsendbreak 来发送一个 BREAK。