我们一起来学Shell - 初识shell
我们一起来学Shell - 初识shell
我们一起来学Shell - shell的变量
我们一起来学Shell - shell的条件判断
我们一起来学Shell - shell的循环控制
我们一起来学Shell - shell的数组
我们一起来学Shell - shell的函数
我们一起来学Shell - shell的并发及并发控制
我们一起来学Shell - 正则表达式
Shell 的分类
-
Linux中默认的Shell是/bin/bash,流行的Shell有
ash
、bash
、ksh
、csh
、zsh
等,不同的Shell都有自己的特点以及用途。bash
大多数Linux系统默认使用的Shell,
bash Shell
是Bourne Shell
的一个免费版本,它是最早的Unix Shellbash还有一个特点,可以
通过help命令来查看帮助
。包含的功能几乎可以涵盖Shell所具有的功能,所以一般的Shell脚本都会指定它为执行路径。
csh
csh是具有C语言风格的一种Shell,其内部命令有52个,较为庞大。
目前使用的并不多,已经被/bin/tcsh所取代。
ksh
Korn Shell
的语法与 Bourne Shell 相同,同时具备了 C Shell 的易用特点。许多安装脚本都使用ksh
ksh 有42条内部命令,与bash相比有一定的限制性。
tcsh
tcsh
是csh
的增强版,与csh
完全兼容。sh
是一个
快捷方式
,已经被/bin/bash
所取代。nologin
指用户不能登录
zsh
目前Linux里最庞大的一种 zsh。
它有84个内部命令,使用起来也比较复杂。
一般情况下,不会使用该Shell。
Shell 能做什么
- 自动化批量系统初始化程序 (update,软件安装,时区设置,安全策略…)
- 自动化批量软件部署程序 (LAMP,LNMP,Tomcat,LVS,Nginx)
- 应用管理程序 (KVM,集群管理扩容,MySQL,DELLR720批量RAID)
- 日志分析处理程序(PV, UV, 200, !200, top 100, grep/awk)
- 自动化备份恢复程序(MySQL完全备份/增量 + Crond)
- 自动化管理程序(批量远程修改密码,软件升级,配置更新)
- 自动化信息采集及监控程序(收集系统/应用状态信息,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL)
- 配合Zabbix信息采集(收集系统/应用状态信息,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL)
- 自动化扩容(增加云主机——>业务上线)
- Zabbix监控CPU 80%+|-50% Python API AWS/EC2(增加/删除云主机) + Shell Script(业务上线)
- 俄罗斯方块,打印三角形,打印圣诞树,打印五角星,运行小火车,坦克大战,排序算法实现
- Shell可以做任何事(一切取决于业务需求)
bash 环境变量文件
/etc/profile
全局(公有)配置,不管是哪个用户,登录时都会读取该文件。
/etc/bashrc
Ubuntu 没有此文件,与之对应的是 /ect/bash.bashrc
它也是全局(公有)的
bash 执行时,不管是何种方式,都会读取此文件。
~/.profile
若 bash 是以 login 方式执行时,读取 ~/.bash_profile,
若它不存在,则读取 ~/.bash_login,
若前两者不存在,读取~/.profile。
图形模式登录时,此文件将被读取,即使存在 ~/.bash_profile 和 ~/.bash_login。
~/.bash_login
若 bash 是以 login 方式执行时,读取 ~/.bash_profile,
若它不存在,则读取 ~/.bash_login,
若前两者不存在,读取 ~/.profile。
~/.bash_profile
Unbutu 默认没有此文件,可新建。
只有 bash 是以 login 形式执行时,才会读取此文件。
通常该配置文件还会配置成去读取 ~/.bashrc。
~/.bashrc
当 bash 是以 non-login 形式执行时,读取此文件。
若是以 login 形式执行,则不会读取此文件。
.bashrc 文件内写一行
source /etc/profile
每执行一次shell,都会执行一次bashrc,然后就会执行source,避免环境变量报错
~/.bash_logout
注销时,且是 longin 形式,此文件才会读取。
在文本模式注销时,此文件会被读取,
图形模式注销时,此文件不会被读取。
bash 环境变量加载
- 图形模式登录时,顺序读取:
/etc/profile
和~/.profile
- 图形模式登录后,打开终端时,顺序读取:
/etc/bash.bashrc
和~/.bashrc
- 文本模式登录时,顺序读取:
/etc/bash.bashrc
,/etc/profile
和~/.bash_profile
- 从其它用户 su 到该用户,则分两种情况:
- 如果
带 -l 参数(或-参数,--login 参数)
,如:su -l username,则 bash 是login
的,它将顺序读取以下配置文件:/etc/bash.bashrc
,/etc/profile
和~/.bash_profile
。 - 如果
没有带 -l 参数
,则 bash 是non-login
的,它将顺序读取:/etc/bash.bashrc
和~/.bashrc
- 如果
- 注销时,或退出 su 登录的用户,如果是
longin
方式,那么 bash 会读取:~/.bash_logout
- 执行自定义的 Shell 文件时,若使用
bash -l a.sh
的方式,则 bash 会读取行:/etc/profile
和~/.bash_profile
,- 若使用其它方式,如:
bash a.sh
,./a.sh
,sh a.sh
(这个不属于bash Shell),则不会读取上面的任何文件
。
- 若使用其它方式,如:
- 上面的例子,凡是读取到
~/.bash_profile
的,若该文件不存在
,则读取 ~/.bash_login
,若前两者不存在
,读取 ~/.profile
。
login shell与non-login shell
login shell
取得 bash 时需要完整的登陆流程的,就称为
login shell
。举例来说,我们登陆终端时,需要输入用户的账号与密码,此时取得的 bash 就称为
login shell
non-login shell
取得 bash 时不需要完整的登陆流程的 ,就称为
non-login shell
。在原本的 bash 环境下再次使用 bash 这个命令,建立了一个bash子进程,没有输入账号密码, 那第二个 bash (子程序) 也是
non-login shell
。
bash 特性
命令和文件自动补齐
很多命令都会提供一个 bash-complete 的脚本,在执行该命令时,敲 tab 可以自动补全参数,会极大提高生产效率。
linux命令自动补全需要安装 bash-completion (
yum install -y bash-completion
)注意: 断开终端,重新连接后可正常 tab 补齐
默认情况下,Bash 为 Linux 用户提供了下列标准补全功能。
- 变量补全
- 用户名补全
- 主机名补全
- 路径补全
- 文件名补全
命令历史记忆功能
Bash 有自动记录命令的功能,自动记录到.bash_history隐藏文件中。还可以在下次需要是直接调用历史记录中的命令
centos 可以通过/etc/profile中的文件来定义一些参数、
在bash中,使用history 命令来查看和操作之前的命令,以此来提高工作效率。
history是bash的内部命令,所以可以使用 help history 命令调出 history 命令的帮助文档(history默认保存1000行)。
关于history的一些用法,可以看我的另一篇博客:有个姑娘叫history
别名功能
alias
命令别名的好处是可以把本来很长的指令简化缩写,来提高工作效率。
alias
# 查看系统当前所有的别名
alias h5='head -5'
# 定义新的别名。这时候输入h5就等于输入’head-5‘
unalias h5
# 取消别名定义如果想要文件永久生效,只需将上述别名命令写到对应用户或者系统 bashrc 文件中
如果想用真实命令可以在命令前面添加反斜杠 ,使别名失效
关于alias的一些用法,可以看我另一篇博客:linux可以这样玩 之 杂乱无章的随笔(不定期更新){搜索:利用alias传参,自定义linux命令}
快捷键
快捷键不区分大小写,大写是为了方便区分
快捷键 | 作用 |
---|---|
ctrl+A | 把光标移动到命令行开头。如果我们输入的命令过长,想要把光标移动到命令行开头时使用。 |
ctrl+E | 把光标移动到命令行结尾。 |
ctrl+C | 强制终止当前的命令。 |
ctrl+L | 清屏,相当于clear命令。 |
ctrl+U | 删除或剪切光标之前的命令。我输入了一行很长的命令,不用使用退格键一个一个字符的删除,使用这个快捷键会更加方便 |
ctrl+K | 删除或剪切光标之后的内容。 |
ctrl+W | 删除或剪切光标之前的内容(注意:和浏览器的快捷键会有冲突,浏览器的快快捷键是关闭当前标签页)。 |
ctrl+Y | 粘贴ctrl+U或ctul+K剪切的内容。 |
ctrl+R | 在历史命令中搜索,按下ctrl+R之后,就会出现搜索界面,只要输入搜索内容,就会从历史命令中搜索。 |
ctrl+D | 退出当前终端。 |
ctrl+Z | 暂停,并放入后台。这个快捷键牵扯工作管理的内容,我们在系统管理章节详细介绍。 |
ctrl+S | 暂停屏幕输出。 |
ctrl+Q | 恢复屏幕输出。 |
前后台作业控制
Linux bash Shell单一终端界面下,经常需要管理或同时完成多个作业,如一边执行编译,一边实现数据备份,以及执行SQL查询等其他的任务。所有的上述的这些工作可以在一个 bash 内实现,在同一个终端窗口完成。
前后台作业的定义
前后台作业实际上对应的也就是前后台进程,因此也就有对应的 pid。在这里统称为作业。
无论是前台作业还是后台作业,两者都来自当前的Shell,是当前Shell的子程序。
前台作业:可以由用户参与交互及控制的作业我们称之为前台作业。
后台作业:在内存可以自运行的作业,用户无法参与交互以及使用[ctrl]+c来终止,只能通过bg或fg来调用该作业。
几个常用的作业命令
command &
直接让作业进入后台运行
[ctrl]+z
将当前作业切换到后台
jobs
查看后台作业状态
fg %n
让后台运行的作业n切换到前台来
bg %n
让指定的作业n在后台运行
kill %n
移除指定的作业n
"n"
为jobs命令查看到的job编号
,不是进程id。- 每一个job会有一个对应的job编号,编号在当前的终端从1开始分配。
- job 编号的使用样式为[n],后面可能会跟有 “+” 号或者 “-” 号,或者什么也不跟。
- “+” 号表示最近的一个job,
- “-” 号表示倒数第二个被执行的Job。
- 注,"+" 号与 “-” 号会随着作业的完成或添加而动态发生变化。
通过jobs方式来管理作业,当前终端的作业在其他终端不可见。
作业脱机管理
- 将作业(进程)切换到后台可以避免由于误操作如[ctrl]+c等导致的job被异常中断的情形,而脱机管理主要是针对终端异常断开的情形。
- 通常使用nohup命令来使得脱机或注销之后,Job依旧可以继续运行。也就是说nohup忽略所有挂断(SIGHUP)信号。
- 如果该方式命令之后未指定&符号,则job位于前台,指定&符号,则job位于后台。
- 不加&
不加&,作业会在一个新的终端运行,当前终端是无法通过
jobs
命令查看到信息,只能通过ps -ef
命令查看进程由于使用的是sleep命令,并且没有加&,所以
ps -ef | grep sleep
会出现两个进程,父进程pid不会变,子进程pid会一直在变,因为他会不断的打开新的终端执行sleep
这个命令,所以,如果要停止这个作业,需要使用kill 父进程pid
命令
nohup sleep
- 加&
使用
jobs
命令,可以查看到当前作业的运行情况(sleep命令后面的数字单位是秒,如果数字写的小,很快就退出了,jobs会查看不到作业)不指定日志目录的情况下,会将日志写入到当前目录下的
nohup.out
文件中
nohup sleep 3000 &
指定日志目录的方法
注释:
>&
等同于2>&1
和&>
,将正确的和错误的日志,都写入到指定的文件中
nohup sleep 3000 >& ./sleep.log &
命令执行的顺序
&& 具备逻辑判断
command1 && command2
只有在 command1 成功执行后才会执行 command2
|| 具备逻辑判断
command1 || command2
在 command1 没有成功执行时执行command2
; (分号)不具备逻辑判断
command1 ; command2
command1无论是否成功执行,执行完成后,都会去执行commadn2
通配符(元字符)表示的不是本意
常见的通配符
注意与正则稍有不同
字符 | 含义 | 实例 |
---|---|---|
* | 匹配0个或多个任意字符 | a*b,a与b之间可以有任意长度的字符,也可以没有。例如:aabcb,ab,azxcb… |
? | 匹配一个任意字符 | a?b,a与b之间必须但也只能存在一个字符,该字符可以是任意字符。例如:aab,abb,acb… |
[list] | 匹配list中的任意单个字符 | a[xyz]b,a与b之间必须但也只能存在一个字符,该字符只能是x或y或z。例如:axb,ayb,azb |
[!list] | 匹配除list中的任意单个字符 | a[!a-z]b,a与b之间必须但也只能存在一个字符,该字符不能是小写字母。例如:aAb,a0b… |
[c1-c2] | 匹配c1-c2间的任意单个字符 | a[0-1]b,a与b之间必须但也只能存在一个字符,该字符只能是数字。例如:a0b,a1b… |
{string1,string2,…} | 匹配string1、string2等中的一个字符串 | a{abc,xyz,opq}b,a与b之间必须但也只能存在一个字符串,字符串只能是abc或xyz或opq。例如:aabcb,axyzb,aopqb… |
Shell 脚本规范
Shell脚本绝大部分命令自己平时也经常使用,但是在写成脚本的时候总觉得写的很难看。而且当我在看其他人写的脚本的时候,总觉得难以阅读。毕竟Shell脚本这个东西不算是正经的编程语言,他更像是一个工具,用来杂糅不同的程序供我们调用。因此很多人在写的时候也是想到哪里写到哪里,基本上都像是一段超长的main函数,不忍直视。同时,由于历史原因,Shell有很多不同的版本,而且也有很多有相同功能的命令需要我们进行取舍,以至于代码的规范很难统一。
考虑到上面的这些原因,我查阅了一些相关的文档,发现这些问题其实很多人都考虑过,而且也形成了一些不错的文章,但是还是有点零散。因此我就在这里把这些文章稍微整理了一下,作为以后我自己写脚本的技术规范。
风格规范
开头有“蛇棒”
所谓shebang其实就是在很多脚本的第一行出现的以”#!”开头的注释,他指明了当我们没有指定解释器的时候默认的解释器,一般可能是下面这样:
#!/bin/bash
上面这种写法可能不太具备适应性,一般我们会用下面的方式来指定:
#!/usr/bin/env bash
除了 bash 之外,可以用下面的命令查看本机支持的解释器:
cat /etc/shells
注释
- 注释的意义不仅在于解释用途,而在于告诉我们注意事项,就像是一个 README。
- 具体的来说,对于Shell脚本,注释一般包括下面几个部分:
- shebang
- 脚本的参数
- 脚本的用途
- 脚本的注意事项
- 脚本的写作时间,作者,版权等
- 各个函数前的说明注释
- 一些较复杂的单行命令注释
参数要规范
- 这一点很重要,当脚本需要接受参数的时候,一定要先判断参数是否合乎规范,并给出合适的回显,方便使用者了解参数的使用。
变量
- 一般情况下会将一些重要的环境变量定义在开头,确保这些变量的存在。
- 一段好的代码通常是不会有很多硬编码在代码里的“魔数”的。如果一定要有,通常是用一个变量的形式定义在开头,然后调用的时候直接调用这个变量,这样方便日后的修改。
缩进
-
因为很多需要缩进的地方(比如if,for语句)都不长,很多人都懒得去缩进,而且很多人不习惯用函数,导致缩进功能被弱化。
-
正确的缩进是很重要的,尤其是在写函数的时候,否则在阅读的时候很容易把函数体跟直接执行的命令搞混。
-
常见的缩进方法主要有"soft tab"和"hard tab"两种:
- 所谓soft tab就是使用n个空格进行缩进(n通常是2或4)
- 所谓hard tab当然就是指真实的"\t"字符
-
对于if和for语句之类的,最好不要把then,do这些关键字单独写一行,这样看上去比较丑。
命名有标准
所谓命名规范,基本包含下面这几点:
- 文件名规范,以.sh结尾,方便识别
- 变量名字要有含义,不要拼错
- 统一命名风格,写 Shell 一般用小写字母加下划线
编码要统一
在写脚本的时候尽量使用 UTF-8 编码,能够支持中文等一些奇奇怪怪的字符。不过虽然能写中文,但是在写注释以及打log的时候还是尽量英文,毕竟很多机器还是没有直接支持中文的,打出来可能会有乱码。
日志和回显
- 日志的重要性不必多说,能够方便回头纠错,在大型的项目里是非常重要的。
- 如果这个脚本是供用户直接在命令行使用的,那么最好还要能够在执行时实时回显执行过程,方便用户掌控。
- 为了提高用户体验,会在回显中添加一些特效,比如颜色啊,闪烁啊之类的。
密码要移除
- 不要把密码硬编写在脚本里。
太长要分行
- 在调用某些程序的时候,参数可能会很长,这时候为了保证较好的阅读体验,我们可以用反斜杠来分行:
./configure \
–prefix=/usr \
–sbin-path=/usr/sbin/nginx \
–conf-path=/etc/nginx/nginx.conf
注意:在反斜杠前有个空格。
代码有效率
- 在使用命令的时候要了解命令的具体做法,尤其当数据处理量大的时候,要时刻考虑该命令是否会影响效率。
- 比如下面的两个sed命令:
sed -n '1p' file
sed -n '1p;1q' file 12
作用一样,都是获取文件的第一行。
但是第一条命令会读取整个文件,而第二条命令只读取第一行。
当文件很大的时候,仅仅是这样一条命令不一样就会造成巨大的效率差异。
当然,这里只是为了举一个例子,这个例子真正正确的用法应该是使用head -n1 file命令
勤用双引号
- 几乎所有的大佬都推荐在使用"$"来获取变量的时候最好加上双引号。
- 不加上双引号在很多情况下都会造成很大的麻烦,
#!/bin/sh
# 已知当前文件夹有一个a.sh的文件
var="*.sh"
echo $var
echo "$var"
运行结果如下:
a.sh
*.sh
可以解释为它执行了下面的命令
echo *.sh
echo "*.sh"
在很多情况下,在将变量作为参数的时候,一定要注意上面这一点,仔细体会其中的差异。上面只是一个非常小的例子,实际应用的时候由于这个细节导致的问题实在是太多了
学会查路径
- 很多情况下,会先获取当前脚本的路径,然后以这个路径为基准,去找其他的路径。通常我们是直接用 pwd 以期获得脚本的路径。
- 不过其实这样是不严谨的,pwd 获得的是当前Shell的执行路径,而不是当前脚本的执行路径。
- 正确的做法应该是下面这两种
script_dir=$(cd $(dirname $0) && pwd)
script_dir=$(dirname $(readlink -f $0 ))
应当先cd进当前脚本的目录然后再pwd,或者直接读取当前脚本的所在路径。
代码要简短
- 这里的简短不单单是指代码长度,而是只用到的命令数。原则上我们应当做到,能一条命令解决的问题绝不用两条命令解决。这不仅牵涉到代码的可读性,而且也关乎代码的执行效率。
- 最最经典的例子如下
cat /etc/passwd | grep root
grep root /etc/passwd
cat 命令最为人不齿的用法就是这样,用的没有任何意义,明明一条命令可以解决,非得加根管道
使用新写法
这里的新写法不是指有多厉害,而是指可能更希望使用较新引入的一些语法,更多是偏向代码风格
- 尽量使用func( ){ }来定义函数,而不是func{ }
- 尽量使用[[ ]]来代替[ ]
- 尽量使用$()将命令的结果赋给变量,而不是反引号
- 在复杂的场景下尽量使用printf代替echo进行回显
其他小技巧
- 路径尽量保持绝对路径,不容易出错,如果非要用相对路径,最好用
./
修饰 - 优先使用bash的变量替换代替awk sed,这样更加简短
- 简单的if尽量使用
&&
和||
,写成单行。比如[[ x > 2]] && echo x
- 当export变量时,尽量加上子脚本的namespace,保证变量不冲突
- 会使用
trap
捕获信号,并在接受到终止信号时执行一些收尾工作 - 使用
mktemp
生成临时文件或文件夹 - 利用
/dev/null
过滤不友好的输出信息 - 会利用命令的返回值判断命令的执行情况
- 使用文件前要判断文件是否存在,否则做好异常处理
- 不要处理
ls
后的数据(比如ls -l | awk '{ print $8 }'
),- ls的结果非常不确定,并且平台有关
- 读取文件时不要使用
for loop
而要使用while read
Shell 脚本调试
Shell脚本的语法调试,使用bash的相关参数进行调试
sh [参数] 文件名.sh
- -n 不要执行script,仅查询语法的问题
- -v 在执行script之前,先将script的内容输出到屏幕上
- -x 将使用的脚本的内容输出到屏幕,该参数经常被使用
脚本运行方式
Linux中Shell脚本的执行通常有4种方式,分别为工作目录执行,绝对路径执行,sh执行,Shell环境执行。
工作目录执行
工作目录执行,指的是执行脚本时,先进入到脚本所在的目录(此时,称为工作目录),然后使用
./脚本
方式执行,这种方式,需要脚本有执行权限,否则会报错Permission denied
绝对路径执行
绝对路径中执行,指的是直接从根目录/到脚本目录的绝对路径,同样也是需要脚本有执行权限
sh 执行
sh执行,指的是用脚本对应的sh或bash来接着脚本执行,所以,脚本不是必须要有执行权限
Shell 环境执行
Shell环境执行,指的是在当前的Shell环境中执行,可以使用 . 接脚本 或 source 接脚本
. test.sh
注意,点后面有空格
source test.sh
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南