什么是 shell
为了避免非专业人士直接操作系统导致系统受破坏,因此需要有个 shell 来操作。每个命令都是一个应用,shell 是一个能调用各个应用接口的程序。
shell 的命令
可以用 type 命令来查看那些命令是 shell 内置
1 | type cd #cd是shell内置的 |
如果命令过长,可以用‘\’将回车转义(escape),这样就可以在下一行继续输入命令。
有时候需要删除和编辑写好的命令,可以通过下面的组合键操作。
| 组合键 | 功能 |
|---|---|
| ^u | 删除光标处到头部的内容 |
| ^k | 删除光标处到尾部的内容 |
| ^a | 到命令头部 |
| ^e | 到命令尾部 |
shell 的变量功能
获得变量
1 | #shell中获取变量需要在变量前加$ |
设置变量
1 | #用法 |
删除变量
1 | unset variable |
变量的设置规则
- 变量名由字母、数字、下划线组成,并且不能由数字开头
- 变量赋值等号左右两侧不能由空格,如“name = allenbai”或“name=allen bai”
- 如果变量的值内要包含空格需要用‘“’或‘’’来包裹,或‘\’转义。如“name=”hello world””,“name=hello\ world”。注意如果使用的是单引号,那么单引号的内容就会变成一般的字符,里面定义的变量就会失去意义。如“name=’${name} is you’”,这里的“${name}”会变成字符,而不是变量。
- 如果在赋值的过程中引用变量需要“”$变量名””或“${变量名}”。如“name=”$name”love”或“name=${name}love”
- 如果需要其他指令提供的信息,可以用反单引号“`指令`”或“$(指令)”。如“name=$(uname -r)”
- “export variable”可以将变量导到环境变量中。可以用“env”查看所有环境变量
- 系统的变量默认全大写,自己的变量可以用小写,以便区分。这点要不要遵守看个人偏好。
- “unset varaible”可以取消变量
范例
1 | #进入到目前的核心目录模块 |
环境变量
查看当前环境变量
1 | #查看当前环境变量 |
export 不加参数也可以查看当前环境的变量
1 | export |
如果需要查看包括自定义变量和环境变量在内的所有变量可以用 set
1 | #不带任何参数即可 |
一些需要了解的系统环境变量
- HOME 代表使用者的主文件夹
- SHELL 当前环境用的是哪个 shell
- HISTSIZE 能被系统记录的历史指令的数量
- MAIL 当前用户的邮件信箱文件
- PATH 可执行文件搜索的路径
- LANG 语系数据
- RANDOM 随机数变量。值介于 0~32767 之间。如果要取得 0~9 之间可以这样:
1 | declare -i number=$RANDOM*10/32768;echo $number |
bash 不只有环境变量,还有一些 bash 操作接口有关的变量,以及自定义的变量。用 set 可以观察所有变量。
除了环境变量外,其他 bash 内的变量
- PS1 提示字符的设置
- \$ shell 的 PID
- ? 关于上个指令的回传值,0 表示成功,如果上个指令执行报错,会回传错误代码
- OSTYPE,HOSTTYPE,MACHTYPE 主机硬件
环境变量与自订变量的差别是该变量是否会被子程序继续引用。
语系变量
1 | locale |
变量的有效范围
鸟哥书里提到的环境变量和自订变量也可以称为全局变量(global variable)和局部变量(local variable)。
read,array,declare
read 可以读取来自键盘输入的变量
1 | #用法 |
declare/typeset 设置变量类型
1 | #用法 |
定义数组
1 | person[1]='allen' |
变量的删除、取代和替换
从前面开始删除变量中的某些内容
1 | #用法 变量名+‘#’+匹配规则(pattern) |
1 | echo $PATH |
从后面开始删除变量中的某些内容
1 | #从后面开始向左删,且删最短的那个。用法与‘#’类似 |
替换内容
1 | #用法 pattern是指匹配规则 new是新内容 |
1 | #将匹配到的第一个sbin替换为大写的SBIN |
变量的测试与内容替换
‘-’
1 | # 如果old_var未定义 则new_var的值为content |
有点像 js 里的管道符号
1 | var new_var = old_var || content; |
1 | # 如果old_var未定义或为空值 则new_var的值为content |
‘+’
1 | # 如果old_var有定义(包括空值) 则new_var的值为content |
有点像 js 里的‘&&’
1 | var new_var = old_var && content; |
1 | # 如果old_var有定义且值不为空 则new_var的值为content |
‘=’
1 | # 这个效果和‘-’一样,但多了一个效果,就是content赋值给new_var的同时也会赋值给old_var |
1 | # 这个效果和‘:-’一样,但多了一个效果,就是content赋值给new_var的同时也会赋值给old_var |
‘?’
1 | # 如果old_var未定义 就会告知控制台warn的内容 |
1 | # 如果old_var未定义或空值 就会告知控制台warn的内容 |
其实不难发现‘-’,‘=’,‘?’都是假设 old_var 未定义才生效的,如果需要 old_var 为空值也生效加个‘:’就好。
而‘+’是假设 old_var 已定义才生效(无论有没有值),如果需要 old_var 为空值不生效加个‘:’就好。
命令别名与历史命名
命令别名
1 | # 查看所有命令别名 |
bash 的环境配置文件
重要的环境配置文件
| 配置文件 | 功能 |
|---|---|
| /etc/issue | bash 的开机画面 |
| /etc/motd | bash 登录后的提示信息 |
| /etc/locale.conf | 决定默认使用语系,由/etc/profile.d/lang.sh 调用进来 |
| /etc/profile | 登录 bash 时读取的文件,是系统整体的设置 |
| /etc/profile.d/*.sh | /etc/profile 运行时回去执行/etc/profile.d 下所有.sh 结尾的文件 |
| /usr/share/bash-completion/completions/* | Tab 的补全设置,由/etc/profile.d/bash_completion.sh 调用进来 |
| ~/.bash_profile 或 ~/.bash_login 或 ~/.profile | 属于使用者个人的设置。其实只会读取其中的一个。最先读取~/.bash_profile,如果没有再读取下一个,以此类推 |
| ~/.bashrc | 获取 no-login shell 时会读取该文件。还有~/.bash_profile 会读该文件 |
| ~/.bash_history | 历史命令记录的地方 |
| ~/.bash_logout | 登出的时候执行的文件 |
读入环境配置文件的指令
1 | #用法 |
万用字符和特殊符号
万用字符
万用字符有点像正则匹配有点类似但有差别
| 符号 | 意义 |
|---|---|
| * | 代表 0 到无穷个任意字符,相当于正则的.* |
| ? | 随机的一个字符 |
| [] | 表示括号内的任意一个字符 |
| [-] | 字符编码范围,例如[0-9]表示 0 到 9 之间的所有字符 |
| [^] | 反向选择,例如[^abc]表示非 a,b,c 的字符 |
特殊符号
| 符号 | 意义 | |
|---|---|---|
| # | 注释符号 | |
| \ | 转义 | |
| \ | 管线(pipe),分隔两个管线命令的界定 | |
| ; | 连续指令下达分隔符号 | |
| ~ | 使用者的主文件夹 | |
| \$ | 取用变量前置字符:亦即是变量之前需要加的变量取代值 | |
| & | 工作控制 (job control):将指令变成背景下工作 | |
| ! | 逻辑运算符,not 的意思 | |
| / | 目录符号:路径分隔的符号 | |
| >和>> | 数据流重导向:输出导向,分别是“取代”与“累加” | |
| \<和\<\< | 数据流重导向:输入导向 | |
| ‘ ‘ | 单引号,不具有变量置换的功能 (\$ 变为纯文本) | |
| “ “ | 具有变量置换的功能! (\$ 可保留相关功能) | |
| ` | 两个“`”中间为可以先执行的指令,亦可使用 \$( ) | |
| () | 在中间为子 shell 的起始与结束 | |
| { } | 在中间为命令区块的组合! |
数据流重导向
数据流重导向就是将某个指令执行后应该要出现在屏幕上的数据, 给他传输到
其他的地方,例如文件或者是设备。
| 符号 | 意义 |
|---|---|
| \< | 标准输入,将某个文件代替键盘输入 |
| \<\< | 标准输入,以某个符号作为文件的结束标记 |
| >或 1> | 标准输出,覆盖原内容 |
| >>或 1>> | 标准输出,添加的原内容尾部 |
| 2> | 标准错误输出,覆盖原内容 |
| 2>> | 标准错误输出,添加的原内容的尾部 |
以下是例子
1 | #例 将ll列出的内容保存到~/rootfile内 |
;和&&和||
‘;’这个很好理解,相当于每个语句的结束符。有了它,就可以同时写个条指令了。
1 | #例 关机前执行两次sync同步写入磁盘 |
&&前后连接两个指令,前一个指令报错就不执行后一个指令;||前后连接两个指令,前一个不报错就不执行后一个指令。
1 | #非root用户执行find /root,$?返回错误代码,所这里的ls不执行 |
1 | #我们要在tmp下面创建/abc/hehe,但我们不知道abc是否存在。于是我们可以用下面的方法。 |
解析:
情况一,tmp 下没 abc 文件夹。
由于没有 abc 文件夹,那第一个指令回传值(\$?)不为 0,也就是说第一个指令报错了,于是会执行第二指令新建 abc 目录。第二个指令执行成功,于是会执行第三个指令创建 hehe 文件。
情况二,tmp 下有 abc 文件夹。
第一个指令成功,所以第二个指令不执行。注意了,这行语句并没有就此结束。第一个回传值会继续传递,由于第一个回传值是 0,所以第三个指令会执行。
结论:无论有没有 abc 目录,我们都能创建/tmp/abc/hehe 文件
管线符号(pipe)
‘|’就是管线符号了。如果数据流重导向是将指令的结果导到设备和文件或设备和文件的数据导到指令的话;那么 pipe 就是将前一个命令的数据输出做为后一个命令的输入。
使用管线符号需要注意两点:
- 管线符号后面的那个命令必须是管线命令。能接受前一个指令的数据成为 standard input 才是管线命令。
- 管线命令仅会处理 standard output,对于 standard error output 会被忽略。
管线符号的组合运用
- cut
cut 可以将数据按我们的要求切出来,可以想象成 split。该命令是按行来操作的。
1 | #用法一;按分隔字符将一行数据切成多段,并攫取其中的几个片段 |
1 | #用法二; |
- grep
查询每一行,把含有我们想要的文字的句子列出来。grep 能配合正则使用。
1 | grep [-acinv] [--color=auto] '搜寻字符' filename |
- sort
sort 很明显是用来排序的。排序的字符和语系有关,因此, 如果您需要排
序时,建议使用 LANG=C 来让语系统一,数据排序比较好一些。
1 | #用法 |
- uniq
将排序完的数据去重。(只有连续的重复内容才会被去除,所以使用前需要排序)
1 | #用法 |
- wc
查看文件多少行,多少个字符
1 | #用法 |
- tee
我们可以使用数据流重导向‘>’将 stdout 输入到文件。如果我们在输入到文件的同时,还要继续使用这个 stdout 的话,我们可使用 tee(双向重导向)。
1 | #用法 |
- unix2dos dos2unix
unix2dos 是将文本的 unix 换行符号换成 dos 的。
- tr
删除或替换文本
1 | #用法 |
- col
将 tab 转成对等的空白键
1 | #用法 |
- join
将含有相同数据的行拼接在一起。使用时最好排序下。
1 | #用法 |
- paste
paste 比 join 简单粗暴,它直接将两个文件的行相连,并用 tab 隔开
1 | #用法 |
- expand
将 tab 转成空白键
1 | #用法 |
- split
他可以帮你将一个大文件,依据文件大小或行数来分区,就可以将大文件分区成为小文件了!
1 | #用法 |
- xargs
xargs 可以读入 stdin 的数据,并且以空白字符或断行字符作为分辨,将 stdin 的数据分隔成为 arguments
1 | xargs [-0epn] command |
- -
在管线命令的使用中‘-’是非常重要的,它指代前一个命令的标准输出
1 | mkdir /tmp/homeback |