GUIDE TO UNIX AND LINUX (8-13)
本文是一篇读书笔记。主要介绍了 Unix 手册和 shell 的使用。
我选择性忽略了原书一部分内容,将我认为重要的部分摘录于此。
本读书笔记适合有一定 Linux 基础的朋友参考。
这是一篇语雀副本,用于同步我的个人博客。
^本文目前仅在语雀、个人博客发布发布,内容为作者 tou 原创,暂不允许全文转载。谢谢!
Chapter 8 一些程序的介绍
再次强调:Unix 不是一个具体的操作系统,而是一族操作系统(如 Linux、FreeBSD、Solaris 等)的总称。
which
命令:查看某程序是否存在
如果使用的 shell 是 Bash,也可用 type
替代;如果使用的是 Korn shell 也可用 whence
命令替代。
由图可知,笔者使用的 centOS 7.0 默认使用的 shell 是 Bash
bc
命令:调用计算器程序
有些程序执行一项任务后就退出,有些程序会进入一个环境,在这个环境中通过一条条指令和程序交互。因此,当结束该程序的工作时,需要输入一个特殊的命令退出。我们以计算器程序命令 bc
为例:
8.12-8.16 介绍了计算器的详细用法。此略。
cal
命令:调用日历程序
cal + {month} + {year}
: 显示本月日历并标出当前日期位置cal -j + {month} + {year}
: 同上,不过显示的内容不是当月日期号,而是当年日期号(可用查看是否是闰年)cal + {year} + < | less>
: 按页显示此年份的日历
可以查看一下 1752 年 9 月的日历,你会发现 9 月 2 日后就是 14 日。这跟当时世界上日历的修正有关。
Unix 中确实存在一个·日历提醒服务的程序,可以用
calendar
命令使用。略。
查看系统信息
uptime
命令
显示系统运行时间、当前登录的用户标识有几个、等待执行的程序数量(1 分钟、5 分钟、15 分钟)。
hostname
命令
显示计算机名称
uname
命令
显示操作系统名称。
使用 -a
选项(all information,全部信息),可以看到内核版本。
查看用户自身信息
whoami
命令
显示当前用户标识
quota
命令
我没能成功查看…
查看磁盘空间限制配额
查看其他用户信息
users
命令
显示当前登录系统的所有用户标识。
who
命令
显示用户标识、终端名称、登陆时间
w
命令
更详细的用户信息:who is doing what
也可以只查看某用户:w + {用户标识}
Chapter 9 Unix 手册和 Info
RTFM
原意:”Read the fucking manual“(阅读该死的手册)
委婉一点:”Read the fine manual”(阅读精美的手册)
RTFM 表示的是 Unix 传统:在请求帮助之前尽最大的努力自己解决问题。比如阅读 Unix 联机手册(程序创建者会上传程序的文档资料)以及通过搜索引擎在 Internet 查找信息。
“I have RTFM’d for the last two hours, and I can’t figure out how to connect my cat to the Internet.”(其中 RTFM’d 表示 RTFM 的过去分词)
联机手册
man {命令名}
,manual 的缩写。
查阅 cp
命令(复制文件)的文档资料,输入 man cp
:
快捷键
可以查看 cp
的联机手册,它是按页(less)展示的,之前提到过,按 <Space>
/ <b>
(或方向键)查看下一页 / 上一页。你还可以键入前缀 /
和 ?
进行查找。手册中更多的使用命令见下:
‘!’ is Bang
你可以同时打开两个终端,一个执行原先的任务,一个用来打开联机文档查阅说明。
但其实在查阅文档的时候,你可以键入前缀 !
后输入一条 shell 命令并执行获得结果。我们称此处的 !
为 “Bang”。
底部显示了运行的 shell 命令和结果。
文中 cp 被高亮了是因为之前执行了/cp
你甚至可以在联机文档里再次查阅提及的其它联机文档。
指定节号
Unix 联机手册被分成 8 节内容:
当你使用 man kill
查阅 kill
命令时,你看到的是第 1 节中的 kill 内容:
默认情况下 man 假定我们参考第 1 节。
在最末尾,你会发现有一个 kill(2):
查阅特定节的说明:man 2 kill
⚠
这里我(centos 7)遇到了联机文档查阅不到 kill(2) 的问题,原因可能是没有安装完整 man-pages。
- 使用命令:
sudo yum install man-pages
- 报错:could not resolve host: mirror…
- 自己
ping
了一下:ping www.baidu.com
,发现确实网络连接有问题。- 根据 CentOS7 更新 yum 报 Could not resolve host:mirrorlist.centos.org; Unknown error 解决办法,修改了/etc/sysconfig/network-scripts/ifcfg-ens33 文件,将 onBoot 改为 true,重新启动网络服务
service network restart
- 重新安装:
sudo yum install man-pages
- 可以正常查阅了:
man 2 kill
此时查阅到了第 2 节中的 kill
:
还有更多的查法,见 9.9,此略。
大多时候,我们只对第 1 节(命令)感兴趣,因此没必要指定节号。
当寻找与编程(2、3、4、5、7)或者系统管理(4、7、8)相关信息的时候才使用节号查询。
说明书页的格式
你会发现每一个说明书都按以下组织方式,这是因为有些说明书非常长,长到可以成为独立的参考手册。
whatis
命令:查看命令简介
whatis {命令1} {命令2}
仅展示说明书页的 Name 部分。
可以查看 1 条或多条命令的简介。
与之等价的命令是: man -f {命令1} {命令2}
⚠
这里我遇到 whatis 无法正常展示内容的情况。即man time
显示了内容,但是使用whatis time
不显示 Name 而是 “Nothing appropriate”.
原因是因为whatis
命令本质是去索引一个数据库,有可能是我们刚刚更新了 manual,数据库还没来得及更新,此时我们可以手动更新数据库:
- centOS 版本 < 7 时:
sudo makewhatis
- centOS 版本 ≥7 时:
sudo mandb
参考:使用 whatis 命令时提示 nothing appropriate、centos whatis: nothing appropriate
apropos
命令:反向搜索
当你不清楚该使用哪个命令时,apropos
会尝试寻找说明页 Name 中匹配的单词。
该命令不区分大小写。
apropos 是法语,其重音在后,且 ‘s’ 不发音:a-pro-poe。在法语中等价 “related to”,在英语中作为前置词表示 “concerning”(关联意)。
与之等价的命令是:man -k {命令}
foo、bar 和 foobar
术语 foobar , foo , bar , baz 和 qux 经常在计算机编程或计算机相关的文档中被用作通用标识符(generic identifier)的名字。
当变量,函数,或命令本身不太重要的时候, foobar , foo , bar ,baz 和 qux 就被用来充当这些实体的名字,这样做的目的仅仅是阐述一个概念,说明一个想法。这些术语本身相对于使用的场景来说没有任何意义。
Foobar 经常被单独使用;而当需要多个实体举例的时候,foo,bar,和 baz 则经常被按顺序使用。
Info 系统
类似 Unix 联机手册,这个主要记录 GNU 实用工具。(因为许多类型的 Unix,包括几乎所有的 Linux 系统,都使用了 GNU 实用工具,详见第二章)
Info 有点类似早期的网页,里面充满可跳转的链接。其体系结构由 Richard Stallman (自由软件基金会和 GNU 项目创始人)设计的。
Indo 是 Texinfo 的一部分,后者是 GNU 项目的官方文档资料系统。
关于 info 更多的使用从 9.17 开始查看,此略。
Chapter 10 命令语法
一次输入多条命令
使用分号(;)隔开。注意:命令行行尾不需要分号。
命令语法
1 | $ <command> + {option} + {argument} |
选项(options)有时候也被称为开关(Switches)或者标志(Flags)。
toulzx:
关于参数 argument 与 parameter,它们都有参数、变量意。
在编程中(主要是 C99 之后),习惯将 argument 定义为“实参”,将 parameter 定义为“形参”
如:现有函数foobar(int foo, int bar){...}
,我们调用它foobar(2, 3);
。我们认为,前者里的foo
、bar
为形参(parameter),后者里的2
、3
为实参。
但在其它地方,它们的区别并不是非常明显。
我们也常认为方程f(x) = 5x + 17
中的 x 就是参数(虽然在汉语里,我们更愿意称呼x
为“变量”,而称5
为参数)。
我认为这里不要带入中文的思维,统一使用英文看待。
语法规则:
- [方括号] 的项是可选的
- 不在 [方括号] 的项是必填的
- 黑体字是必须按原样准确键入的
- 斜体字必须用适当值替代(argument)
- 省略号(…)表示可任意重复多次
- 如果 [一个选项和一个参数] 组合在一起,那么该选项和参数必须同时使用
- 由竖线(|)字符分开的 [两个或多个项],表示可以从中选择一个项使用。
注意,这个规则在不同系统中有一点不同,如上 centOS 系统中用绿色来表示 argument
选项 -
选项顺序不重要。
1 | ls -l -F file1 |
选项的大小写有区别!
1 | ls -f file1 |
使用 man ls
可以查到这两个不同的选项:
可以尝试一下我们之前提到的 man -f
、man -k
选项 --
GNU 使用工具开发出来之后。
原有的选项由于设计为可组合,导致无法使用更长的选项(原来的不够用了)。
为不改变原有的选项,通过这种方式进行拓展。由于 Linux 使用 GNU 实用工具,所以会常见到这种 --
的选项,而在其它大多数系统中,通常只有单字符的选项。
很多长选项的功能被设置得与短选项一致,便于记忆:
1 | ls -r |
尽管确实存在一些奇葩的选项,但总归很少见。
当我们谈论选项时
在 20 世纪 90 年代中期之前学习 Linux 的,习惯将其称为 “minus”(相比于 “hyphen” 更容易表达),比如,ls -l
发音为 “L-S-minus-L”。
但在 Linux 文化中,我们常将连字符称为 “dash”,特别是年轻用户中。比如,ls --help
发音为 “L-S-dash-dash-HELP”。
Chapter 11&12&13 shell
shell 的历史略。
Bash 是目前最流行的 shell。
使用的是 Linux 登录的 shell 大概率是 Bash;如果使用的是商业版 Unix,登录的 shell 可能就是 Korn shell;如果使用的是 FreeBSD,那么登录的 shell 可能是 Tcsh。
如何改变登录的默认 shell,见 11.5-11.6,此略。
你还可以在 shell 中打开另一个 shell,并通过 exit
命令退回上一个 shell。
交互式 shell 和非交互式 shell
shell 既可以充当用户界面,也可以做为脚本解释器。
当根据 shell 提示输入命令 -> shell 处理命令 -> shell 返回提示,此时我们使用的是交互式 shell,shell 充当用户界面。
当我们创建一组命令(即 shell 脚本)并保存在文件中,运行脚本时,shell 从文件中读取命令,并且在不需要输入的情况下处理所有的命令。此时我们使用的就是非交互式的 shell。
环境、进程与变量
在进程运行过程中,它需要访问环境,即一组用来存放信息的变量。
变量,一组存储数据的实体(标识符和数据 || 变量名和值)。变量只有 4 中操作:
- 创建
- 查看
- 修改
- 销毁
在第 7 章中,我们使用了
echo $TERM
来查看系统使用终端的类型。常见的 TERM 值有 xterm、linux、vt100、ansi。
变量名可以用大小写字母、数字、下划线构成,且第一个字符不能是数字。全局变量通常习惯采用大写字母命名,而局部变量通常采用小写字母命名。
对于 shell 来说,变量几乎总数 hi 存储一种类型的数据:字符串。未赋值的变量拥有一个 null 值。
父进程和子进程
当我们在 shell 中启动了另一个程序,如 vi 文本编辑器。我们说一个进程 shell 启动了另一个进程 vi。其中,第一个进程称为父进程或双亲,第二个进程称为子进程或孩子。
在创建子进程的时候,系统为子进程复制了父进程的环境,我们称子进程继承了父进程的环境。这意味着:父进程可以访问到的所有的环境变量,子进程通通可以访问。
vi 能够访问到 shell 的环境变量 TERM,因此 vi 可以正确地格式化它的输出,以适应特定的终端。
Bourne shell 家族 与 C-shell
Bourne shell 家族:
- Bash
- Korn shell
C-Shell 家族:
- C-Shell
- Tcsh
提到这两个家族,是因为这两个家族的 shell 处理局部变量和全局变量时有不同。
局部变量和全局变量
shell 变量是创建它们的 shell 的局部变量。环境变量是全局变量。
严格从编程意义上来说,环境变量不完全是全局的,因为子进程对环境变量的修改不会传到父进程。
Bourne shell 家族中,shell 变量也默认是是大写的(和环境/全局变量一样)。C-shell 则没这种习惯。
对于大多数编程语言,变量或是局部的、或是全局的,但 shell 不是这样,有些变量同时拥有局部变量和全局变量的含义!(有些变量对于 shell 有用,应该是局部变量,但同时对于 shell 的启动进程也有作用,这又应该是环境变量)
Bourne shell 的处理方式是将变量只定义为如下两种形式:
- 局部变量
- 既是局部变量又是全局变量
也就是说,Bourne shell 没有一个只属于环境变量的变量!
1 | # 创建局部变量 HAPPYLINUX |
C-shell 家族中,环境变量通过命令 setenv
创建,并以大写命名;shell 变量由 set
命令创建,并且以小写字母命名。
为了解决上面提到的特殊变量(既是局部变量又是全局变量)的问题,C-shell 定义了少数几个特殊的 shell 变量,分别绑定到对应的环境变量,当其中一个被修改后,另一个会自动修改。
常见的双名称变量:
shell 变量 | 环境变量 | 含义 |
---|---|---|
cwd | PWD | 当前/工作目录 |
home | HOME | home 目录 |
path | PATH | 搜索程序的目录 |
term | TERM | 正在使用的终端类型 |
user | USER | 当前用户标识 |
cwd 和 PWD 这个组合确实有点奇葩:
cwd: current/working directory
PWD(pwd): print working directory
pwd 来源于最初的 Unix 命令,那是一个计算机输出真正打印在纸张上的时代。
显示环境变量:env
或 printenv
env | less | sort
显示 shell 变量和它们的值:set
Bourne shell 家族中你看到的变量名都是大写的,C-shell 家族中则是小写。
在 Bourne shell 中如何确定一个“局部+全局”变量
奇怪但真实:比较 env
和 set
的输出…
显示(一个)变量并使用它的值:echo
1 | echo I Love PCA |
echo 默认后面的是一串字符串的值,因此用 $
符(dollar)表示后面的变量。
${}
的表示方法允许变量与字符串不分隔显示。
元字符
<
与 >
是元字符(第 15 章),表示重定向。
echo
的更多用法
显示多个变量的值:
1 | echo $HOME $TERM $PATH $SHELL |
在 Korn shell 中,你还可以使用
echo
,以前为了兼容不同系统,现在不需要了。
在 Bourne shell 家族使用变量
1 | # 创建/修改局部变量 |
在 C-Shell 家族使用变量
setenv
:
1 | setenv MY_NAME toulzx |
注意:有空格,没有等号!
set
:
1 | set his_name=VanPCA |
注意:有空格、有等号!
注意:区分将变量设为空(null)和删除变量。
shell 选项
Bourne shell 家族根据 shell 选项设置交互方式。
你可以使用 set +o
或 set -o
查看 shell 选项。
略。
机器可读(machine-readable)和人类可读(human-readable)
机器可读是指以适合程序的方式输出,比如对于一串数据,以逗号分隔的一串字符串显然适合程序输出。尽管我们难以阅读。
比如
set +o
和set-o
GNU 实用工具很多都提供了人类可读的选项。
命令和定制
元字符
我们使用分号;
在一行中分隔各条命令,实现同一个命令行上多条命令的执行。因此分号对 shell 有特殊的含义,所以它就是一个元字符。
shell 中使用的元字符:
引用和转义
使用转义字符 \
引用字符
1 | echo The search path is $PATH\; the shell is $SHELL. |
我们通过转义字符进行“引用”,被“引用”的字符不再发挥元字符的作用,而是作为普通字符使用。
使用单引号 '
引用一串字符串(除了 <Return>
、<Tab>
、<Space>
)
1 | # 示例:It is warm (and sunny); come over & visit. |
当使用单引号进行“引用”的时候,被引用的字符串内所有的字符全部被转义,不再发挥元字符的作用。
使用双引号 "
引用一串字符串(除了 $
、```、\
、 <Return>
、<Tab>
、<Space>
)
1 | echo "My userid is <$USER>; my terminal is<$TERM>" |
双引号会“引用”字符串中的所有内容,除了 $
、```、\
。
强引用与弱引用
单引号比双引号功能更强,称单引号为强引用(strong quote),双引号为弱引用(weak quote)。
反斜线(转义字符)比单引号、双引号都要强。
但单引号还是会使反斜线被“引用”,不要误会下面的例子:
1 | # 转义字符并不会转义。 |
新行字符的引用
反斜线甚至可以(立刻)转义新行字符 <Return>
,意味着,你可以在命令行中实现换行输入,这在一条命令十分长的时候很有用。(toulzx: 比如炼丹的时候!!)
相比之下,未结束的单引号会等待匹配,在此之前的新行字符都将生效。
以上是 Bourne shell 家族的处理方式,如果是 C-Shell 家族,第一次回车就会报错了(缺少匹配)。
内部命令与外部命令
内部命令是少数的。
内部命令一般没有自己的说明书页,其包含在 shell 发说明书页中。
或者使用 type
命令查看。
使用内部 help
命令快速查看关键字的语法
1 | help -s case |
外部命令与搜索路径
date
是一条外部命令。当我们输入 date
命令的时候,shell 会去寻找 date 程序,然后,运行它。
shell 通过检查环境变量 PATH 去寻找程序,PATH 包含一系列目录的名称,我们称其为搜索路径。你可以使用 echo $PATH
查看 PATH 变量。
PATH:存放程序的目录:搜索路径
shell 查找外部命令时,会寻找一下路径的程序:
1 | # 存放系统中供所有用户使用的程序,Unix 安装时自动设置 |
在 Bourne shell 家族中,你可以这样修改搜索路径:
1 | # export 使变成“全局+局部”变量 |
在 C-Shell 家族中,你可以这样修改搜索路径:
1 | # 设置局部变量 path 会自动设置全局变量 PATH |
路径添加的顺序决定了 shell 检查的顺序,shell 会运行找到的第一个程序。
一般都会把用户自己的路径往后放。
确保搜索路径中不包含工作目录,或者其它用户可能访问的其它任何目录。
这常常是黑客创建后门的技巧:寻找一种会以 root 账户运行的程序,并修改它。当 root 账户下一次运行的时候,就会利用权限创建秘密文件(后门),使用这个文件就可以为黑客获得超级用户的访问权限。
修改 shell 提示
常用于 shell 提示的环境变量
对于 Bourne shell 家族,需要修改名为 PS1
的环境变量。
PS1: prompt for the shell, number 1
还存在类似的 PS2,PS3,PS4,这里不需要用到。
1 | export PS1="% " |
对于 C-Shell 家族,需要修改名为 prompt
的 shell 变量。
1 | set prompt = "% " |
引用变量使用什么引号
1 | export PS1="${USER}$" |
1 | export PS1='Lucky Number ${RANDOM} $' |
你是否发现,上方两个例子使用的不同的引用符号(单引号和双引号)。根据之前所学,双引号允许将 $
解释为元字符,而单引号会“引用”该符号。
换句话说,双引号"
的弱引用意味着 shell 处理该命令的时候就将用变量的值替换字符串中的变量了,而单引号 '
强引用使 $
符号被保留下来,直到实际创建 shell 提示的时候才对变量 $
求值。
toulzx: 是不是可以这样的理解:shell 对字符串中元字符的处理,发生了两次:第一次是创建时,第二次是使用时。
使用转义字符的特殊码
只有 Bourne shell 家族的 bash 和 C-Shell 家族的 Tcsh 允许使用特殊码。
特殊码允许在 shell 提示中插入颜色、字体形式、工作目录名称等信息。
其中有些特殊码和环境变量的设置有相同效果(bash 为例):
1 | export PS1="\u $" |
toulzx:
如图,我的默认 shell 提示由<用户标识> + <计算机主机名> + <当前工作目录(基名)>
组成。
由于 Bash 中没有完整工作目录的特殊码,但没关系,我们可以使用环境变量代替:$PWD
使用反引号 ``` 进行命令替换
command substitution
允许在一条命令中嵌入另一条命令,并用输出替换该命令,最后 shell 再执行整个命令。(嵌套,由内而外执行)
显然,单引号会使反引号被“引用”。
注意:date 是命令不是变量。
历史列表
回顾以下知识点:
- 使用
<Bachspace>
删除键入的最后一个字符(光标处),发送 erase 信号。 - 使用
^W
(Ctrl
+W
)删除键入的最后一个单词,发送 werase 信号。 - 使用
^U
或^X
删除整行,发送 kill 信号。 - 使用 stty 命令显示系统上所有键映射。
- 使用左右方向键移动光标,上下方向键选择历史命令。(仅 Bash 支持)
输入命令时,你应该遵循此原则:重用,而不是重新键入。
查看历史列表
无论正确错误的历史命令都会被记录,且 shell 会给它编号。
Bourne shell 家族常用 fc
命令。
C-Shell 家族常用 history
命令或 !
命令。
Bash 支持 C-Shell 的历史列表命令。
1 | # 按页显示 |
执行上一条命令:
1 | fc -s |
toulzx: 书 P269 最后一行把 fc 写错成 fs 了
执行指定编号的命令:
1 | # 执行编号 208 的命令 |
修改指定编号的命令再执行:
1 | # 现已有编号为 208 的命令如下: |
因此当你不小心执行了内容错误的命令内容时:
1 | # 上一条不小心输错的命令是 `date`: |
缝合怪 Bash 占尽优势
Bash 支持两个家族所有的历史记录的命令,所以建议使用 history
/!
Bash 额外支持 ^R
命令,按下后,输入字符串,可以自动查找最近使用的、包含字符串内容的命令。
设置历史记录存储值
Bourne shell 家族修改环境变量 HISTSIZE:
1 | export HISTSIZE=50 |
C-Shell 家族修改 shell 变量 history:
1 | set history = 50 |
将历史列表的事件编号显示在 shell 提示中
1 | # 如:`[208]$` |
你可能注意到 Tcsh 的命令中多了一个反斜线,这是因为反斜线和右括号相邻会有问题,此问题难解,不用关心。
自动补全
涉及变量、文件名、路径名…
命令的别名
自定义简化的命令,代替常用的、复杂的命令。
现假设我们经常使用这条命令:ls -l temp*
,我们希望每次输入 lt
就可以调用这条命令。
查看别名:alias
使用 alias
命令可以查看所有的别名。
使用 type {commands}
命令可以查看某条命令是不是别名。
创建别名:alias {name}={commands}
1 | # Bourne shell 家族:alias {name}={commands} |
这里使用了强引用的单引号包裹命令,是因为命令中包含了多个空格和元字符(*),单引号保留了元字符的含义,直到别名执行。
移除别名:unalias {name}
1 | unalias lt |
别名和已有命令重复
使用转义字符(\)使用原有命令。
1 | alias ls="ls -l" |
别名示例:避免删错文件
当我们打算删除所有匹配特定模式的文件之前,我们一定要养成检查的习惯,因为该过程不可逆!
即,在执行 rm temp* extra?
之前,应该先执行 ls temp* extra?
。
当我们执行了 ls temp* extra?
并确认后,我希望通过别名 del
完成 rm temp* extra?
:
1 | alias del='fc -s ls=rm' |
GUIDE TO UNIX AND LINUX (8-13)
http://www.toulzx.top/2021/12/20/guide_to_unix_and_linux_8to13/