Linux-Shell

背景

本文主要赘述Linux下Shell、环境变量相关的知识,以及对Linux下一些快捷方式的描述。

内容

shell命令行

以前在创建用户的时候,说到将用户的登录终端修改为/bin/bash,因为它的终端对于用户而言更加友好,实际上,在Linux下可以调用任意的shell终端:

1
2
3
4
5
# 比如使用/bin/dash终端,则
/bin/dash

# 此时相当于创建了一个子shell,需要退出的话,则:
exit

如果我们想要在一行中同时执行多条命令,则可以使用;将命令间隔开:

1
[命令];[命令];[命令];[命令]...

使用上述的方式,在shell中就会依次执行命令,而并不是当成一条shell命令在执行。如果希望将上述命令绑定成一条命令执行,则用小括号包裹命令:

1
([命令];[命令];([命令];[命令])...)

如果你想检验这个是不是真的绑定微一条命令在执行,则可以通过$BASH_SUBSHELL变量的返回值判断:

1
2
# 返回值为0,则表示不是一个shell命令,非0表示是一个shell命令
([命令];[命令];([命令];[命令]);echo $BASH_SUBSHELL...)

有时,命令执行的过程是需要时间的,如果坐在那等,显然不是很理智,此时可以在命令的尾部追加&,将命令执行置入后台运行。

1
2
[命令] &
# 命令执行过程中,如果有输出,千万别被吓着哈

在这个时候,如果需要查看后台有多少任务在执行,除了通过ps -f,还可以:

1
2
3
4
jobs

# 当后台的任务运行结束后,执行jobs,会看到:
[1]+ Done sleep 2

通过&(命令)的方式,我们可以理解为shell中的多进程(毕竟是后台干活了),其次,shell还支持通过coproc创建协程

1
2
3
4
5
6
# 直接调用,则系统默认将该协程命名为COPROC
coproc 命令

# 自定义协程名称
# 命令用{}包裹,名称与{之间有空格,命令必须以;结尾, 且最后的分号与}之间有空格
coproc [协程名称] { ([命令];) }

协程的实现,是通过创建一个子shell后,在子shell中执行命令,但是创建子shell的成本并不低,不过它带来了很多灵活性和便利性。

其实在Linux中,命令分为两类:内建命令、外部命令,内建命令是内嵌在Linux内核中的,直接运行。而外部命令则反之,会自动创建子shell运行。还有些命令,既有内建,也有外部的。可以通过以下方式查看:

1
2
3
4
5
# 如果能输出路径,则说明它可能是外部命令
which [命令名称]

# 或者
type [命令名称]

如果需要查看输入的历史命令,则可以通过history命令:

1
2
3
4
5
6
7
history

# -a:在退出shell前将命令写入历史记录中
history -a

# -n:更新各终端history的历史命令
history -n

有时,你可能需要直接调用上一条命令,那么除了,还可以通过!!直接调用并执行

1
2
3
4
!!

# 也可以通过![num]的方式调用对应编号的history命令,并执行
!20

对于有很多参数的命令,我们反复的手敲,显得很不聪明,Linux也为我们想到了,所以在Linux中允许用户自建命令的别名:

1
alias [别名]='[真实的命令]'

如果想查看当前shell下已经创建了多少别名的命令,则:

1
alias -p

然而这样创建的别名会随着用户退出shell而失效,这也就意味着下次登录的时候需要用户再次手动创建。

环境变量

和程序一样,在Shell中也有全局变量、局部变量一说,全局变量对于所有的shell终端都是可见的,而局部变量只对打开它的shell可见。一般在用户登录的时候,其实系统就已经建立了一些全局变量,可以通过以下方式查看:

1
2
3
4
env

#
printenv

但是当想查看某个环境变量的值时,则使用printenv命令

1
2
3
4
printenv [命令名称]

# 当然也可以用echo
echo $[命令名称]

但对于局部变量而言,则没有具体的命令可以查看,只能通过set将所有的全局、局部、用户变量全部打印出来查看

1
set 

那么用户如何自定义局部变量呢?

1
2
3
4
5
# 通过赋值的方式,=左右没有空格
[变量名]="变量值"

# 调用的时候,加$符号
echo $[变量名]

对于定义的局部变量,当退出该局部变量所属的shell时,则该局部变量则销毁,并且当前shell中的局部变量是无法被其他shell(包括子shell)访问。

那么如何定义全局变量呢?全局 变量需要export导入

1
2
3
4
5
6
7
8
# 先创建一个局部变量
[变量名]="变量值"

# 通过export导入
export [变量名]

# 调用的时候,加$符号
echo $[变量名]

全局变量时允许子shell访问的,但是在子shell中修改全局变量只会影响到该子shell,而不会影响父shell中的全局变量值

而当我们需要删除这些环境变量的时候,则可以通过unset:

1
unset [变量名]

通过上面的描述,你可能有点懵,为什么有的时候变量名需要加$,而有的时候不需要加。对于这个,有个标准:用到变量的时候加$,修改变量的时候不加$

在所有的变量中,有个变量比较特殊:$PATH,它的作用跟windows下的PATH一样,是系统寻找程序的默认路径,如果需要追加新的路径,则:

1
2
# 这种方式修改PATH,等退出shell,或者重启系统时就会失效
PATH=$PATH:[路径]

那么,如何将我们自定义的变量持久化的保存在系统中呢?这个就要说到Linux的Shell启动时加载的文件了。当我们登录Linux系统时,Linux默认会加载以下几个文件:

1
2
3
4
5
6
7
8
9
10
11
# 所有终端用户都会加载的文件
/etc/profile

# 单个用户登录时加载的文件,按从上到下顺序查找,找到一个则将剩余的忽略不加载
# 这四个文件也可能一个都没有,但不影响系统查找
$HOME/.bash_profile
$HOME/.bash_login
$HOME/.profile

# 单个用户特殊加载的文件,这个有啥用,我还真没弄懂,你可以将变量声明在前三个文件中
$HOME/.bashrc

如果你有兴趣查看/etc/profile,就会发现该执行文件会将/etc/profile.d下的.sh都执行一遍。

所以,如果我们想实现变量持久化,则:

  • 对所有的用户都设置一个变量,则将变量的声明和导入到/etc/profile.d下。
  • 针对单个用户,则直接编辑该用户家目录下的四个文件即可

说到这里,其实关于环境变量的内容基本说完了,但是还有一个不常用的知识点,就是在Linux中其实是支持列表类型的变量的,它的定义如下:

1
2
# 变量值用()包裹,变量值之间用空格间隔
[变量名]=([变量值1] [变量值2] [变量值3] ...)

当需要访问其中一个索引的值时,则:

1
2
3
4
5
6
7
8
# 索引从0开始
echo ${变量名[索引值]}

# 修改索引值
变量名[索引值]=值

# 删除指定索引值时
unset 变量名[索引值]

然而当你真的将其当成列表时:

1
2
3
4
5
6
7
8
# 直接打印列表,并不会输出所有的值,而只输出第一个元素
echo $[变量名]

# 如果真的需要打印所有的值的话
echo ${变量名[*]}

# 删除整个列表
unset [变量名]