本文主要是记录学习 Shell 编程时所记录的一些笔记。
Shell 脚本的格式
在 Linux 中有很多命令,每一条命令都可以完成某种特定的任务,这也是 UNIX 的设计哲学:一条命令只做一件事。因此为了组合命令以及便于多次执行,可以使用脚本文件来保存需要执行的命令。
创建一个以 .sh
为后缀的文件,我们可以在其中编写很多指令。如果执行的是刚编写好的脚本文件,那么在执行前还需要为其赋予可执行权限 chmod u+rx filename
。
说到 Shell 脚本执行方式,有下面几种:
bash ./filename.sh
./filename.sh
source ./filename.sh
.filename.sh
在 1、2 中,会创建一个名叫 bash 的子进程,Shell 脚本就会运行在该子进程中,两者的区别就在于 2 需要在脚本开头使用 #!/bin/bash
来指定使用 bash,而方式 1 在执行时就明确需要使用 bash 了。方式 3、4 则是在当前进程中执行 Shell 脚本。
如何理解前两种和后两种执行方式的区别呢?比如在 Shell 脚本中存在切换目录的操作 cd
,在前两种方式中,在脚本执行完成后,当前的路径是不会被改变的,因为是使用另一个进程执行的该脚本,而后两种方式在脚本执行完成后,当前路径就会被改变,因为它就是在当前进程下执行的。
有时候我们会发现 Shell 脚本的第一行往往都是 #!/bin/bash
,写这一行代码有什么作用呢?或者说有什么好处呢?
简而言之,在第一行开头使用 #!
可以在文件中指定脚本的解析方式,在使用 bash filename.sh
来执行 Shell 时,就是直接指定 bash 作为解析方式,文件内以 #
开头的部分都会被当作注释。而在使用 ./filename.sh
来执行脚本文件时,会使用系统自带的 Shell 来解析脚本文件,遇到以 #!
开头的行时就会被当成非注释,因此在开头写上 #!/bin/bash
就表示需要使用 bash 来解析脚本文件。换句话说,如果第一行写的是,#!/usr/bin/python
或者 #!/usr/bin/python3
就表示该文件需要使用 Python 来解析,即使文件后缀为 .sh
。
管道与重定向
- 管道:进程通信的主要工具,在 Shell 中是为了方便两条命令能够进行通信
- 重定向:将标准输出输出到指定的位置
管道
因为在 Shell 编程中,管道是没有名字的,所以也叫匿名管道或者管道符。管道符是 |
,其作用就是将前一个命令执行的结果传递给后面的命令。比如,echo 123 | cat
。
值得注意的是,管道会为两条命令创建一个子进程来进行通信,所以在使用 cd
、pwd
等内部命令时,当前的进程是得不到相应的结果的。
重定向
一个进程默认会打开标准输入、标准输出、错误输出三个文件描述符
- 输入重定向
<
比如read var < /path/filename
- 输出重定向
>
、>>
、2>
、&>
比如echo 123 > /path/filename
>
清空文件内容并输入>>
在文件末尾追加2>
当命令执行错误时,会将错误信息输出到指定文件&>
无论命令执行成功与否,都会将信息输出到指定文件中
变量
定义 变量的命名与大多数编程语言的区别相差不大,都是由字母、数字以及下划线组成且不以数字开头。
变量的赋值
变量名=变量值
(=
两边不允许出现空格,因为出现空格后,变量名会被当作命令,因此会报错)a=123
- 使用 let 为变量赋值
let a=10+20
- 将命令赋值给变量
l=ls
- 将命令结果赋值给变量,使用
$()
或者''
letc=$(ls -l /etc)
- 变量值有空格等特殊字符可以包含在
""
或''
中
变量的引用
定义完变量后就需要使用它,使用方式为 ${变量名}
,当然如果变量在引用时不会引发歧义的话,是可以省略掉 {}
的,什么叫不会引发歧义呢?
1 | s1='hello' |
此外,还有一种使用场景,如果我需要使用的变量为空值,是否可以使用别的值来代替空值,而当变量被被复赋值后,又可以直接输出变量值而非控制?有一种才做叫做变量替换,形式为 变量=${引用变量-替换值}
1 | # 设定 path 为文件名,如果为空则输出下划线 _ |
变量的作用范围
变量的默认作用范围是只在当前的进程当中,也就是说,别的进程想要引用另外一个进程的变量是取不到的。
这就需要用到之前提及过的 Shell 脚本的几种解析方式了,如果我们想在脚本中使用该进程中设置的其他变量,就需要使用到 source filename.sh
或者 . filename.sh
,因为这样脚本就会运行在当前进程下。
- 那如果子进程也想访问父进程中的变量,该如何实现呢?
可以使用 export 关键字,通过
export 变量
的方式,让别的进程也可以放到这一变量。 - 如果想要删除设置过的变量,又该如何实现呢?
可以使用 unset 关键字,通过
unset 变量
来删除变量。
系统环境的变量
系统的环境变量都为大写字母加下划线的组合,要查看有哪些环境变量可以使用命令 env | more
或者 set | more
来查看,使用 set
可以查看更多的命令,这里还是用了管道加 more
指令的方式来分页查看命令
常见的系统变量,比如有 PATH
,当我们要使用非系统自带的指令时,就需要将可执行命令的路劲追加到 PATH
中,也就是使用冒号 :
来分割 PATH=$PATH:新的命令路径
,并记得用 export
来到处 PATH
,这样在使用时,系统就可以找到需要使用的命令。
除了系统变量外,还有一些预定义变量
$?
:用来表示上一条命令是否成功执行,成功为 0, 失败为 1$$
:用来查看当前进程的 PID,在对脚本的运行状态以及监测时会用到$0
:用来查看当前进程的名称
此外,还有用于为脚本传递参数的位置变量,比如 $1
、$2
、$3
、$4
、$5
、$6
、$7
、$8
、$9
、${10}
…… ……
它们分别用来代替第一个脚本参数以及之后的参数,这样我们在编写 Shell 脚本时就可以很方便的指代传进来的参数。
环境变量的配置
环境变量的配置文件有很多个
/etc/profile
保存系统或终端启动时的运行环境,在登录情况下,会被第一个加载~/.bash_profile
/etc/profile.d/
这是一个目录,根据不同的 Shell 版本会执行下面不同的脚本~/.bashrc
/etc/bashrc
简单区别,在 etc
目录下的配置文件,是所有用户通用的;而其他跟在 ~
后的配置文件,表示在家(home)目录下,当前用户特有的配置文件。此外,除了目录的不同,还区分了 profile
和 bashrc
这几种配置文件,这是因为登录当前用户的情况分为 loggin shell
和 no login shell
,也就是说对于 login shell
会使用当前的所有配置文件,而对于 no login shell
则会只使用 bashrc
的文件。
各个配置文件在加载时也分先后,相同的变量,后加载的会覆盖掉先加载的。在登陆状态下,配置文件的加载顺序为
/etc/profile
~/.bash_profile
~/.bashrc
/etc/bashrc
而在非登录的情况下,配置文件是加载不完全的,其加载顺序如下:
~/.bashrc
/etc/bashrc
值得注意的是,这些配置文件都是在每次打开终端或者 Shell 时会去加载,因此如果新添加了环境变量是不会立即生效的,所以要使得环境变量立即生效可以使用 source
来重新加载添加了新环境变量的的配置文件。
数组
- 定义数组
array=( 1 2 3 4 5 )
元素间用空格区分 - 显示数组的所有元素
echo ${array[@]}
- 显示数组元素个数 ```echo $