# Bash Shell 的操作环境
在我们登陆主机的时候,屏幕上会有一些说明文字,告知我们的 Linux 版本之类的信息,还可以显示一些欢迎等信息。此外,我们习惯的环境变量、命令别名等,是否可以在登录后就主动帮我设置好?
这些设置又分为系统全局配置和个人账户级配置,仅是文件放置位置不同
# 路径与指令搜寻顺序
前面讲到过使用 alias 可以建立别名,比如创建了一个 ls 的别名,其实 ls 有少的指令,那么到底是哪一个会被选中执行呢?基本上,指令运行顺序可以这样看:
- 以相对、绝对路径执行命令,例如
/bin/ls
或./ls
- 由 alias 找到该指令来执行
- 由 bash 内置的指令来执行
- 通过 $PATH 这个变量的顺序搜索到第一个指令执行
举例来说:
/bin/ls
:该指令运行后,没有颜色ls
:该指令运行后输出的内容有颜色,因为是使用别名alias ls=‘ls --color=auto’
也可以使用 type -a ls 来查询指令搜寻的顺序
# 范例:设置 echo 的命令别名为 echo -n,然后观察 echo 执行的顺序
[mrcode@study ~]$ alias echo='echo -n'
[mrcode@study ~]$ type -a echo
echo is aliased to `echo -n'
echo is a shell builtin
echo is /usr/bin/echo
2
3
4
5
6
可以看到上面的顺序与本节总结的执行顺序一致
# bash 的进站与欢迎信息:/etc/issue、/etc/motd
# 进站信息 /etc/issue
在 tty1~tty6 登录时,会有几行提示字符,这个就是进站画面,该字符串在 /etc/issue 中配置的
[mrcode@study ~]$ cat /etc/issue
\S
Kernel \r on an \m
2
3
4
如上的变量引用使用的是反斜杠,变量可以通过 man issue 中查看到 agetty ,再 man agetty 得到如下的信息,代码变量信息如下
\d
:本地端时间的日期\l
:显示第几个终端机接口\m
:显示硬件的等级(i386、i486、i586...)\n
:显示主机的网络名称\O
:显示 domain name\r
:操作系统的版本(相当于 uname -r)\t
:显示本地端时间的时间\S
:操作系统的名称\v
:操作系统的版本
# 练习:如果想在 tty3 的进站画面看到如下显示,该如何设置才能达到效果?
CentOS Linux 7 (Core)(terminal:tty3)
Date:2019-12-01 18:00:00
Kernel 3.10.0-229.e17.x86_64 on an x86_64
Welcome!
使用 root 身份参考上面的变量说明修改 /etc/issue 文件达成效果
vim /etc/issue
\S (terminal: \l)
Date: \d \t
Kernel \r on an \m
Welcome!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
怎么登录 tty 和切换 tty 请参考之前的章节,记得,进站画面是切换到 tty 时顶部显示的信息,而不是登录后显示的信息。
该文件中的规则就是使用反斜杠引用上面的变量,其他的你可以随意操作,比如写个字符画等,搞得个性一点
当使用 telnet 登录主机时,是不会显示 /etc/issue
中的配置,而是显示 /etc/issue.net
中的配置
# 欢迎信息 /etc/motd
想要使用者登录后,取得一些信息,例如使用注意事项信息,就可以修改 /etc/motd 文件
[root@study ~]# vi /etc/motd
Hello everyone,
这是欢迎信息中文测试
# 重新登录后会看到如下的信息
Last login: Sun Dec 1 17:37:58 2019 from 192.168.0.105
Hello everyone,
这是欢迎信息中文测试
[mrcode@study ~]$
2
3
4
5
6
7
8
9
10
# bash 的环境配置文件
我们一进入 bash 就取得了一堆有用的变量,这是因为系统有一些环境配置文件的存在,让 bash 在启动时直接读取这些配置文件,以规划好 bash 的操作环境。而这些配置文件分为全局系统配置和用户个人偏好配置
# login 与 non-login shell
在介绍 bash 的配置文件前,一定要先知道 login shell 与 non-login shell ,重点就在于有没有登录(login)
login shell:取得 bash 时需要完整的登录流程,就称为 login shell
举例来说,你要由 tty1~tty6 登录,需要输入用户的账户与密码,此时取得的 bash 就称为「login shell」
non-login shell:取得 bash 接口的方法不需要重复登录的举动
比如:你以 x window 登录 linux 后,再以 X 的图形化接口启动终端机,此时该终端机并没有再次输入账户与密码,那么该 bash 的环境就称为 non-login shell
再比如:你再原本的 bash 环境下再次下达 bash 这个指令,同样也没有输入账户密码,那第二个 bash(子程序)也是 non-login shell
上面两种情况取得的 bash 配置文件不一致。由于我们需要登录系统,所以先谈谈 login shell 会读取哪些配置文件?一般来说,login shell 其实只会读取这两个配置文件
- /etc/profile:系统整体配置,最好不要修改这个文件
~/.bash_profile
或~/.bash_login
或~/.profile
:属于使用者个人设置
# /etc/profile (login shell 才会读)
该文件相对于现在我们来看,可能还不太能看得懂,里面是利用使用者的标识符(UID)来决定很多重要的变量数据,这也是 每个使用者登录取得 bash 时一定会读取的配置文件 ,也就是系统级全局配置,主要变量如下:
- PATH:会依据 UID 决定 PATH 变量要不要含有 sbin 的系统指令目录
- MAIL:依据账户设置好使用者的 mailbox 到 /var/spool/mail/账号名
- USER:根据用户的账户设置该变量类容
- HOSTANME:依据主机的 hostname 指令决定此变量内容
- HISTSIZE:历史命令记录数量。CentOS 7.x 设置为 1000
- umask:包括 root 默认为 022 而一般用户为 002 等
/etc/profile 可不止会做这些事情,还会呼叫外部的设置数据,在 CentOS 7.x 默认情况下,下面的数据会依序被呼叫进来:
# /etc/profile.d/*.sh
通配符方式,加载该目录内所有的 sh 文件,另外,使用者需要具有 r 的权限,那么该文件就会被 /etc/profile 调用。
在 CentOS 7.x 中,该目录下的文件规范了 bash 操作窗口的颜色、语系、ll 与 ls 指令的命令别名、vi 的命令别名、which 的命令别名等。如果你需要帮所有使用者设置一些共享的命令别名时,可以在该目录下自行建立后缀为 .sh 的文件,并将所需要的数据加入即可
# /etc/local.conf
该文件是由 /etc/profile.d/lang.sh
呼叫进来的,这也是我们决定 bash 预设使用何种语系的重要配置文件!文件里最重要的就是 LANG、LC_ALL 这些变量的设置,前面讨论过
# /usr/share/bash-completion/completions/*
tab 键补全,除了命令补齐、文档名补齐外,还可以进行指令的选项、参数补齐功能。就是从这个目录里面找到对应的指令来处理的。
该目录下的内容是由 /etc/profile.d/bash_completion.sh 文件载入的
# ~/.bash_profile
(login shell 才会读)
bash 在读完了整体环境设置的 /etc/profile ,并借此加载其他配置文件后,接下来则是会读取使用者的个人配置文件。在 login shell 的 bash 环境中,所读取的个人偏好配置文件其实主要有 3 个,依序分别是:
~/.bash_profile
~/bash_login
~/,profile
其实 bash 的 login shell 设置只会读取上面三个文件中的一个,而读取的顺序则是依照上面的顺序。
什么意思呢?是当第一个文件不存在时,读取第二个,那么当第一个文件存在时,后面的都不读取了
为什么会有这么多的文件?是因为其他 shell 转换过来的使用者习惯不同,而做的兼容。
# 看看 mrcode 的 .bash_profile 的内容
# 具体路径为 /home/mrcode/.bash_profile
[mrcode@study ~]$ cat ~/.bash_profile
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then # 判断并读取 ~/.bashrc
. ~/.bashrc
fi
# User specific environment and startup programs
# 下面再处理个人化设置
PATH=$PATH:$HOME/.local/bin:$HOME/bin
export PATH
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
该文件设置了 PATH,并使用 export 将 PATh 变成环境变量,看配置是通过累加方式将用户家目录下的 ~/bin/ 目录添加进 PATH 了,这就意味着,你可以将可执行文件放到 ~/bin/ 下,执行时,就不需要写全路径了
上面的文件内容中有一段 if...then... 代码,该代码后续再 shell sript 中讲解,这里判断 ~/.bashrc 文件是否存在,存在则加载。
bash 配置文件的读入方式是通过 source 指令来读取的。这个后续讲解,最后来看看整个 login shell 的读取流程
实线的方向是主线流程,虚线的方向则是被加载的配置文件。从上图来看,CentOS 的 login shell 环境下,最终被读取的配置文件是 ~/.bashrc
文件,所以可以将自己的偏好设置写入该文件即可。
下面还要讨论 source 与 ~/.bashrc
# source : 读取环境配置文件的指令
由于 /etc/profile
与 ~/.bash_profile
都是在取得 login shell 的时候才会读取的配置文件,所以将自己的偏好设置写入上述文件后,通常都是需要注销后再登录,才会生效。可以使用 source 指令达到立即生效
source 配置文件名
# 范例:将 家目录的 ~/.bashrc 的设置读入目前的 bash 环境中
[mrcode@study ~]$ source ~/.bashrc
[mrcode@study ~]$ . ~/.bashrc
# 使用 source 或则 小数点的语法 都能将内容读取到当前的 shell 环境中
2
3
4
source 还可以用于不同环境配置文件的场景中,比如,我的工作环境分为 3 个,那么需要分别编写属于 3 个项目的环境变量配置文件,当需要该环境时,直接使用 source 加载进来
# ~/.bashrc
(non-login shell 会读)
在非登录情况下取得 bash 环境配置文件时,仅会读取 ~/.bashrc
文件
[mrcode@study ~]$ cat ~/.bashrc
# .bashrc
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# Uncomment the following line if you don't like systemctl's auto-paging feature:
# export SYSTEMD_PAGER=
# User specific aliases and functions
2
3
4
5
6
7
8
9
10
11
12
注意看,不同身份账户不同,这也解释了个人偏好配置文件是什么
[root@study ~]# cat ~/.bashrc
# .bashrc
# User specific aliases and functions
# 使用者的个人设置
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
# Source global definitions
# 整体环境的设置
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
2
3
4
5
6
7
8
9
10
11
12
13
14
CentOS 7.x 中为什么会主动加载 /etc/bashrc 文件呢?是因为 /etc/bashrc 帮我们的 bash 定义出下面的数据:
- 依据不同的 UID 规范出 umask 的值
- 依据不同的 UID 规范出提示字符(就是 PS1 变量)
- 加载 /etc/profile.d/*.sh 的设置
需要注意的是,/etc/bashrc 是 CentOS 特有的(Red Hat 系统),其他的 distribution 可能不是该名称。由于 ~/.bashrc
会加载 /etc/bashrc
和 /etc/profile.d/*.sh
所以,当你不小心删除了 ~/.bashrc
那么这些都不能读取了,你的 bash 提示字符可能就变成下面这个样子了
-bash-4.2$
原因是,没有加载 /etc/bashrc 来规范 PS1 d的变量,这种情况也不会影响你的 bash 使用。可以复制 /etc/skel/.bashrc 文件复制到 ~/.bashrc ,恢复回来
# 其他相关配置文件
事实上还有一些配置文件可能会影响到你的 bash 操作
# /etc/man_db.conf
该文件对于系统管理员来说,是一个很重要的文件,它规范了使用 man 时, man page 的路径到哪里去寻找。
如果你是以 tarball 的方式来安装你的数据库,那么你的 man page 可能会放置在 /usr/local/softpackage/man 中,softpackage 是套件的名称,这个时候就需要手动将该路径加到 /etc/man_db.conf 中。否则 man 就会找不到相关的说明文档
# ~/bash_history
在讲解「历史命令」时提到过该文件,预设情况下,历史命令就记录在该文件中。每次登陆 bash 后,bash 会先读取这个文件,将所有的历史指令读入内存,因此,当我们登陆 bash 后就可以查知上次使用过哪些指令
# ~/.bash_logout
该文件则记录了:当我注销 bash 后,系统再帮我做完师门动作后才离开的意思。你可以读取下该文件的内容,预设情况下,注销时,bash 只是帮我们清掉屏幕的信息而已。
不过,你也可以将一些备份或则是其他你认为重要的工作写在这个文件中(如:清空暂存盘)
# 终端机的环境设置:stty、set
前面讲解过可以在 tty1~tty6 这 6 个文字终端机(terminal)环境中登录,登录的时候可以取得一些字符设置的功能。比如
- 使用退格键(删除键)来删除命令行上的字符
- ctrl + c 来强制终止一个指令的执行
- 当时呼入错误时,会有声音跑出来警告
以上功能都是在登录终端机时,自动获取终端机的输入环境设置实现的
事实上,目前我们使用的 Linux distributions 都帮我们制作了最棒的使用者环境了,但是在某些 Unix like 机器中,还是可能需要手动修改配置
# setting tty
stty [-a]
参数 a:将目前所有的 stty 参数列出来
2
3
# 范例 1 :列出所有的按键与按键内容
[root@study ~]# stty -a
speed 38400 baud; rows 19; columns 126; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S;
susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke
# 以上特殊字符 ^ 表示 Ctrl,^C 表示 ctrl + c
2
3
4
5
6
7
8
9
10
下面是几个重要的含义:
- intr:送出一个 interrupt 中断信号给目前正在 run 的程序
- quit:送出一个 quit 信号给目前正在 run 的程序
- erase:向后删除字符
- kill:删除在目前指令列上的所有文字
- eof:End of file 的意思,代表「结束输入」
- start:在某个程序停止后,重新启动它的 output
- stop:停止目前屏幕的输出
- susp:送出一个 terminal stop 的喜好给正在 run 的程序
比如要设置 ctrl + h 来进行字符的删除
stty erase ^h
# 默认可以看到使用 ^? 但是实际测试的时候,改不回去了
2
错误操作问题:在 windows 下 ctrl + s 是保存功能,在 Linux 使用 vim 时,使用 ctrl + s 整个画面死锁,不能动了,是什么原因?
通过 stty -a 可以看到 ctrl + s 是 stop 功能,停止目前屏幕的输出了,恢复输出的话就是 start,ctrl + q
2
3
除了 stty 之外,bash 还有自己的一些终端机设置
set [-uvCHhmBx]
选项与参数:
- u:预设不启用。若启用后,当使用未设置变量时,会显示错误信息
- v:预设不启用。若启用后,在信息被输出前,会先显示信息的原始内容
- x:预设不启用。若启用后,在指令被执行前,会显示指令内容(前面有 ++ 符号)
- h:预设启用。与历史命令有关
- H:预设启用。与历史命令有关
- m:预设启用。与工作管理有关
- B:预设启用。与括号
[]
的作用有关 - C:预设不启用。若使用 > 等,则若文件存在时,该文件不会被覆盖
# 范例 1: 显示目前所有的 set 设置
[mrcode@study ~]$ echo $-
himBH
# 范例 2:若使用未定义变量时,则显示错误信息
[mrcode@study ~]$ set -u
[mrcode@study ~]$ echo $mrcode
-bash: mrcode: unbound variable
[mrcode@study ~]$ set +u # 关闭该功能使用 + 号
[mrcode@study ~]$ echo $mrcode
[mrcode@study ~]$
# 范例 3:执行前,显示该指令内容
[mrcode@study ~]$ set -x
++ printf '\033]0;%s@%s:%s\007' mrcode study '~'
[mrcode@study ~]$ echo ${HOME}
+ echo /home/mrcode
/home/mrcode
++ printf '\033]0;%s@%s:%s\007' mrcode study '~'
#要输出的指令都会被先打印到屏幕上,前面会多出 + 号
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
另外,还有其他的按键设置功能,前一小节提到的 /etc/inputrc 这个文件里面设置。还有例如 /etc/DIR_COLORS* 与 /usr/share/terminfo/*
等,也都是与终端机有关的环境配置文件。但是这里不建议修改 tty 的环境,因为 bash 的环境以及设置的很亲和了。
bash 默认的组合键汇总如下
组合按键 | 功能 |
---|---|
ctrl + c | 终止目前的命令 |
ctrl + D | 输入结束(EOF),例如邮件结束的时候 |
ctrl + M | Enter |
ctrl + S | 暂停屏幕的输出 |
ctrl + Q | 恢复屏幕的输出 |
ctrl + U | 在提示字符下,将整列命令删除 |
ctrl + Z | 暂停 目前的命令 |
# 通配符与特殊符号
在 bash 操作环境中,通配符(wildcard)是非常有用的,利用 bash 处理数据就更方便了。下面是一些常用的通配符:
符号 | 含义 |
---|---|
* | 代表「0 个到无穷多个」任意字符 |
? | 代表「一定有一个」任意字符 |
[] | 代表「一定由一个在括号内」的字符(非任意字符)。例如[abcd] 则表示一定由一个字符,可能是 a、b、c、d 中的任意一个 |
[-] | 若有减号在括号中时,表示「在编码顺序内的所有字符」。例如[0-9] ,表示 0~9 之前所有数字 |
[^] | 若括号中的第一个字符为指数符号 ^,表示反向旋转,例如[^abc] ,表示不包含 a、b、c |
实践练习
# 范例 1:找出 /etc 下一 cron 开头的文件名
[mrcode@study ~]$ ll -d /etc/cron* # -d 仅显示目录
drwxr-xr-x. 2 root root 54 Oct 4 18:25 /etc/cron.d
drwxr-xr-x. 2 root root 57 Oct 4 18:25 /etc/cron.daily
# 范例 2:找出 etc 下刚好是 5 个字母的目录名
[mrcode@study ~]$ ll -d /etc/?????
drwxr-x---. 3 root root 83 Oct 4 18:38 /etc/audit
drwxr-xr-x. 4 root root 71 Oct 4 18:25 /etc/avahi
# 范例 3:找出 etc 下目录名含有数字的目录
[mrcode@study ~]$ ll -d /etc/*[0-9]* # 记得通过 ** 来模糊匹配
drwxr-xr-x. 4 root root 78 Oct 4 18:22 /etc/dbus-1
-rw-r--r--. 1 root root 5725 Aug 6 21:44 /etc/DIR_COLORS.256color
# 范例 4:找出 etc 下,目录名开头不是小写的目录
[mrcode@study ~]$ ll -d /etc/[^a-z]*
ls: cannot access /etc/[^a-z]*: No such file or directory
# 看到没有找到不是小写的目录,换成非大写的,出来结果了
[mrcode@study ~]$ ll -d /etc/[^A-Z]*
drwxr-xr-x. 3 root root 101 Oct 4 18:23 /etc/abrt
-rw-r--r--. 1 root root 16 Oct 4 18:31 /etc/adjtime
# 范例 5:将范例 4 中找到的文件复制到 /tmp/upper 中
[mrcode@study ~]$ mkdir /tmp/upper; cp -a /etc/[^a-z]* /tmp/upper
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
除了通配符外,bash 环境中的特殊符号还有以下项,这里进行整理:
符号 | 含义 |
---|---|
# | 批注、注释符号 |
\ | 跳脱符号、转义符号 |
| | 管线 pipe:分割两个管线命令的节点(后续介绍) |
; | 连续指令下达分隔符:连续性命令的节点。与管线命令不相同 |
~ | 用户的家目录 |
$ | 取用变量前导符 |
& | 工作控制(job control):将指令变成背景下工作 |
! | 逻辑运算意义上的「非」not 的意思 |
/ | 目录符号:路径分割的符号 |
> 、>> | 数据流重导向:输出导向,分别是「覆盖」和「追加」 |
< 、<< | 数据流重导想:输入导向(下个章节讲解) |
'' | 单引号,不具有变量替换功能,$ 变为纯文本 |
"" | 双引号,具有变量替换功能,$ 可保留相关功能 |
`` | 两个 「`」中间为可以先执行的指令,也可以使用 $() |
() | 在中间为 子 shell 的起始与结束 |
{} | 在中间为命令区块的组合 |
以上是 bash 环境中常见的特殊符号整理,理论上,文件名尽量不要使用上述字符