扫盲 Linux&UNIX 命令行

扫盲 Linux&UNIX 命令行★引子  首先,  这篇是为了补前几年的“欠债”。这些年,俺写了好多篇 Linux 相关的技术教程。但还从来没有【系统性】地介绍 Linux 命

大家好,欢迎来到IT知识分享网。

★引子

  首先,
  这篇是为了补前几年的“欠债”。这些年,俺写了好多篇 Linux 相关的技术教程。但还从来没有【系统性】地介绍 Linux 命令行相关的基本概念和基本知识。几年来,已经有不少读者催俺填上这个大坑,但俺比较懒,一直拖到现在,惭愧 :(
  其次,
  一个多月前(9月份)写了一篇 netcat 的扫盲教程,其中涉及了很多命令行相关的知识。很多菜鸟读者,如果缺乏这些基础知识,恐怕看不懂那篇 netcat 教程。再加上前几天的博文谈到了【系统性学习】相关的方法论,并且还聊了【费曼学习法】的各种好处。
  今天这篇,算是俺第 N 次践行“费曼学习法”——无论对俺还是读者,这都是【双赢】滴 :)

★本文目标读者

  虽然本文的标题号称是【扫盲】,但俺相信:即使是一些 POSIX 系统的命令行【老手】,对本文中介绍的某些概念,可能也会有【欠缺】。
  因此,这篇教程既适合于命令行的新手,也值得某些【老手】看一看。

  由于本文介绍的是 POSIX 系统中【通用的】概念与知识。因此,包括 Linux、BSD 家族、macOS 等各种系统的用户,应该都能从中受益。
  (注:POSIX 是某种操作系统的标准/规范。各种 Linux 发行版以及所有的 UNIX 变种,包括 macOS,都属于“POSIX 系统”)

  如果你是这方面的【菜鸟】,并且想要掌握这个领域。【不要】企图只看一遍就完全理解本文的内容(可能需要看好几遍)。俺的建议是:要一边看,一边拿命令行的环境【实践】一下。

★一切都从【电传打字机】开始说起

  (说完了“引子”与“目标读者”,开始切入正题)
  可能有些读者会纳闷——“聊命令行的基本概念”,为啥要扯到“电传打字机”?是不是扯得太远了?
  俺来解释一下:
  IT 行业的很多基本概念都来自于【历史遗迹】。有时候你觉得某些东西很奇怪(并纳闷“为啥会设计成这样”);而当你搞清楚历史的演变过程之后,自然就明白其中的原因。

◇在那遥远的【电报时代】

  在计算机诞生之前(二战前),【电报】属于高科技的玩意儿——它能够瞬间把信息传送到另一个城市(甚至传送到大洋彼岸)。
  当年的电报线路,是以【字符】为单位发送信息。在线路两端使用【电传打字机】,就可以自动地把对方发过来的字符打印出来。


◇“回车/换行”的来历

  稍微懂点 IT 的同学,应该都听说过“回车/换行”,洋文分别称之为“carriage return”&“line feed”。在编程领域,这两个字符简称为 \r & \n。
  为啥会有这么两个玩意儿捏?
  因为在电传打字机时代,当打印完一行之后,需要用一个控制命令把“打印头”复位(移到打印纸的左边),然后再用另一个控制命令把“打印头”往下移动一行。很自然地,这俩动作就对应了两个控制字符(CR & LF),也就是所谓的“回车 & 换行”。

◇其它控制字符

  如果你去留意一下 ASCII 字符表的开头部分,前面那32个字符都是控制字符,很多都源于遥远的【电报时代】。
  在本文后续的介绍中,还会再聊到这些“控制字符”。

★终端(terminal/TTY)

◇历史演变

  “终端”一词,洋文称之为“terminal”。有时候又被称作 TTY,而 TTY 这个简写就来自刚才介绍的【电传打字机】(teletype printer)。
  因为早期的大型机,其“终端”就是【电传打字机】。那时候的终端,也称作【硬件终端】。

  为啥会有“终端”这个概念捏?你依然需要了解历史的变迁。
  最早期的计算机(大型机)是【单任务】滴——也就是说,每次只能干一件事情。
  到了60年代,出现了一个【革命性】的飞跃——发明了【多任务】系统,当时叫做“time-sharing”(分时系统)。有了“分时系统”,就可以让多个人同时使用一台大型机。而为了让多个人同时操作这台大型机,就引入了【终端】的概念。每一台大型机安装多个终端,每个操作员都在各自的终端上进行操作,互不干扰。

◇(跑题)“约翰·麦卡锡”其人

  聊到这里,稍微跑题一下:
  最早的“分时系统”由 IT 超级大牛“约翰·麦卡锡”(John McCarthy)设计。此人不仅仅是“分时系统它爹”,还是“Lisp 语言它爹”,另外还参与设计了编程语言“ALGOL 60”。而这个“ALGOL 60”编程语言虽然知道的人不多,但该语言深刻影响了后续的 Ada、BCPL、C、Pascal……
  为了让你体会这只大牛到底有多牛。俺引用另一个牛人保罗·格雷汉姆(《
黑客与画家》作者)的观点——他认为在所有编程语言中, Lisp 与 C 是两座无法超越的高峰。而“约翰·麦卡锡”亲自发明了 Lisp 语言,然后又深刻地影响了 C 语言。
  另外,麦卡锡这只大牛还参与创立了“MIT 人工智能实验室”与“斯坦福人工智能实验室”。前者涌现出一大批早期的黑客,其中包括大名鼎鼎的
Richard Stallman(此人开创了:自由软件运动、GNU 社区、GCC、GDB、GNU Emacs ……)。

◇【远程】终端

  跑题结束,言归正传。
  “终端”的好处不光是“多任务”,而且还可以让用户在【远程】进行操作。这种情况下,“终端”通过 modem(调制解调器)与“主机”相连。这种玩法很类似于——互联网普及初期的拨号上网。

  最早的“终端”,本质上就是“电传打字机”——以“打字机”作为输入;以“打印纸”作为输出。这类终端,比较经典的是如下这款:


(Teletype Model 33 ASR)
  到了上世纪70年初,终于有了带【屏幕】的远程终端。
DEC 公司的 VT05 是第一款基于 CRT 显示器的远程终端。


◇内部结构示意图

  下面这张是大型机时代,“终端”与“进程”通讯的示意图。
  图中的 UART 是洋文“Universal Asynchronous Receiver and Transmitter”的缩写(相关维基百科链接在“这里”)。LDISC 是洋文“line discipline”的简写(相关维基百科链接在“这里”)。
  通俗地说,UART 用来处理物理线路的字符传输(比如:“错误校验”、“流控”、等);LDISC 用来撮合底层的“硬件驱动”与上层的“系统调用”,并完成某些“控制字符”的处理与翻译。

◇如今的含义

  如今,“终端”一词的含义已经扩大了——用来指:基于【文本】的输入输出机制。
  在本文后续的章节中, terminal 与 TTY 这两个术语基本上是同义词。

★终端的3种【缓冲模式】——字符模式、行模式、屏模式

◇字符模式(character mode)

  又要说回到【电传打字机】。
  在本文开头,已经聊过这个玩意儿,并且提到——它是基于【字符】传输滴。也就是说,操作员每次在“电传打字机”上按键,对应的字符会立即通过线路发送给对方。这就是最传统的【字符模式】
  通俗地说,“字符模式”也就是【无缓冲】的模式。

◇行模式(line mode)

  不客气地说,“字符模式”是非常SB滴!因为如果你不小心按错键,这个错误也会立即发送出去。
  比如说,你在输入一串很长的命令,结果输到半当中,敲错一个按键,整个命令就废了——要重新再输入一遍。
  所以,当早期的程序员对“字符模式”实在忍无可忍之后,终于发明了【行模式】。
  【行模式】也叫做“行缓冲”。也就是说,终端会把你当前输入的这行先缓冲在本地。只有当你最终按了【回车键】,才会把这一整行发送出去。如果你不小心敲错了一个字符,可以赶紧用“退格键”删掉重输这个字符。
  因此,这种模式称之为【行缓冲】。

  顺便说一下:
  早期的标准键盘,【没有】方向键(“上下左右”这4个键)。不信的话,可以去看本文前面贴的那张“Teletype Model 33 ASR”的照片。
  因为无论是“字符模式”还是“行模式”,都没这个需求。

◇屏模式(screen mode/block mode)

  “行模式”进一步的发展就是【屏模式】。这个玩意儿也叫“全屏缓冲”,顾名思义,终端会缓冲当前屏幕的内容。
  在这种模式下,用户可以利用方向键,操纵光标(cursor)在屏幕上四处游走。
  开发这种类型的软件,比较复杂——程序员至少需要做如下工作:
1. 保存整个屏幕的状态
2. 根据键盘输入,操纵光标(cursor)移动
3. 控制屏幕的哪些区域是光标可达,哪些是不可达;
4. 对于光标可达的部分,控制哪些是“可编辑”,哪些是“只读”;
5. 根据“光标移动”以及某些“特定的按键”(比如“翻页键”),重新绘制屏幕
……
  后来,为了简化”屏模式“的编程,专门搞了一个叫做 curses 的编程库。如今的“ncurses 库”就是从 curses 衍生出来滴(前面加了一个 n 表示 new)。

  前面说了——早期的键盘【没】方向键。有了这个【屏模式】之后,键盘上才开始增加了“方向键”(所以“方向键”位于键盘的扩展区)

◇小结

  上述这三种模式,第1种基本淘汰(仅限于极少数场景);第3种用得也不多。与本文关系比较密切的,其实是【第2种】——行模式。
  为了加深你的印象,用 cat 命令来举例(注:这个命令其实与“猫”【无关】,而是 concatenate 的简写)
  大部分情况下,都是用它来显示某个文件的内容,比如说:cat 文件名 。但如果你运行 cat【没】加任何参数,那么它就会尝试读取你在终端的输入,然后把读到的文本再原样输出到终端。

  在上述动中,你的输入并【没有】直接传递给 cat 进程。要一直等到你按下【回车键】,cat 进程才收到你的输入,并立即打印了输出。

★终端的【回显】

◇“回显”是啥?

  在刚才那个 gif 动画中,当俺逐个输入 test 的每个字母,这些字母也会逐个显示在屏幕上。这种做法叫做【回显】。

◇“回显”地打开与关闭(启用/禁用)

  虽然“回显”很人性化,但某些特殊的场合是【不想】“回显”滴,比如当你输入密码/口令的时候。
  因此,终端提供了某种机制,使得程序能够控制“回显”的启用/禁用。
  对于大多数终端,可以用【Ctrl + S】禁用“回显”,然后用【Ctrl + Q】启用“回显”。
  如果你在禁用“回显”的情况下输入一些文本,当你重新启用“回显”的瞬间,这些文本会一起出现在屏幕上。

  顺便说一下:
  由于【Ctrl + S】在 Windows 上是很常见的组合键。某些菜鸟刚开始玩 Linux 命令行的时候,会习惯性地按这个组合键,结果就禁用了回显。这时候,任何键盘输入都没有反应。菜鸟就以为终端死掉了。

◇历史演变

  对于 Windows 用户来说,【Ctrl + S】实在太常用了,很容易误按。肯定有大量的用户吐槽过 POSIX 终端的这个快捷键。
  那么,为啥要用这两个快捷键来控制“回显”捏?俺又要第 N 次说到【电传打字机】了。
  由于这玩意儿的输出是【打印纸】,其速率比较【慢】。一旦“对方发送字符的速率”高于“自己这边的打印速率”,就需要向对方发一个控制信号,让对方暂停发送;等到自己这边打印完了,再发送另一个控制字符,通知对方继续。
  (注:上述这种玩法,通信领域行话称之为“流量控制/流控”)
  当年用来表示“暂停发送”的控制字符,对应的就是【Ctrl + S】;用来“恢复发送”的控制字符,也正是【Ctrl + Q】。

★(早期的)系统控制台/物理控制台(system console)

  (前面说了)在【没】发明“分时系统”之前,当时的计算机只能执行【单任务】。因此,那时候的大型机只有【一个】操作界面,称之为【控制台】。
  话说那时的“控制台”,真的是一个台子
  后来发明了“分时系统”。如刚才所说——“分时系统”使得大型机可以具备多个终端。在这种情况下,你可以把“控制台”通俗地理解为“本地终端”,而【不】是“控制台”的那些终端,称之为“远程终端”。
  在那个年代,计算机属于【非常非常稀缺】的资源。于是拥有大型机的公司,就可以【出租计算资源】,获得一笔相当可观的收入。他们把大型机的某个“远程终端”租给外来人员使用,然后根据“时间/空间”收取费用。由于资源的稀缺性,当年的 CPU 是按【秒】计费,而内存是按【KB】计费。
  由于“远程终端”可能会被【外人】使用,因此对“远程终端”的【权限】要进行一些限制。如果要进行一些高级别的操作(比如“关闭整个系统”),就只能限制在【控制台】(本地终端)进行。有些公司为了安全起见,还会把“控制台”单独锁在某个“secured room”里面。

★(如今的)虚拟控制台(virtual console)

  到了 PC 时代,传统意义上的【控制台】已经看不到了。但 console 这个术语保留了下来。

◇从“物理 console”到“虚拟 console”

  早期大型机的 console 是【独占】硬件滴——“键盘/显示器”固定用于某个 console 滴。
  【现代】的 POSIX 系统,衍生出“virtual console”的概念——可以让几个不同的 console【共用】一套硬件(键盘/显示器)。“virtual”一词就是这么来滴。
  再重复唠叨一下:不论是早期的“物理控制台”还是后来的“虚拟控制台”,都属于广义上的“终端”。

◇举例:Linux 的 virtual console

  假设你的 Linux 系统没安装图形界面(或者默认不启用图形界面),当系统启动完成之后,你会在屏幕上看到一个文本模式的登录提示。这个界面就是 virtual console 的界面。
  在默认情况下,Linux 内置了【6个】virtual console 用于命令行操作,然后把第7个 virtual console 预留给图形系统。你可以使用 Alt + Fn 或 Ctrl + Alt + Fn 在这几个 console 之间切换(注:上述所说的 Fn 指的是 F1、F2… 之类的功能键)。

◇虚拟控制台的【内部结构】

★终端模拟器(terminal emulator)

  请注意上面那张示意图,图中出现了一个【终端模拟器】,这就是本章节要说的东东。
  如果你对比前面的【TTY 示意图1】与【TTY 示意图2】的变化,会发现——“UART & UART 驱动”没了,然后多了这个【终端模拟器】。
  多出来的这个玩意儿相当于加了一个【抽象层】,模拟出早期硬件终端的效果,因此就【无需改动】系统内核中的其它部分,比如:LDISC(line discipline
  请注意,这个场景下的“终端模拟器”位于操作系统【内核】。换句话说,它属于【内核态】的模拟器。正是因为它处于这个地位,所以能够在“驱动”&“LDISC”之间进行协调。

★伪终端(PTY/pseudotty/pseudoterminal)

◇从“文本模式”到“图形模式”

  前面讲的那些,都是【文本模式】(文本界面)。
  话说到了上世纪80年代,随着【图形界面】的兴起,就出现某种需求——想在图形界面下使用“【文本】终端”。于是就出现了“伪终端”的概念。
  通俗地说,“伪终端”就是用某个图形界面的软件来模拟传统的“文本终端”的各种行为。前面说了,TTY 这个缩写相当于“终端”的同义词;因此“pseudotty” 就衍生出 PTY 这个缩写。

◇从“【内核态】终端模拟器”到“【用户态】终端模拟器”

  在上一个章节中,emulator 运行在系统内核中,因此是“内核态模拟器”;
  等到后来搞“伪终端”的时候,就直接把这个玩意儿从【内核态】转到【用户态】——让它直接运行在【桌面环境】。如此一来,用户就可以直接在桌面环境中使用“终端模拟器”。
  当“终端模拟器”变为【用户态】,它就【无法】直接与“键盘驱动 or 显卡驱动”打交道。在这种情况下,由“GUI 系统”(比如:X11)负责与这些驱动打交道,然后再把用户的输入输出转交给“终端模拟器”。

  xterm。别看它长得丑,它的出现也算是“里程碑”了。

◇内部结构示意图

  很多人把“emulator”与“PTY”混为一谈。实际上两者处于【不同】层次。
  在操作系统内部(内核),PTY 分为两部分实现,分别叫做“PTY master” & “PTY slave”。master 负责与“terminal emulator”打交道;而用户通过 emulator 里面的 shell 启动的其它进程,则与 slave 打交道。
  在这个环节中,“PTY slave”又进一步缩写为“PTS”。如果你用 ps 命令查看系统中的所有进程,经常会看到 PTS 之类的字样,指的就是这个玩意儿。对普通用户而言,看到的是“终端模拟器”的界面,至于 PTY 内部的 master & slave,通常是感觉不到滴。

  为了让大伙儿更加直观,再放一张 PTY 的结构示意图。

★shell——命令行解释器

  费了好多口水,咱们终于聊到 shell 了。
  顺便吐槽一下:
  扫盲命令行的教程,很少会像俺这样,从最基本的概念说起。其导致的后果就是——很多人(甚至包括很多 Linux 程序员)都搞不清“shell、terminal、console、TTY、PTY、PTS”这些概念到底有啥区别。
  在《如何【系统性学习】——从“媒介形态”聊到“DIKW 模型”》一文中,俺特别强调了【基本概念/基础知识】的重要性。这也就是俺为啥前面要费这么多口水的原因。

◇shell VS terminal

  前面所说的“终端”(terminal),本质上是:基于【文本】的输入输出机制。它并【不】理解具体的命令及其语法。
  于是就需要引入 shell 这个玩意儿——shell 负责解释你输入的命令,并根据你输入的命令,执行某些动作(包括:启动其它进程)。

◇常见 shell 举例

  常见的 shell 包括如下这些(为避免排名纠纷,按字母序列出):

bash
csh
fish
ksh
zsh


  在维基百科的“
这个页面”,列出了各种各样的 shell 及其功能特性的对照表。
  如今影响力最大的 shell 是
bash(没有之一)。其名称源自“Bourne-again shell”,是 GNU 社区对 Bourne shell 的重写,使之符合自由软件(GPL 协议)。
  本文后续章节对 shell 的举例,如果没有做特殊说明,均指 bash 这个 shell。

★shell 的基本功能

◇显示【命令行提示符】

  当你打开一个 shell,会看闪烁的光标左侧显示一个东东,那个玩意儿就是【命令行提示符】(参见下图)


(截图中的“命令行提示符”包含了:用户名、当前路径、$分隔符)
  很多 shell 的“命令行提示符”都会包含【当前路径】。当你用 cd 命令切换目录,提示符也会随之改变。这有助于你搞清楚当前在哪个目录下,
可以有效避免误操作
  “命令行提示符”随着当前目录的变化而变化。

  大部分 shell 都可以让你自定义这个【命令行提示符】,使之显示更多的信息量。
  比如说,可以让它显示:当前的时间、主机名、上一个命令的退出码……
  (注:如果你需要开多个【远程】终端,去操作多个【不同】的系统,“主机名”就蛮有用)

◇解析用户输入的【命令行】

  假设你想看一下 /home 这个目录下有哪些子目录,可以在 shell 中运行了如下命令:

ls /home[/pre]
  当你输入这串命令并敲回车键,shell 会拿到这一行,然后它会分析出,空格前面的 ls 是一个外部命令,空格后面的 /home 是该命令的参数。
  然后 shell 会启动这个外部命令对应的进程,并把上述参数作为该进程的启动参数。

◇内部命令 VS 外部命令

  (刚才提到了【外部命令】这个词汇,顺便解释一下)
  通俗地说,“内部命令”就是内置在 shell 中的命令;而“外部命令”则对应了某个具体的【可执行文件】。
  当你在 shell 中执行“外部命令”,shell 会启动对应的可执行文件,从而创建出一个“子进程”;而如果是“内部命令”,就【不】产生子进程。
  那么,如何判断某个命令是否为“外部命令”捏?
  比较简单的方法是——用如下方式来帮你查找。如果某个命令能找到对应的可执行文件,就是“外部命令”;反之则是“内部命令”。
whereis 命令名称[/pre]
◇翻译【通配符】

  玩过命令行的同学,应该都知道:“星号”(*)与“问号”(?)可以作为通配符,用来模糊匹配文件名。
  当你在 shell 中执行的命令包含了上述两个通配符,实际上是 shell 先把”通配符“翻译成具体的文件名,然后再传给相应命令。

◇翻译某些【特殊符号】

  比如说:在 POSIX 系统中,通常用 ~ 来表示当前用户的【主目录】(home 目录)。
  如果你在 shell 中用到了 ~ 这个符号,shell 会先把该符号翻译成“home 目录的【全路径】”,然后再传给相应命令。

◇翻译【别名】

  很多 POSIX 的 shell 都支持用 alias 命令设置别名(把一个较长的命令串,用一个较短的别名来表示)。
  设置了别名之后,当你在 shell 中使用“别名”,由 shell 帮你翻译成原先的命令串。

  举例:
  在《扫盲 netcat(网猫)的 N 种用法——从“网络诊断”到“系统入侵”》一文中,俺使用如下命令创建了 nc-tor 这个别名。
alias nc-tor=’nc -X 5 -x 127.0.0.1:9050′[/pre]  设置完之后,当你在 shell 中执行了这个 nc-tor 命令,shell 会把它自动翻译成 nc -X 5 -x 127.0.0.1:9050

◇历史命令

  大部分 shell 都会记录历史命令。你可以使用某些设定的快捷键(通常是【向上】的方向键),重新运行之前执行过的命令。

◇自动补全

  很多 shell 都具备自动补全的功能。
  该功能不仅对“命令”本身的自动补全,还包括对“命令的参数”进行自动补全。

◇操作“环境变量”

  关于这部分,在下面的“环境变量”章节单独聊。

◇“管道”与“重定向”

  关于这部分,在下面的“管道”章节单独聊。

◇“进程控制”与“作业控制”

  关于这部分,在下面的“进程控制”与“作业控制”章节单独聊。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/77574.html

(0)
上一篇 2024-08-07 07:15
下一篇 2024-08-07 08:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信