接上文
https://t66y.com/htm_data/2103/7/4417855.html★进程的启动与退出◇进程的【启动】及其【父子关系】 一般来说,每个“进程”都是由另一个进程启动滴。如果“进程A”创建了“进程B”,则 A 是【父进程】,B 是【子进程】(这个“父子关系”很好理解——因为完全符合直觉)
有些同学会问,那最早的【第一个】进程是谁启动滴?
一般来说,第一个进程由【操作系统内核】(kernel)亲自操刀运行起来;而 kernel 又是由“引导扇区”中的“boot loader”加载。
◇进程树 在 POSIX 系统(Linux & UNIX),所有的进程构成一个【单根树】的层次关系。进程之间的“父子关系”,体现在“进程树”就是树上的【父子节点】。
你可以使用如下命令,查看当前系统的“进程树”。
pstree[/pre]
(“进程树”的效果图。注:为了避免暴露俺的系统信息,特意【不】用自己系统的截图)
◇初始进程 一般情况下,POSIX 系统的“进程树”的【根节点】就是系统开机之后【第一个】创建的进程,并且其进程编号(PID)通常是 1。这个进程称之为“初始进程”。
(注:上述这句话并【不够】严密——因为某些 UNIX 衍生系统的“进程树”,位于根节点的进程【不是】“初始化进程”。这种情况与本文的主题没太大关系,俺不打算展开讨论)
对于“大部分 UNIX 衍生系统”以及“2010年之前的 Linux 发行版”,系统中的“初始进程”名叫 init;
如今越来越多的 Linux 发行版采用
systemd 来完成系统引导之后的初始化工作。在这些发行版中,“初始进程”名叫 systemd。
你可以用如下命令显示“进程树”中每个节点的“进程编号”(PID),然后就能看到编号为 1 的“初始进程”。
pstree -p[/pre]
◇进程的三种死法 关于进程如何死亡,大致有如下三种情况:
自然死亡 如果某个进程把它该干的事情都干完了,自然就会退出。
这种是最常见的情况,也是最优雅的死法。俺习惯称作【自然死亡】。
自杀 如果某个进程的工作干到半当中,突然收到某个通知,让它立即退出。
这时候,进程会赶紧处理一些善后工作,然后自行了断——这就是【自杀】。
它杀 比“自杀”更粗暴的方式称之为【它杀】。也就是让“操作系统内核”直接把进程干掉。
在这种情况下,进程【不会】收到任何通知,因此也【不】可能进行任何善后事宜。
(注:上述三种死法纯属比喻,以加深大伙儿的印象;不必太较真。十年前俺刚开博客,写过几篇帖子谈“
C++ 对象之死”,也用过类似比喻)
关于“自杀&它杀”的方式,会涉及到【信号】。在下一个章节,俺会单独讨论【进程控制】,并会详细介绍“信号”的机制。
◇“孤儿进程”及其“领养” 如果某个进程死了(退出了),而它的子进程还【没】死,那么这些子进程就被形象地称之为“孤儿”,然后会被上述提到的【初始进程】“领养”——“初始进程”作为“孤儿进程”的父进程。
对应到“进程树”——“孤儿进程”会被重新调整到“进程树根节点”的【直接下级】。
★“进程控制”与“信号”◇用【Ctrl + C】杀进程 为了演示这个效果,你可以执行如下命令:
ping 127.0.0.1[/pre]
如果是 Windows 系统里的 ping 命令,它只会进行4次“乒操作”,然后就自己退出了;
但对于 POSIX 系统里面的 ping 命令,它会永远运行下去(直到被杀掉)。
当 ping 在运行的时候,只要你按下 Ctrl + C 这个组合键,就可以立即终止这个 ping 进程。
◇“Ctrl + C”背后的原理——【信号】(signal) 当你按下了 Ctrl + C 这个组合键,当前正在执行的进程会收到一个叫做【SIGINT】的信号。
如果进程内部定义了针对该信号的处理函数,那么就会去执行这个函数,完成该函数定义的一些动作。一般而言,该函数会进行一些善后工作,然后进程退出。
如果进程【没有】定义相应的处理函数,则会执行一个【默认动作】。对于 SIGINT 这个信号而言,默认动作就是“进程退出”。
上述这2种情况,都属于前面所说的自杀。这2种属于【常规情况】。
下面再来说【特殊情况】——有时候 Ctrl + C【无法】让进程退出。为啥会这样捏?
假如说,编写某个进程的程序员,定义了该信号的处理函数,但在这个函数内部,并【没有】执行“进程退出”这个动作。那么当该进程收到 SIGINT 信号之后,自然就【不会】退出。这种情况称之为——
信号被该进程【屏蔽】了。
◇【谁】发出“Ctrl + C”对应的信号? 很多人(包括很多玩命令行的老手)都有一个【误解】——他们误以为是 shell 发送了 SIGINT 信号给当前进程。
其实不然! 在上述 ping 的例子中,当 ping 进程在持续运行之时,你的键盘输入是关联到 ping 进程的“标准输入”(stdin)。在这种情况下,shell 根本【无法】获取你的按键信息。
实际上,是【终端】获取了你的 Ctrl + C 组合键信息,并发送了 SIGINT 信号。因为【终端】处于更底层,它负责承载你所有的输入输出。因此,它当然可以截获用户的某个特殊的组合键(比如:Ctrl + C),并执行某些特定的动作。
聊到这里,大伙儿会发现——
如果没有正确理解“终端”与“shell”这两者的关系,就会犯很多错误(造成很多误解)。
有的读者可能会问:“终端”如何知道【当前进程】是哪一个?(能想到这点,通常是比较爱思考滴)
俺来解答一下:
当 shell 启动了某个进程,它当然可以拿到这个进程的编号(pid),于是 shell 会调用某个系统 API(比如 tcsetpgrp)把“进程编号”与 shell 所属的“终端”关联起来。
当“终端”需要发送 SIGINT 信号时,再调用另一个系统 API(比如 tcgetpgrp),就可以知道当前进程的编号。
◇对比杀进程的几个信号:SIGINT、SIGTERM、SIGQUIT、SIGKILL SIGINT 在大部分 POSIX 系统的各种终端上,Ctrl + C 组合键触发的就是这个信号。
通常情况下,进程收到这个信号后,做完相关的善后工作,就自行了断(自杀)。
SIGTERM 这个信号基本类似于 SIGINT。
它是 kill & killall 这两个命令【默认】使用的信号。
也就是说,当你用这俩命令杀进程,并且【没有】指定信号类型,那么 kill 或 killall 用的就是这个 SIGTERM 信号。
SIGQUIT 这个信号类似于前两个(SIGINT & SIGINT),差别在于——进程在退出前会执行“
core dump”操作。
一般而言,只有程序员才会去关心“core dump”这个玩意儿,所以这里就不细聊了。
SIGKILL 在杀进程的几个信号中,这个信号是是最牛逼的(也是最粗暴的)。
前面三个信号都是【可屏蔽】滴,而这个信号是【不可屏蔽】滴。
当某个进程收到了【SIGKILL】信号,该进程自己【完全没有】处理信号的机会,而是由操作系统内核直接把这个进程干掉。
此种行为可以形象地称之为“它杀”。
当你用下列这些命令杀进程,本质上就是在发送这个信号进行【它杀】。【SIGKILL】这个信号的编号是 9,下列这些命令中的 -9 参数就是这么来滴。
kill -9 进程号kill -KILL 进程号killall -9 进程名称killall -KILL 进程名称killall -SIGKILL 进程名称[/pre]
为了方便对照上述这4种,俺放一个表格如下:
信号名称编号能否屏蔽默认动作俗称
|
SIGINT | 2 | YES | 进程自己退出 | 自杀 |
SIGTERM | 15 | YES | 进程自己退出 | 自杀 |
SIGQUIT | 3 | YES | 执行 core dump 进程自己退出 | 自杀 |
SIGKILL | 9 | NO | 进程被内核干掉 | 它杀 |
◇【它杀】的危险性与副作用 请注意:
【它杀】是一种比较危险的做法,可能导致一些【副作用】。只有当你用其它各种方式都无法干掉某个进程,才考虑用这招。
有读者在评论区问到了“它杀的副作用”,俺简单解释一下:
一方面,当操作系统用这种方式杀掉某个进程,虽然可以把很多内存相关的资源释放掉,但【内存之外】的资源,内核就管不了啦;另一方面,由于进程遭遇“它杀”,无法完成某些善后工作。
基于上述两点,就【有可能】会产生副作用。另外,“副作用的严重程度”取决于不同类型的软件。无法一概而论。
举例1: 某个进程正在保存文件。这时候遭遇“它杀”可能会导致文件损坏。
(注:虽然某些操作系统能做到“写操作的原子性”,但数据存储可能会涉及多个写操作。当进程在作【多个】关键性写操作时,遭遇它杀。可能导致数据文件【逻辑上】的损坏)
举例2: 还有更复杂的情况,比如涉及跨主机的网络通讯。某个进程可能向【远程】的某个网络服务分配了某个远程的资源,当进程“自然死亡 or 自杀”,它会在“善后工作”释放这个资源;而如果死于内核的“它杀”,这个远程的资源就【没】释放。
◇kill VS killall 这两个的差别在于——前者用“进程号”,后者用”进程名“(也就是可执行文件名)。
对于新手而言,
如果用 kill 命令,你需要先用 ps 命令打印出当前进程清单,然后找到你要杀的进程的编号;而如果要用 killall 命令,就比较省事(比较傻瓜化)。但万一碰到有多个【同名】进程在运行,而你只想干掉其中一个,那么就得老老实实用 kill 了。
◇进程退出码 任何一个进程退出的时候,都对应某个【整数类型】的“退出码”。
按照 POSIX 系统(UNIX & Linux)的传统惯例——
当“退出码”为【零】,表示“成功 or 正常状态”;
当“退出码”【非零】,表示“失败 or 异常状态”。
◇暂停进程 刚才聊“杀进程”的时候提到了“自杀 VS 它杀”。前者比较“温柔”;而后者比较“粗暴”。
对于暂停进程,也有“温柔 & 野蛮”两种玩法。而且也是用 kill 命令发信号。
【温柔】式暂停(SIGTSTP)kill -TSTP 进程编号[/pre] 这个【SIGTSTP】信号类似前面提及的【SIGINT】——
1. 两者默认都绑定到组合键(【SIGINT】默认绑定到组合键【Ctrl + C】;【SIGTSTP】默认绑定到组合键【Ctrl + Z】)
2. 这两个快捷键都是由【终端】截获,并发出相应的信号(具体原理参见本章节的某个小节)
3. 两者都是【可】屏蔽的信号。也就是说,如果某个进程屏蔽了【SIGTSTP】信号,你就【无法】用该方式暂停它。这时候你就得改用【粗暴】的方式(如下)。
【粗暴】式暂停(SIGSTOP)kill -STOP 进程编号[/pre] 这个【SIGSTOP】信号与前面提及的【SIGKILL】有某种相同之处——这两个信号都属于【不可屏蔽】的信号。也就是说,收到【SIGSTOP】信号的进程【无法】抗拒被暂停(suspend)的命运。
与“杀进程”的风格类似——当你想要暂停某进程,应该先尝试“温柔”的方法,搞不定再用“粗暴”的方法(套用咱们天朝的老话叫“先礼后兵”)。
◇恢复进程 当你想要重新恢复(resume)被暂停的进程,就用如下命令(该命令发送信号【SIGCONT】)
kill -CONT 进程编号[/pre]
◇引申阅读 除了前面几个小节提到的信号,POSIX 系统还支持其它一些信号,具体参见维基百科的“
这个页面”。
★作业控制(job) 聊完了“进程控制”,再来聊“作业控制”。
(注:这里所说的“作业”是从洋文 job 翻译过来滴)
◇啥是“作业”? “作业”是 shell 相关的术语,用来表示【进程组】的概念(每个作业就是一组进程)。
比如说,当你用“管道符”把若干命令串起来执行,这几个命令对应的进程就被视作【一组】。
(注:“管道符”的用法,后面某个章节会介绍)
◇同步执行(前台执行) VS 异步执行(后台执行) 大部分情况下,你在 shell 中执行的命令都是“同步执行”(或者叫“前台执行”)。对于这种方式,只有当命令运行完毕,你才会重新看到 shell 的“命令行提示符”。
如果你以“异步执行”的方式启动某个外部命令,在这个命令还没有执行完的时候,你就可以重新看到“命令行提示符”。
请注意:
对于【短】寿命的外部命令(耗时很短的外部命令),“同步/异步”两种方式其实【没】啥区别。比如 ls 命令通常很快就执行完毕,你就感觉不到上述两种方式的差异。
只有当你执行了某个【长】寿命的外部命令(其执行时间至少达到若干秒),上述这两种方式才会体现出差别。
到目前为止,本文之前聊的命令执行方式,都属于“同步执行”;如果想用【异步】,需要在整个命令的最末尾追加一个半角的 & 符号。
【同步】方式举例 下列命令以【同步】的方式启动火狐浏览器,只有当你关闭了火狐,才会重新看到 shell 的命令行提示符。
firefox[/pre]
【异步】方式举例 下列命令以【异步】的方式启动火狐浏览器。你刚敲完回车,就会重新看到 shell 的“命令行提示符”(此时火狐依然在运行)
firefox &[/pre]
以“同步”方式启动的进程,称作“【前台】进程”;反之,以“异步”方式启动的进程,称作“【后台】进程”。
◇“前台”切换到“后台” 假设当前的 shell 正在执行某个长寿命的【前台】进程,你可以按【Ctrl + Z】,就可以让该进程变为【后台】进程——此时你立即可以看到“命令提示符”。
只要你不是太健忘,应该记得前一个章节有提到过【Ctrl + Z】这个组合键——它用来实现”【温柔】式暂停“,其原理是:向目标进程发送【SIGTSTP】信号。
◇“后台”切换到“前台” 假设当前 shell 正在执行某个后台进程。由于该进程在【后台】执行,此时有“命令提示符”,然后你在 shell 中执行 fg 命令,就可以把该后台进程切换到【前台】。
某些爱思考的同学会问了——如果同时启动了【多个】“后台进程”,fg 命令会切换哪一个捏?
在这种情况下,fg 命令切换的是【最后启动】的那个。
如果你有 N 个“后台进程”,你想把其中的某个切换为“前台进程”,这时候就需要用到 jobs 命令。该命令与乔布斯同名 :)
举例:
假设俺同时启动了 vim 与 emacs 作为后台进程,先用 jobs 命令列出所有的后台进程。假设该命令的输出是如下这个样子。
$ jobs[1] running vim[2] running emacs[/pre] 在上述的终端窗口,中括号里面的数字称作“job id”。你可以用 fg 命令搭配“job id”,把某个后台进程切换到前台。
(在本例中)如果你想切换 emacs 到前台,就运行 fg %2,如果想切换 vim 就运行 fg %1(以此类推)
◇引申阅读 想进一步了解“作业控制”,可以参考维基百科(
这个链接)。
★环境变量(environment variable)◇“环境变量”是啥? 所谓的“环境变量”,你可以通俗理解为某种【名值对】——每个“环境变量”都有自己的【名称】和【值】。并且名称必须是【唯一】滴。
◇如何添加并修改“环境变量”? 在 bash(或兼容 bash 的其它 shell),你可以用 export 设置环境变量。比如下面这个命令行设置了一个“环境变量”,其名称是 abc,其值是 xyz
export abc=xyz[/pre]
假如你要设置的【值】包含空格,记得用双引号引用该值(示例如下)。
export abc="program think"[/pre]
由于“环境变量”的名称具有【唯一性】,当你设置【同名】的“环境变量”就等同于对它的【修改】。
◇如何查看“环境变量”? 设置完之后,你可以用 env 命令查看。该命令会列出【当前 shell】中的【全部】“环境变量”。
◇“环境变量”的【可见性】和【可继承性】 某个进程设置的“环境变量”,其【可见性】仅限于该进程及其子进程(也就是“进程树”中,该进程所在的那个枝节)。
基于上述的【可见性】原则,你在某个 shell 中设置的“环境变量”,只在“该 shell 进程本身”,以及通过该 shell 进程启动的“其它子进程”,才能看到。
另外,如果系统关机,所有进程都会退出,那么你采用上一个小节(export 方式)设置的“环境变量”也就随之消失了。
为了让某个“环境变量”永久生效,需要把相应的 export 命令添加到该 shell 的初始化配置文件中。对于 bash 而言,也就是 ~/.bashrc 或者 ~/.profile
估计有些同学会问:上述这两个初始化配置文件,有啥差别捏?
俺如果有空,会单独写一篇关于 bash 的定制教程,到时候再聊这个话题。
◇“环境变量”有啥用? 通俗地说,“环境变量”是某种比较简单的“IPC 机制”(进程通讯机制),可以让两个进程共享某个简单的文本信息。
举例:
很多知名的软件(比如:curl、emacs)都支持“以环境变量设置代理”。
如果你按照它的约定,在 shell 中设置了约定名称和格式的“环境变量”,然后在【同一个】shell 中启动这个软件,(由于环境变量的【可继承性】)该软件就会看到这个“环境变量”,并根据“环境变量”包含的信息,设置代理。
★“标准流”(standard stream)与“重定向”(redirection)◇进程的3个“标准流” 在 POSIX 系统(Linux & UNIX)中,每个进程都内置了三个“标准流”(
standard stream),分别称作:“标准输入流”(stdin),“标准输出流”(stdout),“标准错误输出流”(stderr)。
当进程启动后,在默认情况下,stdin 对接到终端的【输入】;stdout & stderr 对接到终端的【输出】。示意图如下:
(三个【标准流】的示意图)
如果你是程序员,俺补充一下:
当你在程序中打开某个文件,会得到一个“文件描述符”(洋文叫“
file descriptor”,简称 fd)。fd 本身是个整数,程序员可以通过 fd 对该文件进行读写。
而进程的三个【标准流】,就相当于是三个特殊的 fd。当进程启动时,操作系统就已经把这三个 fd 准备好了。
由于这三个玩意儿是预先备好滴,所以它们的数值分别是:0、1、2(参见上图中 # 后面的数字)。
◇演示“标准流”的实际效果 在本文前面的某个章节,俺已经用 gif 动画演示了终端的“行模式”。
动画中的 cat 命令同样可以用来演示“标准输入输出”。俺把那个动画再贴一次。
(动画:“标准输入输出”的效果)
请注意,第1行 test 是针对 cat 进程的【输入】,对应于【stdin】(你之所以能看到这行,是因为前面所说的【终端回显】)
第2行 test 是 cat 进程拿到输入文本之后的原样输出,对应于【stdout】。
◇“标准流”的【重定向】 所谓的【重定向】大体上分两种:
1. 【输入流】重定向 把某个文件重定向为 stdin;此时进程通过 stdin 读取的是该文件的内容。
这种玩法使用小于号(<)
2. 【输出流】重定向 把 stdout 重定向到某个文件;此时进程写入 stdout 的内容会【覆盖 or 追加】到这个文件。
这种玩法使用【单个】大于号(>)或【两个】大于号(>>)。前者用于【覆盖】文件内容,后者用于【追加】文件内容。
另外,有时候你会看到 2>&1 这种写法。它表示:把 stderr 合并到 stdout。
(注:前面俺提到过——stdout 是“数值为 1 的文件描述符”;stderr 是“数值为 2 的文件描述符”)
◇【重定向】举例 cat 的例子 下面这个命令把某个文件重定向到 cat 的 stdin。
cat < 文件名[/pre]
很多菜鸟容易把上面的命令与下面的命令搞混淆。
请注意:上面的命令用的是【输入重定向】,而下面的命令用的是【命令行参数】。
cat 文件名[/pre]
cat 命令还可以起到类似“文件复制”的效果。
比如你已经有个 文件1,用下面这种玩法,会创建出一个内容完全相同的 文件2。
cat < 文件1 > 文件2[/pre] 某些同学可能会问了:既然能这么玩,为啥还需要用 cp 命令进行文件复制捏?
原因在于:cat 的玩法,只保证内容一样,其它的不管;而 cp 除了复制文件内容,还会确保“目标文件”与“源文件”具有相同的属性(比如 mode)。
更多的例子 在之前那篇《
扫盲 netcat(网猫)的 N 种用法——从“网络诊断”到“系统入侵”》,里面介绍了十多种 nc 的玩法。很多都用到了【重定向】。
★匿名管道(anonymous pipe)◇“匿名管道”的【原理】 在大部分 shell 中,使用竖线符号(|)来表示【管道符】。用它来创建一个【
匿名管道】,使得前一个命令(进程)的“标准输出”关联到后一个命令(进程)的“标准输入”。
◇举例 俺曾经在“
这篇博文”中介绍过——如何用 netstat 查看当前系统的监听端口。
对于 Windows 系统,可以用如下命令:
netstat -an | find "LISTEN"[/pre] 对于 POSIX 系统,可以用如下命令:
netstat -an | grep "LISTEN"[/pre]
在上述两个例子中,都用到了【管道符】。因为 netstat -an 这个命令的输出可能会很多,先把它的输出通过【匿名管道】丢给某个专门负责过滤的命令(比如:POSIX 的 grep 或 Windows 的find)。当这个过滤命令拿到 netstat 的输出内容,再根据你在命令行参数中指定的【关键字】(也就是上述例子中的 LISTEN),过滤出包含【关键字】的那些【行】。
最终,你看到的是“过滤命令”(grep 或 find)的输出。
◇【串联的】匿名管道(chained pipeline) 前面的例子,可以用来列出当前系统中所有的监听端口。
现在,假设你运行了 Tor Browser,然后想看看它到底有没有开启 9150 这个监听端口,那么你就可以在上述命令中进行【二次过滤】(具体命令大致如下)。这就是所谓的【串联】。
netstat -an | grep "LISTEN" | grep "9150"[/pre]
◇“匿名管道”与“作业”(进程组) 用“匿名管道”串起来的多个进程,构成一个“作业”(这点前面提到了)。
你可以尝试执行某个长寿命的,带管道符的命令行,然后用 Ctrl + Z 切到后台,再执行 jobs 看一下,就能看出——该命令行对应的【多个】进程属于同一个 job。
★批处理(batch)◇啥是“批处理”? 通俗地说就是:同时执行多个命令。
为了支持“批处理”,shell 需要提供若干语法规则。而且不同类型的 shell,用来搞“批处理”的语法规则也存在差异。
在本章节中,俺以 bash 来举例。
◇【无】条件的“批处理” 如果你把多个命令写在同一行,并且命令之间用半角分号隔开,这种玩法就属于【无条件】的批处理执行。
举例:
假设当前目录下有一个 abc.txt 文件,然后要在当前目录下创建一个名为 xxx 的子目录,并把 abc.txt 移动到这个新创建的子目录中。你可以用如下方式搞定(只用【一行】命令)
mkdir ./xxx/; mv abc.txt ./xxx/[/pre]
为啥这种方式叫做“【无条件】批处理”捏?因为不管前一个“子命令”是否成功,都会继续执行下一个“子命令”。
请注意:
虽然俺上述举例只使用了两个“子命令”,但实际上这种玩法可以把 N 个“子命令”串起来。
◇【有】条件的“批处理” 与“无条件”相对应的,当然是“有条件”啦。
这种玩法的意思是——后一个“子命令”是否执行,取决于【前一个】“子命令”的结果(成功 or 失败)。
(注:如何界定“成功/失败”,请参见前面某个章节聊到的【进程退出码】)
【有】条件的批处理,常见的方式有两种,分别是【逻辑与】、【逻辑或】。
逻辑与(语法:&&) 只要前面的某个“子命令”【失败】了,就【不再】执行后续的“子命令”。
举例:
还是拿前一个小节的例子。如下方式使用了“逻辑与”。如果创建子目录失败,就【不再】执行“移动文件”的操作
mkdir ./xxx/ && mv abc.txt ./xxx/[/pre]
逻辑或(语法:||) 只要前面的某个“子命令”【成功】了,就【不再】执行后续的“子命令”。
举例:
把上述例子进一步扩充,变为如下:
mkdir ./xxx/ && mv abc.txt ./xxx/ || echo "FAILED!!!"[/pre]
这个有点复杂,俺稍微解释一下:
你把前面两句看作一个【整体】。其执行的逻辑参见前面所说的“逻辑与”。然后这个“整体”与后面的那句 echo 再组合成【逻辑或】的关系。
也就是说,如果前面的“整体”成功了,那么就【不】执行 echo(【不】打印错误信息);反之,如果前面的“整体”失败了,就会打印错误信息。
★shell 脚本 虽然前一个章节拿 bash 来举例。但其实有很多其它类型的 shell 都支持类似的“批处理”机制。
只要某个 shell 支持刚才所说的【有条件批处理】的机制,它就已经很接近【编程语言】了。
于是很自然地,那些 shell 的作者就会把 shell 逐步发展成某种【脚本语言】的解释器。然后就有了如今的“shell script”(shell 脚本)和“shell 编程”。
由于“shell 编程”这个话题比较大。哪怕俺只聊 bash 这一类 shell 的编程,也足够写上几万字的博文。考虑到本文已经很长了,这个话题就不再展开。
对此感兴趣的同学,可以参考俺分享的电子书。具体参见
电子书清单的如下几本(这几本都位于【IT类 / 操作系统 / 使用教程】分类目录下)
《
Shell 脚本学习指南》(Classic Shell Scripting)
《
Linux 与 UNIX Shell 编程指南》(Linux and UNIX Shell Programming)
《
高级 Bash 脚本编程指南》(Advanced Bash-Scripting Guide)
上述这几本,都属于俺在《
如何【系统性学习】——从“媒介形态”聊到“DIKW 模型”》中提到的【入门性读物】。最后一本书的名称中虽然有“高级”字样,不过别怕——其内容的5个部分,有4部分都是在讲基础的东西,只有最后一部分才稍微有一点点深度。
★结尾 由于这篇涉及的内容比较杂,跨度也比较大。可能会有一些俺没覆盖到的地方。欢迎在博客留言中补充。
如果你发现本文的错误之处,也欢迎批评指正 :)
俺博客上,和本文相关的帖子(需翻墙):
《
扫盲 Linux:新手如何搞定 Linux 操作系统》
《
扫盲 Linux:如何选择发行版》
《
扫盲 netcat(网猫)的 N 种用法——从“网络诊断”到“系统入侵”》
《
多台电脑如何【共享】翻墙通道——兼谈【端口转发】的几种方法》
《
如何让【不支持】代理的网络软件,通过代理进行联网(不同平台的 N 种方法)》
《
扫盲操作系统虚拟机》(系列)
《
如何【系统性学习】——从“媒介形态”聊到“DIKW 模型”》
版权声明本博客所有的原创文章,作者皆保留版权。转载必须包含本声明,保持本文完整,并以超链接形式注明作者[url=mailto:program.think@gmail.com]编程随想[/url]和本文原始地址:
https://program-think.blogspot.com/2019/11/POSIX-TUI-from-TTY-to-Shell-Programming.html
赞(3)