1 Shell 的概念
管理整个计算机硬件的其实是操作系统的内核(Kernel),这个内核是需要被保护的,所以我们一般的用户就只能通过 Shell 来跟内核沟通,已让内核完成所想要实现的任务。
什么是 Shell?
举例子说,当你要计算机播放出来音乐的时候,计算机简单来说需要三种东西:
- 硬件
- 内核管理:真正的控制硬件工作的东西,含有 CPU 调度、内存管理、磁盘输入输出等工作
- 应用程序:接受来自使用者的命令,用以与内核进行沟通
而能够操作应用程序的软件都能被称为壳程序,因为它就像鸡蛋的外壳一样,位于最外层,因此这东西也就被称为壳程序(Shell)。狭义上的壳程序指的就是命令行方面的软件,而广义上则包括图形用户界面模式的软件,因为图形用户界面模式其实也能够操作各种应用程序来调用内核工作。
Shell 的版本有很多,如 Bourne shell,C shell,K Shell等,而 Bash 就是 Bourne Shell 的增强版本。
查看 /bin/shells
文件可以查询到当前的 Linux 使用了哪几个 Shell,如果想要查看某个用户是使用哪个 Shell 来工作的,就可到 /etc/passwd
文件中查看得到。
2 Bash
Bash 是 Linux 默认的 Shell,它的主要优点有:
- 历史命令
- 命令与文件补全功能:
[Tab]
按键的功能其实就是只有 Bash 中才用 - 命令别名设置功能:即可以使用别名简化命令的书写,可以直接执行如
alias lm='ls -al'
命令来设置lm
作为ls -al
命令的别名 - 任务管理、前台、后台控制
- 程序化脚本
- 通配符
而为了方便操作,Bash 已经内置了很多命令,如果想要查看某个命令是否内置在 Bash 中,可以使用 type
命令,它的语法如下:
type [-tpa] name
name
为命令名,主要参数说明如下:
-t
:显示以下字眼显示出它的意义file
:表示为外部命令alias
:表示该名为命令别名builtin
:表示该命令为 Bash 的内置命令
- 不加入任何参数时,
type
会显示该命令是外部命令还是内置命令
2.1 通配符与特殊符号
2.1.1 通配符
这些通配符实质跟正则表达式差不多
*
:代表 0 个到无穷多个的任意字符?
:代表一定有一个任意字符[]
:表示一定有一个在括号内的字符,如[abcd]
表示一定有一个字符,可能是 a,b,c,d 这四个任何一个[-]
:如[0-9]
表示 0 和 9 之间的所有数字[^]
:表示非,如[^abc]
表示一定不存在 a、b、c 这三个字符
3 Bash 的变量
3.1 变量的设置
变量的设置很简单,就是使用 =
连接变量名称与变量数值即可。
但要注意的是变量的设置还需要符合某些规定:
- 等号两边不能直接接空格,需要转义或使用双引号、单引号
- 变量名称只能是英文字母与数字,但开头字符不能是数字
- 变量内容如果有空格可使用双引号或单引号括起来,二者区别是:
- 双引号内的特殊字符如
$
,可以保存原本的特性 - 单引号内的特殊字符则为一般文本
- 双引号内的特殊字符如
- 可使用转义符
\
将特殊符号变为一般文本 - 可以使用反单引号 或
$(命令)
的形式作为执行命令的作用,如:version=$(uname -r)
,在一串命令中,使用了反单引号 或$(命令)
的形式命令将会优先执行 - 变量需要在其它子程序执行,需要使用
export
命令 - 取消变量的方法是使用
unset
命令,如unset myname
- 默认的,名称为大写字符的是系统默认变量,小写字符的是自行设置变量
使用 echo
命令可以输出变量的值,只需在变量名称前面加上 $
,或是 ${变量名}
的方式输出即可,如下:
echo $PATH
echo ${PATH}
在 Bash 中,当一个变量尚未被设置时,默认的内容是 空
3.1.1 数组变量的声明
在默认的情况下,变量类型默认为字符串,所以如果不指定变量的类型,形如 1+2
的表达式就是一个字符串而不是计算式。
使用 declare
命令可以声明变量类型,语法如下:
declare [-aixrp] variable
选项与参数:
-a
:将variable
变量定义为数组类型,-i
:将variable
变量定义为整数类型,如declare -i numOfTest=1+2
-x
:用法与export
类似,就是将variable
变量定义为 全局变量+x
:取消全局变量-r
:将variable
变量设置为readonly
类型,该变量不能被更改,unset
,只能通过注销再登陆才能恢复该变量的类型-p
:输出该变量的类型
3.1.2 交互模式下的读取键盘输入变量
在 shell 脚本的编写中,有时候需要与用户进行交互,此时就需要使用到 read
命令,它的语法如下:
read [-pt] variable
-p
:后面可以接指示文本-t
:后面可以设置等待秒数
3.2 字符串变量的删除、取代与替换
3.2.1 删除
#
符号代表的是字符串由前面开始删除,紧接的是匹配字符,如下表示删除路径的第一个目录:
echo ${PATH#/*:}
单一个 #
表示的不是贪婪匹配,想要贪婪匹配此时就需要使用到 ##
符号,此时上述的过程就变成删除路径除最后一个目录以外的目录。
如果需要从最后开始匹配删除,就需要使用到 %
符号,%%
符号也与上述的 ##
同理。
3.2.2 替换
/旧字符/新字符
代表的是字符串由前面开始替换第一个匹配字符,如下就表示为将开头匹配到的第一个 bin
替换为 Bin
:
echo ${path/bin/Bin}
//旧字符/新字符
代表的是全部替换。
3.2.3 变量设定的默认值
有些时候,需要判断某个变量是否存在,如果不存在就赋予一个默认的数值,此时可以使用 -
符号,如下,此时如果 myname
变量未设置,则赋值为 root
:
echo ${myname-root}
但 -
符号仅对未设置的数值有效,对于设置为空字符的变量想达到同样的效果就需要使用 :
符号
3.3 全局变量
全局变量与局部变量的区别在于:该变量能耐否被子进程所继续使用,所谓子进程指的就是在原本运行中的 bash 中触发了另一个 bash,此时原本的 bash 就是父进程,而触发的那个 bash 就是子进程。
一般的,不管是否为全局变量,只要跟我们目前这个 shell 的操作有关的变量,通常都会被设置为大写字符,即 Linux 默认情况下,使用
{大写字母}
来设置的变量一般为 系统内定需要的变量
3.2.1 env
命令行直接输入 env
命令可以列出目前的 shell 环境下的所有全局变量与其内容,如:
HOME
:代表用户的根目录SHELL
:目前这个环境使用的 SHELL 是哪个,默认 BashHISTSIZE
:记录命令的条数MAIL
:邮箱PATH
:执行文件的查找路径LANG
:当你没设置过语系,此时默认的,就会被该变量所取代RANDOM
:随机数变量,介于0 ~ 32767
export
命令可以将自定义变量转成环境变量
3.2.2 set
命令行直接输入 set
命令可以列出目前的所有的变量,此时输出可能比较多,可以添加 more
后缀以便查看,如 set | more
(类似的还有后缀 grep
用以正则表达式查看关键字),下面列举一些比较重要的 系统内定需要的变量
PS1
:这个是命令行前缀那个提示字符的设置$
:关于本 Shell 的 PID,即表示当前的进程号?
:返回上一个执行命令的返回值OSTYPE
、HOSTTYPE
、MACHTYPE
:主机硬件与内核的等级
4 别名
4.1 命令别名设置
当常用的命令特别长的时候,便可以使用 alias
命令设置命令别名,命令别名的设置还可以替换既有的命令,比如:
alias cls='clear'
alias rm='rm -i'
这样原有的 rm
就会被 rm -i
所替代,而取消命令别名就是使用 unalias
命令,如:
unalias rm
5 bash 的环境配置文件
前面谈到的命令别名、自定义的变量,在注销 bash 后就会失效
当用户一进入 bash 就可以通过一些环境配置文件获取一堆预设定的变量,而这些配置文件可以分为全局系统配置文件以及用户个人偏好配置文件,所以想要保存好设置的命令别名和自定义的变量,就需要将设置写入这些配置文件中
在介绍配置文件的时候就需要先了解 login shell
以及 non-login shell
,它们的重点在于有没有登陆:
login shell
:取得 bash 时需要完整的登录流程,此时就称为login shell
non-login shell
:取得 bash 的方法不需要重复登录的操作,比如使用 X Window 登录 Linux 后,再以 X 的图形化接口启动终端,或者初次启动 bash 的时候在命令行中再次执行 bash,此时也无需再次登录,这些就可以称为non-login shell
这两种登录方式不同的 bash 读取的配置文件都是不同的,一般来讲 login shell
这个方式登录的 bash 就只会读取下面这两个配置文件:
/etc/profile
~/.bash_profile
或~/.bash_login
或~/.profile
:
5.1 系统整体的配置文件 /etc/profile
这是 系统整体的配置文件,这个配置文件可以利用用户标识符(UID)来决定很多重要的变量数据,这也是每个用户登录取得 bash 时一定会读取得的配置文件,该文件设置的变量主要有:
PATH
:根据 UID 决定PATH
变量要不要含有 sbin 的系统命令目录MAIL
:根据账号设置用户的 mailboxUSER
:根据用户的账号设置此变量的内容HOSTNAME
:根据主机的 hostname 命令决定此变量内容HISTSIZE
:历史命令记录条数,CentOS 7.x 设置为 1000 条umask
:默认为022
同时, /etc/profile
配置文件还会调用外部的配置文件,在 CentOS 7.x 默认情况下,下面这些文件会别调用:
/etc/profile.d/*.sh
:在/etc/profile.d/
目录下的所有sh
为扩展名的文件,这个目录下的文件通常规范了 bash 操作界面的颜色、语系、ll
与ls
命令的命令别名、vim
的 命令别名、which
的命令别名等。/etc/locale.conf
:这个文件由/etc/profile.d/lang.sh
所调用的,这是决定 bash 默认使用何种语系的重要配置文件,其中最重要的当属LANG/LC_ALL
变量/usr/share/bash-completion/completions/*
:这个配置文件由/etc/profile.d/bash_completion.sh
文件所加载,它用于[Tab]
键补全的
5.2 用户个人配置文件 ~/.bash_profile
在读取个人配置文件的时候,其实会按顺序地读取以下三个配置文件,一旦读取了某个配置文件,就不会继续往下读取:
~/.bash_profile
:默认的会读入~/.bashrc
文件~/.bash_login
~/.profile
5.3 ~/.bashrc
除了上述的 ~/.bash_profile
会读取 ~/.bashrc
文件外,non-login shell
这种非登录情况也自动且仅仅读取该配置文件。该文件会帮 bash 定义下面的内容:
- 根据不同的 UID 设置 umask 的值
- 根据不同的 UID 设置提示字符(即
PS1
变量) - 调用
/etc/profile.d/*.sh
的设置
5.4 source
命令
在修改了配置文件后,通常需要注销后登录才能生效,此时使用 source
命令就可以立即刷新配置,如:
source ~/.bashrc
6 终端的环境设置
比如输入 backspace
可以删除一个输入字符,这些按键内容都是属于 Linux 发行版设置好的用户环境,使用 stty
命令可以获取所有输入按键代表的意义,其中 ^
表示 [Ctrl]
这个按键,而几个重要的关键词是:
intr
:发送一个interrupt
(中断)的信号给目前的正在 run 的程序quit
:发送一个quit
的信号给目前正在 run 的程序erase
:删除字符kill
:删除在目前命令行上的所有文字eof
:结束输入stop
:停止目前屏幕的输出start
:在某个程序停止后,重新启动它的output
susp
:发送一个terminal stop
的信号给正在运行的程序
7 数据流重定向
数据流重定向(redirect)如同字面意思,比如将某个命令执行后应该要出现在屏幕上的数据,给他传输到其它的地方,例如文件或设备(打印机)
当执行一个命令,命令的执行流程大致如下图:
由上图可以看出,执行一个命令,这个命令可能会由文件读入数据,处理过后输出到相应的地方如屏幕上。图中的 standard output
与 standard error output
分别代表 标准输出(STDOUT) 与 标准错误输出(STDERR)。
- 标准输入(STDIN):代码为 0,使用
<
或<<
- 标准输出(STDOUT):代码为 1,使用
>
或>>
- 标准错误输出(STDERR):代码为 2,使用
2>
或2>>
7.1 标准输出以及标准错误输出
当输入以下命令时
ll / > ~/rootfile
此时屏幕不会有数据显示,因为显示的数据已经重定向输出到 ~/rootfile
文件中,如果该文件不存在,系统会自动将它建立起来,如果该文件存在,其内容就会被输出的的数据所覆盖。
此时想要不覆盖原有的内容就需要使用到 >>
这个符号了。
而 2>
与 2>>
同理,只不过它们用于输出错误的信息。
- 将 stdout 和 stderr 分别发布到不同的文件:
find /home -name .bashrc > list_right 2> list_error
- 将 stdout 和 stderr 同时写入到同一文件中,由于两股数据同时写入一个文件,有没有使用特殊的语法,此时两股数据可能会交叉写入该文件内,造成次序的错乱,所以需要使用特殊的语法
2>&1
或&>
,如:
find -name .bashrc > list 2>&1
find -name .bashrc &> list
2>&1
可以理解为错误信息转为正确信息输出,那么同理1>&2
也可以理解有正确信息转为错误信息输出
7.2 标准输入
标准输入可以理解为:将原本需要有键盘输入的数据,改为由文件内容进来替换。如:
cat > catfile < ~/.bashrc
7.3 /dev/null
垃圾桶黑洞
想要将错误信息忽略掉而不显示或存储,此时就可以将输出重定向到 /dev/null
这个黑洞设备中,这个 /dev/null
可以吃掉所有导向这个设备的信息,如:
find /home -name .bashrc 2> /dev/null
8 管道命令
如果输出数据需要经过几道处理之后才能得到所想要的格式,比如使用 ls /etc
查看 /etc
目录下有多少文件,因为 /etc
文件太多,可能会导致一口气就将屏幕塞满, 此时就可以使用管道命令,如:
ls -al /etc | less
管道命令使用的是 |
这个界定符号,管道命令 |
仅能处理经由前面一个命令传来的正确信息,也就是标准输出的正确信息,对于标准错误信息并没有直接处理的能力,同时它必须要能接受来自前一个命令的数据成为标准输入继续处理才行。
8.1 选取命令 cut
、grep
8.1.1 cut
cut
命令的作用是分隔,语法如下:
cut -d '分隔字符' -f fields
或
cut -c 字符区间
其选项与参数:
-d
:后面接分隔字符,与-f
一起使用-f
:根据分隔字符将一段信息划分为数段,取第几段输出-c
:以字符的单位取出固定索引间的字符
如:
echo ${PATH} | cut -d ':' -f 3,5
echo ${PATH} | cut -c 12-20
8.1.2 grep
grep
命令是分析一行信息,若当中有所匹配到的信息,就将器过滤出来,其语法如下:
grep [-acinv] [--color=auto] '查找字符' filename
其选项与参数:
-a
:将二进制文件以文本文件的方式查找数据-c
:计算找到匹配字符的次数-i
:忽略大小写-n
:输出行号-v
:反向选择,即输出没有匹配字符的内容--color=auto
:输出时,将匹配字符加上颜色显示
8.2 排序命令
8.2.1 sort
sort
命令顾名思义可以进行排序,其语法如下:
sort [-fbMnrtuk] [file or stdin]
其选项与参数:
-f
:忽略大小写的差异-b
:忽略最前面的空格字符部分-M
:以月份的名字来排序(JAN,DES)-n
:使用纯文字排序-r
:反向排序-u
:uniq,相同的数据只显示一行-t
:分隔符号,默认是[Tab]
键-k
:以哪个区间进行排序
8.2.2 uniq
将重复的数据仅列出一个显示,语法:
uniq [-ic]
其选项与参数:
-i
:忽略大小写-c
:进行计数
8.2.3 wc
wc
命令用户统计输出信息整体数据有多少字,多少行,多少字符,通过它,可以查找出账号文件里有多少个账号(因为账号文件有每一行表示一个账号),其语法:
wc [-lwm]
其选项与参数:
-l
:列出有多少行-w
:列出有多少个字(英文字母)-m
:列出有多少个字符
8.3 双向重定向
8.3.1 tee
tee
会同时将 数据流 分送到文件与屏幕,其语法:
tee [-a] file
其选项与参数:
-a
:以累加的方式,将数据加入 file 当中
8.4 划分命令
8.4.1 split
如果有文件太大,导致携带不方便,可以使用 split
命令将大文件依据文件大小或行数来划分,就可以将大文件划分为小文件,其语法:
split [-bl] file PREFIX
其选项与参数:
-b
:划分的文件大小-l
:以行数来进行划分PREFIX
:代表划分文件的名称前缀字符
下例,使用 ls -al /
命令输出信息中,每十行记录一个文件:
ls -al / | split -l 10 - lsroot
此时重点在于
lsroot
前的-
号,一般来说需要stdout
或stdin
时,却有没有文件,那么这个-
就会被当成stdin
或stdout
。在管道命令中,常常会使用到前一个命令的stdout
作为这次的stdin
,此时stdin
与stdout
都可以利用-
号来替换