0%

日常解惑-为什么我的shell敲了退格键出现了奇奇怪怪的符号

不知道你有没有遇到过下面的这种情况:

我明明按下了退格键,为什么会出现^H啊,明明平时在shell中输入命令按下退格键功能完全正常,为什么在某些情况下它不是把前面的那个字符删除呢?

又或者是如下图所示:

image-20200110141056987

明明只是按下了一个键,突然出来了乱七八糟的一堆东西。这就要从terminal mode中开始讲起。

terminal mode

终端有两种模式,分别叫cooked moderaw mode,从字面看就能理解其意思,一个是处理过的,另外一个是不处理的。所以如果你在raw mode中按下backspace,相当于输入了一个控制字符,但是因为是raw mode,它不会对你输入的控制字符进行处理,但是又不得不显示一下(总不能空着吧,不然这么多控制字符怎么区分呢),然后就会用“脱出字符表示法”来进行表示,就是用^开头的这个,所以当你按下退格键的时候你能看到^H。可以参考这里查看所有的ASCII字符的脱出表示法。

所以总结就是:你在cooked模式下如果分别按下1,2,3,退格键和4这五个按键,那么终端会显示124,但是如果你在raw模式下,则会输出123^H4

如何输入控制字符和可显示字符

我们都知道ASCII字符分成两种,一种是可显示的字符,另一种是控制字符:

  • 可显示的字符不论是在raw模式下还是cooked模式下都是一样的,这些字符全部在键盘上,相信你只要多看几眼键盘一定能打出这些可显示字符。
  • 而控制字符就有点特殊了,因为它本身就有特殊的含义,所以需要特殊处理。如果你想特殊字符发挥作用,那么你只需要根据脱出字符,把前面的^改成ctrl即可。比如^C,那你只需要输入ctrl+c即可输入这个控制字符了。当然你可以通过stty这个命令来修改。而如果你想输入这个字符,你还需要在前面加上一个ctrl+V。可以通过下面的代码来验证。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>       
int main()
{
char ch;
while(1)
{
printf("Enter any character: ");
ch = getchar();
printf("%c is pressed.\n", ch);
printf("ascii is %d\n",ch);
// get "enter"
ch = getchar();
}
return 0;
}

就是你输入一个字符,它会帮你把你输入字符的ASCII码打印出来(已经把回车符去掉了)。那么怎么做到下面图中所示的这样呢?

image-20200110151353126

在ASCII中3号是“ctrl+C”,而要是你在键盘上按下了这个组合键,那么程序就停下来了,自然也不会输出了。所以上面是怎么做到的呢?想想看我们在java中是怎么输出双引号的,我们用了反斜杠来作为脱字符。自然而然,在linux中也有类似的脱字符,是ctrl+V。也就是输入ctrl+V,ctrl+c即可。

应用在实际中,比如我们都知道如果要发送GET的数据包,那需要GET / HTTP/1.0\r\n\r\n,来实际操作看看:

1
2
3
nc -p 1234 www.baidu.com 80
GET / HTTP/1.0
↑此时光标在这里

那我此时如果按下enter,就会发送GET / HTTP/1.0\n出去了,这显然不是我想要的,所以我需要输入\r,而不是让它回车,所以我需要先ctrl+v,然后在输入ctrl+j,这样我就把\r这个字符输进去了,另外同理,就这样输入完成:

image-20200110170453409

最后在输入ctrl+d即可。

PS:最后一个可千万别ctrl+v,然后在ctrl+d哦。这样就把一个数据包发出去了。

PPS:因为现在服务器其实你发送GET / HTTP/1.0\n\n它也会给你答复的,所以其实你按下两个回车也能得到答案,但是这里是为了说明用法。

stty

这个命令能让你修改相关的按键。我们先来看看默认输出吧(ubuntu18.04)

image-20200110163940878

  • intr:发送一个interrupt信号。这就是为什么你按下ctrl+c能够终止程序的执行
  • quit:发送一个 quit信号。
  • erase:清除最后一个输入的字符,功能等同于退格键。
  • kill:把当前这一行整行删除
  • eof:表示这一个文件输入完了。
  • eol:表示这一行输入完了。
  • eol2:eol的备用
  • swtch:切换到另外一个shell(存疑)
  • start:在停止标准输出后再度启用它。
  • stop:停止标准输出。按下之后屏幕就被“冻结”了
  • susp:发送一个terminal信号。
  • rprnt:重新绘制当前的行。
  • werase:清除最后一个单词,退格键的加强版。
  • lnext:转义符的定义。
  • discard:toggle discarding of output…我翻译不出来

后面还有好几行,有兴趣的自己man stty看吧。