shell脚本基础(含正则表达式)
目录
打开终端时系统自动调用:/etc/profile或~/.bashrc
Shell脚本基础
一.什么是shell?
Shell 是一个应用程序,它连接了用户和 Linux 内核,让用户能够更加高效、安全、低成本地使用 Linux 内核,这就是 Shell 的本质。然而Shell本身并不是内核的一部分,它只是站在内核的基础上编写的一个应用程序,但是 Shell 也有着它的特殊性,就是开机立马启动,并呈现在用户面前;用户通过 Shell 来使用 Linux,不启动 Shell 的话,用户就没办法使用 Linux。
Shell 也是一种脚本语言,是系统命令的集合,可以使用逻辑判断、循环等语法,可以自定义函数,我们编写完源码后不用编译,直接运行源码即可。
Shell脚本是在Linux的shell中运行的,所以称为shell脚本。本质上,shell脚本就是一些命令的集合。shell脚本可以实现自动化运维,所以能帮助我们很方便的管理服务器;比如我们可以指定一个任务计划,定时的去执行某个shell脚本已满足需求。
shell的类型
在Linux中有很多类型的shell,不同的shell具备不同的功能,shell还决定了脚本中函数的语法,Linux中默认的shell是/bin/bash,流行的shell有ash、bash、ksh、csh、zsh等,不同的shell都有不同的特点以及用途。
通过cat /etc/shells命令可以看到虚拟机中的shell

二.脚本的调用形式
打开终端时系统自动调用:/etc/profile或~/.bashrc
/etc/profile
此文件为系统的每个用户设置环境信息,当用户第一次登入时,该文件被执行,系统的公共环境变量在这里设置,开机自启动的程序,一般也在这里设置。适用于需要对所有用户生效的配置,例如全局环境变量、系统路径设置等。
~/bashrc
用户自己的家目录中的.bashrc,登入时会自动调用,打开任意终端时也会自动调用。
这个文件一般设置与个人用户有关的环境变量,如交叉编译器的路径等等。适用于需要对单个用户的每个交互式 shell 会话生效的配置,例如别名、函数、命令提示符设置等。
三.shell的结构与执行
1.脚本格式
脚本第一行以#!/bin/bash开头。该命令指定告知系统当前这个脚本要使用的shell解释器,否则使用默认shell
以#开头的行作为解释说明。
Shell脚本通常以.sh为后缀,用于区分这是一个shell脚本。
下面来编写一个shell脚本,如下所示:
| [root@shell ~]# cd shell [root@shell shell]# vi 1.sh [root@shell shell]# cat 1.sh #!/bin/bash echo "第一个shell脚本" |

2.脚本的常用执行方式
第一种:采用bash或sh+脚本的相对路径或绝对路径
| [root@shell shell]# bash 1.sh 第一个shell脚本 [root@shell shell]# sh 1.sh 第一个shell脚本 |

第二种:采用输入脚本的绝对路径或相对路径执行脚本。这种执行方法的前提是脚本本身要有可执行权限。
首先要赋予1.sh脚本可执行权限
| [root@shell shell]# chmod +x 1.sh |
执行脚本
| [root@shell shell]# ./1.sh |

四.常用命令
1.查看脚本执行过程
| [root@shell shell]# bash -x 1.sh |

2.查看脚本是否有语法错误
| [root@shell shell]# bash -n 1.sh |

3.date命令
(1)显示年、月、日
| [root@shell shell]# date +%Y-%m-%d #年(以四位数字格式打印年份)月日 [root@shell shell]# date +%y-%m-%d #(以两位数字格式打印年份)月日 [root@shell shell]# date +%F #(以四位数字格式打印年份)月日 |

(2)显示小时、分钟、秒
| [root@shell shell]# date +%H:%M:%S #小时分钟秒 [root@shell shell]# date +%T #小时分钟秒 |

(3)显示星期
| [root@shell shell]# date +%w [root@shell shell]# date +%W |

(4)时间戳
使用命令date +%s显示从1970年1月1日00:00:00 UTC到目前为止的秒数。示例如下:
| [root@shell ~]# date +%s |

使用命令date -d @1728536739显示输入秒数之前的时间。示例如下:
| [root@shell ~]# date -d @1728536739 |

(5)显示一个小时之前/之后
| [root@shell ~]# date -d "+1 hour" [root@shell ~]# date -d "-1 hour" |

(6)表示一天之前/之后
| [root@shell ~]# date -d "+1 day" [root@shell ~]# date -d "-1 day" |

4.历史命令
我们在linux中可以使用history命令查看用户的所有历史操作,同时shell命令操作记录默认保存在用户目录的.bash_history文件中,通过这个文件可以查询shell命令的执行历史。
History命令
查看历史命令列表
| [root@shell ~]# history |

#注意退出终端这些历史命令会被清除。
清除历史命令列表
| [root@shell ~]# history -c |

#历史命令默认会保存1000条,可以在环境变量配置文件/etc/profile中进行修改。设置HISTSIZE和HISTFILESIZE环境变量可以设置所有用户的历史记录条目数。
5.自动补全以及别名
自动补全
在bash中,命令与文件补全是非常方便和常用的功能,我们在命令行输入命令或者文件名/目录名时,只需要填写前面几个文字后,按下键盘上面的“TAB”键,即可自动补全。当填写的文件名有多个在同一目录时,按两下“tab”键,会把系统中所有已改字符开头的命令或目录都显示出来。
命令别名
别名是Shell中的一个功能,它可以让你为常用的命令设置一个简短易记的替代名称。当你输入这个别名时,Shell会自动替换为对应的命令,从而简化命令的输入和记忆
设置命令别名
通过命令行定义的别名只会临时生效,一旦重启系统这个别名就会消失。
| [root@shell ~]# alias 别名='原命令' |

查询命令别名
| [root@shell ~]# alias |

别名永久生效
通过命令行定义的别名,只会临时生效,一旦重启系统这个别名就会消失。如果要让命令长久生效,归根结底是写入相应的配置文件。常见的配置文件有.bashrc(Bash)和.zshrc(Zsh)。你可以使用文本编辑器打开对应的配置文件,在文件末尾添加别名的定义。
| [root@shell ~]# vi .bashrc [root@shell ~]# cat .bashrc # .bashrc # User specific aliases and functions alias rm='rm -i' alias cp='cp -i' alias mv='mv -i' alias 123='ls' # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi |

删除别名
| [root@shell ~]# unalias 别名 |

6.通配符
在 Shell 中,通配符是用于查找文件名和目录名的,它是由 Shell 处理的,只会出现在命令的参数中。
常用通配符:
| * | 匹配任意长度的字符,可以是0个 |
| ? | 匹配任意单个字符,必须是1个 |
| [ ] | 匹配指定的字符范围内的任意单个字符 |
| [a-z,A-Z,0-9] | 匹配所有字母和数字,可以不加逗号 |
| [a-z] | 匹配所有小写字母 |
| [A-Z] | 匹配所有大写字母 |
| [A-z] | 匹配所有大小写字母,等价于[a-z,A-Z] |
7.输入输出重定向以及管道符
(1)Linux的标准输入与输出
| 设备 | 设备名 | 文件描述符 | 类型 |
| 键盘 | /dev/stdin | 0 | 标准输入 |
| 显示器 | /dev/stdout | 1 | 标准输出 |
| 显示器 | /dev/stderr | 2 | 标准错误输出 |
(2)输入重定向
输入重定向:是指不使用系统提供的标准输入端口,而进行重新的指定。换言之,输入重定向就是不使用标准输入端口输入文件,而是使用指定的文件作为标准输入设备。(重定向简单理解就是使用<符来修改标准输入设备)
| 类型 | 符号(语法) | 作用 |
| 标准输入 | 命令<文件1 | 命令把文件1的内容作为标准输入 |
| 结束符限定输入 | 命令<<结束符 | 命令从键盘读取数据,直到遇见结束符 |
| 输入输出重定向(同时使用) | 命令<文件1>文件2 | 命令把文件1的内容作为标准输入,然后输出给文件2 |
(3)输出重定向
输出重定向:输出重定向是把输出的信息写入到一个文件中去,而不是将要输出的文件信息输出到显示器
&表示,1表示正确输出,2表示错误输出,重定向没写任何数字时默认为1.
| 类型 | 符号 | 作用 |
| 标准输出重定向 | 命令>文件 | 以覆盖方式,把命令的正确输出内容输出到指定的文件或设备当中 |
| 标准输出重定向 | 命令>>文件 | 以追加方式,把命令的正确输出内容输出到指定的文件或设备当中 |
| 标准错误输出重定向 | 错误命令 2>文件 | 以覆盖方式,把命令的错误输出输出到指定的文件或设备当中 |
| 标准错误输出重定向 | 错误命令 2>>文件 | 以追加方式,把命令的错误输出输出到指定的文件或设备当中 |
| 正确输出和错误输出同时保存 | 命令>文件 2>&1 | 以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中 |
| 正确输出和错误输出同时保存 | 命令>>文件 2>&1 | 以追加的方式,把正确输出和错误输出都保存到同一个文件当中 |
| 正确输出和错误输出同时保存 | 命令 &>文件 | 以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中 |
| 正确输出和错误输出同时保存 | 命令 &>>文件 | 以追加的方式,把正确输出和错误输出都保存到同一个文件当中 |
| 正确输出和错误输出同时保存 | 命令 >>文件1 2>>文件2 | 把正确的输出追加到文件1中,把错误的输出追加到文件2中 |
(4)/dev/null文件
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到/dev/null文件中。可以理解为这个文件是个垃圾桶。

(5)多命令顺序执行
| 多命令执行符 | 格式 | 作用 |
| ; | 命令1;命令2 | 多个命令执行时,按顺序执行 |
| && | 命令1&&命令2 | 当命令1正确执行时命令2才会执行,当命令1错误执行时命令2不会执行。 |
| || | 命令1||命令2 | 当命令1错误执行时命令2才会执行,当命令1正确执行时命令2不会执行。 |
(6)管道符
| [root@shell ~]# 命令一 | 命令二 |
左边的输出作为右边的输入
#命令1必须有正确的输出才行,如果命令1报错或者命令1没有正确输出,命令2是不能执行的。
示例:

五.shell运算符
语法:“$((运算式))”或“$[运算式]”
例:计算1+2的值
| [root@shell ~]# s=$(( 1 + 2 )) [root@shell ~]# echo $s 3 [root@shell ~]# unset s [root@shell ~]# echo $s [root@shell ~]# s=$[ 1 + 2 ] [root@shell ~]# echo $s 3 |

六.Shell中的特殊符号
| 符号 | 作用 |
| ' | 单引号。在单引号中的所有特殊符号,如"$"和反引号都没有特殊含义。单引号括起来的都是普通字符,会原样输出。 |
| " | 双引号。在双引号中特殊符号都没有特殊含义,但是“$”,反引号和“\”是例外,拥有“调用变量的值”、“引用命令”和“转义符”的特殊含义。 |
| ` | 反引号。反引号括起来的内容是系统命令,在bash中会先执行它。和()作用一样,不过推荐使用(),因为反引号容易被看错。 |
| $() | 和反引号作用一样,用来引用系统命令。 |
| () | 用于一串命令执行时,()中的命令会在子shell中运行。 |
| {} | 用于一串命令执行时,{}中的命令会在当前shell中运行。也可以用于变量变形与替换。 |
| [] | 用于变量的测试。 |
| # | 在shell脚本中,#开头的行代表注释。 |
| $ | 用于调用变量的值。 |
| \ | 转义符,跟在\后面的特殊符号将失去特殊含义,变为普通字符。 |
七.shell变量
在shell脚本中使用变量可以节省时间并且使我们的脚本更加专业,所以当我们编写一个脚本时,就可以使用变量来代替某个使用频繁并且长度很长的字符串。变量的格式:“变量名=变量的值”。
1.变量的命名规则
(1)变量命名可以使用英文字母、数字和下划线,但是不能以数字开头。
(2)等号左右两侧不能有空格,可以使用下划线
(3)变量的值如果有空格,需要使用单引号或者双引号包括。其中双引号括起来的内容如"$" "()"和反引号都拥有特殊含义,而单引号括起来的内容都是普通字符。
(4)环境变量名建议大写,以便于区分。
(5)在bash中,变量默认类型都是字符串类型,无法直接进行数值运算。
2用户自定义变量
使用name=value的形式定义变量,例如a=1.
这种变量是最常见的变量,由用户自定义变量名和变量值
定义变量A:
| [root@shell ~]# A=1 #定义变量A [root@shell ~]# echo $A #调用变量使用 1 |

变量查看
| [root@shell ~]# set [选项] 选项: -u:如果设定此选项,调用未声明变量时会报错(默认无任何提示) -x:如果设定此选项,在命令执行之前,会把命令先输出一次 +<参数> :取消某个set曾启动的参数。 #直接使用set 命令,会查询系统中所有的变量,包含用户自定义变量和环境变量 [root@shell ~]# set -u [root@shell ~]# echo $b -bash: b: 为绑定变量 #当设置了-u选项后,如果调用没有设定的变量会有报错。默认是没有任何输出的。 [root@shell ~]# set -x ++ printf '\033]0;%s@%s:%s\007' root shell '~' [root@shell ~]# ls + ls --color=auto anaconda-ks.cfg RoadToDevOps-master.zip shell ++ printf '\033]0;%s@%s:%s\007' root shell '~' #如果设定了-x选项,会在每个命令执行之前,先把命令输出一次 [root@localhost ~]$ set +x #取消启动的x参数 |


变量删除
| [root@shell ~]# unset 变量名 |

3.交互式定义变量
使用 read 命令提示用户输入,从而实现简单的交互过程。执行时将从标准输入设备(键盘)读入一行内容,并以空格为分隔符,将读入的各字段依次赋值给指定的变量。若指定的变量只有一个,则将整行内容赋值给此变量。
| [root@localhost ~]$ read [选项][变量名] 选项: -p: “提示信息”:在等待read输入时,输出提示信息 -t: 秒数:read命令会一直等待用户输入,使用此选项可以指定等待时间 变量名: 变量名可以自定义,如果不指定变量名,会把输入保存入默认变量REPLY. 如果只提供了一个变量名,则整个输入行赋予该变量. 如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,而命令行上的最后一个变量取得剩余的所有字 |
示例:
| [root@shell ~]# read -p "请输入一个数字:" n 请输入一个数字:10 [root@shell ~]# echo $n 10 [root@shell ~]# read -p "请输入一个数字:" 请输入一个数字:100 [root@shell ~]# echo $REPLY 100 |

4.系统预定义变量
是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的。
常用系统变量:
| PATH | 命令所示路径,以冒号为分割。 |
| HOME | 打印用户家目录。 |
| SHELL | 显示当前shell类型 |
| USER | 打印当前用户 |
| ID | 打印当前用户id信息 |
| PWD | 显示当前所在路径 |
| TERM | 打印当前终端类型 |
| HOSTNAME | 显示当前主机名 |
| HISTSIZE | 历史命令大小 |
举例:

5.环境变量
环境变量指的是出于运行需要而由 Linux 系统提前创建的一类变量,主要用于设置用户的工作环境,包括用户宿主目录、命令查找路径、用户当前目录、登录终端等。环境变量的值由 Linux 系统自动维护,会随着用户状态的改变而改变。
环境变量的查询
| [root@shell ~]# env |

使用env命令可以查看到当前工作环境下的环境变量。对于常见的一些环境变量应了解其各自的用途。例如:
| USER | 表示用户名称 |
| HOME | 表示用户的家目录 |
| LANG | 表示语言和字符集 |
| PWD | 表示当前所在的工作目录 |
| PATH | 表示命令搜索路径 |
| RANDOM | 表示随机数,会返回0-32767的整数 |
env命令和set命令的区别:
set命令可以查看所有变量,而env命令只能查看环境变量。
6.只读变量
Readonly
语法:readonly 变量名
示例:
| [root@shell ~]# b=10 [root@shell ~]# echo $b 10 [root@shell ~]# readonly b [root@shell ~]# b=20 -bash: b: 只读变量 [root@shell ~]# echo $b 10 |

7.特殊变量
| 特殊变量 | 作用 |
| $n | n为数字,$0表示当前shell脚本的名称,$1-9代表第一到第九个参数,10以上的参数需要用大括号包含,如{10}。 |
| $* | 这个变量代表命令行中所有的参数,$*把所有的参数看成一个整体。 |
| $@ | 这个变量代表命令行中所有的参数,$@把每个参数区分对待 |
| $# | 这个变量代表命令行中所有参数的个数 |
| $? | 最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值为非0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。 |
| $$ | 当前进程的进程号(PID) |
| $! | 后台运行的最后一个进程的进程号(PID) |
示例:
| [root@shell shell]# vi bian.sh #创建一个名为bian.sh的脚本 [root@shell shell]# cat bian.sh #!/bin/bash echo "\$1=$1" #\代表转义使\后面的$1变为普通字符 echo "第二个参数是$2" echo "第三个参数是$3" echo "本脚本一共有$#个参数" echo "\$0是$0" [root@shell shell]# bash bian.sh #执行脚本 $1= 第二个参数是 第三个参数是 本脚本一共有0个参数 $0是bian.sh [root@shell shell]# bash bian.sh 1 2 3 #添加参数后执行脚本 $1=1 第二个参数是2 第三个参数是3 本脚本一共有3个参数 $0是bian.sh |

8.数学运算
Shell脚本中的变量常用于数学运算当中
示例如下:
| [root@shell shell]# vi sum.sh #创建一个名为sum.sh的脚本 [root@shell shell]# cat sum.sh #!/bin/bash a=1 b=2 sum=$[$a+$b] echo "$a+$b=$sum" #还可以采用sum=$(($a+$b))这种写法 [root@shell shell]# bash sum.sh #执行脚本 1+2=3 |
八.shell字符串和数组
1.shell字符串
字符串可以由单引号' '包围,也可以由双引号" "包围,也可以不用引号。
| str1=aaaaa str2='bbbbb' str3="ccccc" |
三种形式的区别:
1.不被引号包围的字符串:不被引号包围的字符串中出现变量时也会被解析;字符串中不能出现空格,否则空格后边的字符串会作为其他变量或者命令解析。
2.由单引号包围的字符串:不被引号包围的字符串中出现变量时也会被解析;字符串中不能出现空格,否则空格后边的字符串会作为其他变量或者命令解析。
3.由双引号包围的字符串:如果其中包含了某个变量,那么该变量会被解析,得到该变量的值,而不是原样输出;字符串中可以出现双引号。
(1)获取字符串长度
${#字符串名}
示例如下:
| [root@shell shell]# a=12345 #定义变量a [root@shell shell]# echo ${#a} #输出获取的字符长度 5 |

(2)字符串拼接
将两个字符串并排放在一起就能实现拼接。
示例如下:
| [root@shell shell]# b=11111 [root@shell shell]# c=22222 [root@shell shell]# d=$b$c #中间不能有空格 [root@shell shell]# e="$b $c" #如果被双引号包围,那么中间可以有空格 [root@shell shell]# echo $d 1111122222 [root@shell shell]# echo $e 11111 22222 |
(3)shell字符串截取
Shell截取字符串通常有两种方式:从指定位置开始截取和从指定字符(子字符串)开始截取。
这种方式需要两个参数:除了指定起始位置,还需要截取长度,才能最终确定要截取的字符串。
从左边开始计数时,起始数字是0;从右边开始计数时,起始数字是1。计数方向不同,起始数字也不同。
不管从哪边开始计数,截取方向都是从左到右。
从字符串左边开始计数
| ${字符串: 起始位置: 截取长度} ${string: start: length} |

#string是要截取的字符串,start是起始位置(从左边开始,从0开始计数),length是要截取的长度(省略的话表示直到字符串的末尾)。
从字符串右边开始计数
| ${字符串: 0-从右边数截取的起始位置: 截取长度} ${string: 0-start: length} |
![]()
使用#号截取右边字符
使用#号可以截取指定字符(或者子字符串)右边的所有字符。
| ${字符串#*子字符串} ${string#*chars} |
![]()
#string表示要截取的字符,chars是指定的字符(或者子字符串),*是通配符的一种,表示任意长度的字符串。*chars连起来使用的意思是:忽略左边的所有字符,直到遇见chars(chars不会被截取)。
使用%号截取左边字符
使用%号可以截取指定字符(或者子字符串)左边的所有字符。
| ${字符串%子字符串*} ${string%chars*} |
![]()
# 注意*的位置,因为要截取chars左边的字符,而忽略chars右边的字符,所以*应该位于chars的右侧。
2.shell数组
(1).数组的定义
数组名=(元素1 元素2 元素3 ... 元素n)
| 1.常用的Bash Shell只支持一维数组,不支持多维数组。Shell数组元素的下标也是从0开始计数。 2.在Shell中,用括号( )来表示数组,数组元素之间用空格来分隔。 3.Shell是弱类型的,它并不要求所有数组元素的类型必须相同。 4.Shell数组的长度不是固定的,定义之后还可以增加元素。 5.无需逐个元素地给数组赋值。 注意:赋值号=两边不能有空格,必须紧挨着数组名和数组元素。 |
(2)数组读取
首先我们需要先定义一个数组a=(1 2 3 4 5);
命令 echo ${a[@]} 读取数组中的全部元素。示例如下:
| [root@shell shell]# a=(1 2 3 4 5) [root@shell shell]# echo ${a[@]} 1 2 3 4 5 |

命令 echo ${#a[@]} 获取数组的元素个数。示例如下:

命令 echo ${a[2]} 读取第三个元素,数组从0开始。示例如下:

echo ${a[*]} 等同于 ${a[@]} 作用为显示整个数组。示例如下:

(3)数组赋值
a[1]=100; echo ${a[@]} 替换指定的元素值。示例如下:

a[5]=2; echo ${a[@]} 如果下标不存在则会自动添加一个元素。示例如下:

a[7]=6; echo ${a[@]} 跳着添加元素时,中间未赋值的元素,不显示且无值。示例如下:

(4)数组的删除
跟变量的删除是一个道理
命令unset a[1] 用于删除单个元素。示例如下:

命令unset a 用于删除整个数组。示例如下:

(5)数组分片
在进行实验操作之前,需要对一个数组进行赋值 a=(`seq 1 5`) 。
命令echo ${a[@]:0:3} 表示从第一个元素开始,截取3个元素,并打印出来。示例如下:

命令echo ${a[@]:1:4} 表示从第二个元素开始,截取4个元素,并打印出来。示例如下:

echo ${a[@]:0-3:2} 表示从倒数第3个元素开始,截取2个元素,并打印出来。示例如下:

(6)数组替换
使用命令echo ${a[@]/2/100} 表示用100替换2,但不会保存替换,只是打印出来。示例如下:

使用命令a=(${a[@]/2/100}) 表示用100替换2,这种方法不仅可以打印出来还可以保存替换。示例如下:

九.条件判断
1.两个整数之间比较判断
如下表:
| 判断 | 说明 | 举例 |
| -eq | 检测两个数是否相等 | [ $a -eq $b ] |
| -ne | 检测两个数是否不相等 | [ $a -ne $b ] |
| -gt | 大于 | [ $a -gt $b ] |
| -lt | 小于 | [ $a -lt $b ] |
| -ge | 大于等于 | [ $a -ge $b ] |
| -le | 小于等于 | [ $a -le $b ] |
示例:
| [root@shell ~]# [ 10 -gt 10 ] [root@shell ~]# echo $? #检查上条命令是否正确 1 [root@shell ~]# [ 10 -eq 10 ] [root@shell ~]# echo $? 0 |

2.字符串之间的判断
| 判断 | 说明 | 举例 |
| = | 检测两个字符串是否相等 | [ $a = $b ] |
| != | 检测两个字符串是否不相等 | [ $a != $b ] |
| -z | 检测字符串长度是否为0 | [ -z $a ] |
| -n | 检测字符串长度是否不为0 | [ -n $a ] |
| Str | 检测字符串是否不为空 | [ $a ] |
3.文件判断
用于检测文件的各种属性
| 操作符 | 说明 | 举例 |
| -e | 判断文件或目录是否存在。 | [ -e $file ] |
| -d | 判断是不是目录以及是否存在。 | [ -d $file ] |
| -f | 判断是不是普通文件以及是否存在。 | [ -f $file ] |
| -r | 判断是否有读权限。 | [ -r $file ] |
| -w | 判断是否有读权限。 | [ -x $file ] |
| -x | 判断是否可执行。 | [ -x $file ] |
#权限几个判断,如果只有一个部分符合,则认为是有权限的。
十.shell流程控制
1.if条件判断
(1)单分支if条件语句
基本语法:
| if [ 条件判断式 ] #注意[ 条件判断式 ]中括号和条件判断式之间必须有空格 then #if后面要有空格 程序 fi |
示例如下:
| [root@shell shell]# vi if1.sh [root@shell shell]# cat if1.sh #!/bin/bash a=5 #定义变量a if [ $a -gt 4 ] #如果$a大于4 then echo ok #则输出ok fi [root@shell shell]# bash if1.sh ok |

(2)双分支if语句
基本语法:
| if [ 条件判断式 ] then 条件成立时,执行的程序 else 条件不成立时,执行的程序 fi |
示例如下:
| [root@shell shell]# vi if2.sh [root@shell shell]# cat if2.sh #!/bin/bash a=5 if [ $a -gt 4 ] then echo ok else echo "not ok" fi [root@shell shell]# bash if2.sh ok [root@shell shell]# vi if2.sh [root@shell shell]# cat if2.sh #!/bin/bash a=3 if [ $a -gt 4 ] then echo ok else echo "not ok" fi [root@shell shell]# bash if2.sh not ok |

(3)多分支if条件语句
基本语法:
| if [ 条件判断式1 ] then 当条件判断式1成立时,执行程序1 elif [ 条件判断式2 ] then 当条件判断式2成立时,执行程序2 --省略更多条件-- else 当所有条件都不成立时,最后执行此程序 fi |
示例如下:
| [root@shell shell]# vi if3.sh [root@shell shell]# cat if3.sh #!/bin/bash a=7 if [ $a -gt 5 ] && [ $a -le 10 ] #如果a大于5小于等于10 then echo ok elif [ $a -gt 10 ] && [ $a -lt 20 ] #如果a大于10小于20 then echo "very ok" else echo "not ok" fi [root@shell shell]# bash if3.sh ok [root@shell shell]# vi if3.sh [root@shell shell]# cat if3.sh #!/bin/bash a=12 if [ $a -gt 5 ] && [ $a -le 10 ] then echo ok elif [ $a -gt 10 ] && [ $a -lt 20 ] then echo "very ok" else echo "not ok" fi [root@shell shell]# bash if3.sh very ok |

(4)嵌套
基本语法:
| if [ 条件判断式1 ] then if [ 条件判断式2 ] then 如果条件判断式2成立会做什么 fi 如果条件判断式1成立会做什么 fi |
#可在嵌套中加入else
示例如下:
| [root@shell shell]# vi if4.sh [root@shell shell]# cat if4.sh #!/bin/bash a=10 if [ $a -gt 4 ] then if [ $a -lt 20 ] then echo "ok" #在a大于4小于20的情况下会输出ok else echo "very ok" #在a大于20的情况下会输出very ok fi else echo "not ok" #在a小于或等于4的情况下会输出not ok fi [root@shell shell]# bash if4.sh ok |

(5)多个条件
相当于多命令顺序执行
| if [ 条件判断式 ] && [ 条件判断式 ] 等价于 if [ 条件判断式 -a 条件判断式 ] #-a表示and if [ 条件判断式 ] || [ 条件判断式 ] 等价于 if [ 条件判断式 -o 条件判断式 ] #-o表示or |
示例如下:
| [root@shell shell]# vi if5.sh [root@shell shell]# cat if5.sh #!/bin/bash read -p "请输入水的剩余数量:" a if [ $a -gt 5 ] && [ $a -lt 10 ] #表示在a大于5小于10的情况下时 then echo "库存良好" elif [ $a -gt 10 ] then echo "库存充足" else echo "库存不足" fi [root@shell shell]# bash if5.sh 请输入水的剩余数量:6 库存良好 |

2.多分支case条件语句
case语句和if…elif…else语句一样都是多分支条件语句,不过和if多分支条件语句不同的是,case语句只能判断一种条件关系,而if语句可以判断多种条件关系。
基本语法:
| case $变量名 in “值1”) #不限制值的个数 程序1 #如果变量的值等于值1,则执行程序1 ;; “值2”) 程序2 #如果变量的值等于值2,则执行程序2 ;; #省略其他分支 *) #此处星号代表的是其他值 程序 #如果变量的值都不是以上的值,则执行此程序 ;; esac |
#注意每一个分支程序之后要通过“;;”双分号结尾,代表该程序段结束。
#case 行尾必须为单词“in”,每一个模式匹配必须以右括号“)”结束。
为了让我们能够更加清晰的理解case逻辑判断,接下来我们编写一个脚本来进行实验,内容如下:
| [root@shell shell]# vi case.sh [root@shell shell]# cat case.sh #!/bin/bash read -p "Please input a number:" n #提示输入一个数字n if [ -z "$n" ] #检测$n是否为空 then echo "Please input a number." #如果变量n为空,就打印提示信息,并退出脚本 exit 1 fi n1=`echo $n|sed 's/[0-9]//g'` #使用sed命令将变量n中的所有数字字符删除,并 if [ -n "$n1" ] 将结果存储在n1中。如果输入完全是数字,那么n1 then 将为空字符串;如果输入包含非数字字符,n1将包含 echo "Please input a number." 这些非数字字符。 exit 1 #如果变量n不为空,就打印提示信息,并退出脚本 fi if [ $n -lt 60 ] && [ $n -ge 0 ] then tag=1 elif [ $n -ge 60 ] && [ $n -lt 80 ] then tag=2 elif [ $n -ge 80 ] && [ $n -lt 90 ] then tag=3 elif [ $n -ge 90 ] && [ $n -le 100 ] then tag=4 else tag=0 fi case $tag in 1) echo "not ok" ;; 2) echo "ok" ;; 3) echo "very ok" ;; 4) echo "oook" ;; *) echo "The number range is 0-100." ;; esac |
执行结果如下:

3.for循环
for循环是固定循环,也就是在循环时已经知道需要进行几次的循环,有时也把for循环称为计数循环。
语法一:
| for 变量 in 循环条件 do 程序 done |
#这种语法中for循环的次数,取决于in后面循环条件中值的个数(空格分隔),有几个值就循环几次,并且每次循环都把值赋予变量。也就是说,假设in后面有三个值,for会循环三次,第一次循环会把值1赋予变量,第二次循环会把值2赋予变量,以此类推。
语法二:
| for (( 初始值;循环控制条件;变量变化 )) do 程序 done |
#初始值:在循环开始时,需要给某个变量赋予初始值,如i=1。
#循环控制条件:用于指定变量循环的次数,如i<=100,则只要i的值小于等于100,循环就会继续。
#变量变化:每次循环之后,变量该如何变化,如i=i+1。代表每次循环之后,变量i的值都加1。
语法一举例:从1加到10的和
| [root@shell shell]# vi for1.sh [root@shell shell]# cat for1.sh #!/bin/bash sum=0 for i in `seq 1 10` #`seq 1 10`指的是1-10的数字。变量i从1开始循 do 环,循环10次。 sum=$[$sum+$i] #每次循环给变量sum赋值 echo $i done echo $sum [root@shell shell]# bash for1.sh 1 2 3 4 5 6 7 8 9 10 55 |

语法二举例:从1加到100的和
| [root@shell shell]# vi for2.sh [root@shell shell]# cat for2.sh #!/bin/bash sum=0 for (( i=1;i<=100;i=i+1 )) #定义初始值为1;循环100次;i每经过一次循环 do 后+1 sum=$[$sum+$i] #每次循环给变量sum赋值 done echo $sum [root@shell shell]# bash for2.sh 5050 |

4.while循环
对while循环来讲,只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止。
基本语法:
| while [ 条件判断式 ] do 程序 done |
示例1:
| [root@shell shell]# vi while1.sh [root@shell shell]# cat while1.sh #!/bin/bash while : do read -p "请输入一个数字:" n if [ -z "$n" ] then echo "你需要输入一些内容" continue #结束本次循环 fi n1=`echo $n|sed 's/[0-9]//g'` if [ -n "$n1" ] then echo "你只能输入数字" continue fi break #结束整个循环 done echo $n [root@shell shell]# bash while1.sh 请输入一个数字:aaaa 你只能输入数字 请输入一个数字: 你需要输入一些内容 请输入一个数字:1111 1111 |

示例2:从1加到100
| [root@shell shell]# vi while2.sh [root@shell shell]# cat while2.sh #!/bin/bash i=1 #给变量i和sum赋值 sum=0 while [ $i -le 100 ] #循环直到变量i的值大于100就停止循环 do sum=$[$sum+$i] i=$[$i+1] done echo "这个值是: $sum" [root@shell shell]# bash while2.sh 这个值是: 5050 |

5.特殊流程控制语句
(1)break语句
当程序执行到break语句时,会结束整个当前循环。而continue 语句也是结束循环的语句,不过continue 语句结束单次当前循环,而下次循环会继续。
示例如下:
| [root@shell shell]# vi break1.sh [root@shell shell]# cat break1.sh #!/bin/bash for i in `seq 1 5` do echo $i if [ $i -eq 3 ] then break fi echo $i done [root@shell shell]# bash break1.sh 1 1 2 2 3 |

(2)continue语句
当在shell脚本中使用continue时,结束的不是整个循环,而是本次循环。忽略continue之下的代码,直接进行下一次循环。
示例如下:
| [root@shell shell]# vi continue1.sh [root@shell shell]# cat continue1.sh #!/bin/bash for i in `seq 1 5` do echo $i if [ $i == 3 ] then continue fi echo $i done echo $i [root@shell shell]# bash continue1.sh 1 1 2 2 3 4 4 5 5 5 |

(3)exit语句
系统是有exit命令的,用于退出当前用户的登录状态。可是在Shell脚本中,exit语句是用来退出当前脚本的。也就是说,在Shell脚本中,只要碰到了exit语句,后续的程序就不再执行,而直接退出脚本。
示例如下:
| [root@shell shell]# vi exit.sh [root@shell shell]# cat exit.sh #!/bin/bash for i in `seq 1 5` do echo $i if [ $i == 3 ] then exit fi echo $i done echo 12346 |
执行结果:

6.函数
shell脚本中的函数就是先把一段代码整理到了一个小单元中,并给这个小单元命名,当我们用到这段代码时直接调用这个小单元的名字就可以了,这样很方便,省时省力。
函数的优点和用途:
代码复用:将一组常用的命令封装在函数中,可以在多个地方调用,减少重复代码。
组织代码:使脚本更易于理解和维护,将相关的操作组合在一起。
提高可读性:函数名可以清晰地表达一段代码的功能,提高脚本的可读性。
(1)函数的基本格式
第一种:
| function 函数名 (){ 程序 } |
第二种:
| 函数名 (){ 程序 } |
#这样只是写了函数,但是并没有被调用,所有执行脚本不会有输出。
(2)函数的调用
调用函数时,只需要使用函数名即可。
#需要注意,在shell脚本中,函数一定要写在前面,因为函数要被调用的,如果还未出现就被调用就会出错。
示例:打印出第一个、第二个参数、参数的个数及脚本名。
| [root@shell shell]# vi fun1.sh [root@shell shell]# cat fun1.sh #!/bin/bash input(){ echo $1 $2 $# $0 } input 1 a b #函数名 参数1 参数2 参数3 [root@shell shell]# bash fun1.sh 1 a 3 fun1.sh |

(3)函数的返回值
Return表示退出函数并返回一个退出值,脚本中可以用$?变量显示该值
退出状态码为0通常表示成功,非零值表示失败。
Return语句和exit语句的不同
Return:结束函数的执行,退出一个(退出值,返回值)
Exit:结束shell环境,返回一个(退出值,返回值)给当前的shell
示例:通过return返回一个乘积
| [root@shell ~]# vi test1.sh [root@shell ~]# cat test1.sh #!/bin/bash test1(){ read -p "请输入一个数字:" num return $[$num*2] } test1 echo $? [root@shell ~]# bash test1.sh 请输入一个数字:10 20 |

正则表达式入门与文本三剑客
一.正则表达式
1.什么是正则表达式?
正则表达式使用单个字符串来描述、匹配一系列符合某个语法规则的字符串。在很多文 本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。在 Linux 中,grep, sed,awk 等文本处理工具都支持通过正则表达式进行模式匹配。
正则表达式:
1.使用字符串来描述、匹配一系列符号某个规则的字符串。
2.普通字符包括大小写字母、数字、标点符号及一些其他符号组成,元字符是指在正则表达式中具有特殊意义的专用字符。
2.正则表达式类型
- 基本正则表达式(BRE:basic regular expression)
- 扩展正则表达式(ERE:xtended regular expression)
- 编程语言支持的高级正则表达式
#基本正则表达式、扩展正则表达式的区别是不用写转义符 \
二.基本正则表达式
元字符(字符匹配)
基础正则表达式支持的工具:
grep、egrep、sed、awk
普通字符包括大小写字母、数字、标点符号及一些其他符号。
元字符是指在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。
| 元字符 | 作用 |
| \ | 转义,把一些特殊的符号转换成普通的符号字符,还可以把一些普通字符转换成特殊功能,例:\!、\n、\$等 |
| ^ | 表示匹配字符串开始的位置,匹配行首 |
| $ | 表示匹配字符串末尾的位置,匹配行尾 |
| ^$ | 匹配空行 |
| . | 匹配除换行符\n之外的任意单个字符 |
| * | 匹配前面子表达式0次或多次 |
| .* | 表示任意长度的任一字符,不包括0次 |
| [ ] | 匹配包含在[字符]之中的任意一个字符 |
| [^] | 匹配[^字符]之外的任意一个字符 |
| [-] | 匹配[^字符]之外的任意一个字符 |
| \? | 匹配之前的项1次或者0次 |
| \+ | 匹配其前面字符最少1次,有且大于等于1次 |
| \{n\} | 匹配前面的子表达式n次 |
| \{,n\} | 匹配前面的子表达式不少于n次 |
| \{n,m\} | 匹配前面的子表达式n到m次(m>=n) |
| \w | 匹配包括下划线的任何单词字符 |
| \W | 匹配任何非单词字符。等价于"[^A-Za-z0-9_]" |
| \s | 匹配任何空白字符,包括空格、制表符、换页符等等 |
| \S | 匹配任何非空白字符 |
| \d | 匹配一个数字字符 |
| \D | 匹配一个非数字字符。等价于[^0-9] |
| [:alnum:] | 字母和数字 |
| [:alpha:] | 代表任何英文大小写字符,亦即 A-Z, a-z |
| [:lower:] | 小写字母,示例:[[:lower:]],相当于[a-z] |
| [:upper:] | 大写字母 |
| [:blank:] | 空白字符(空格和制表符) |
| [:space:] | 包括空格、制表符 (水平和垂直)、换行符、回车符等各种类型的空白,比[:blank:]包含的范围广 |
| [:cntrl:] | 不可打印的控制字符(退格、删除、警铃...) |
| [:digit:] | 十进制数字 |
| [:xdigit:] | 十六进制数字 |
| [:graph:] | 可打印的非空白字符 |
| [:print:] | 可打印字符 |
| [:punct:] | 标点符号 |
三.扩展正则表达式
支持的工具:
egrep、awk、grep -E、sed -r
| 字符 | 作用 |
| + | 表示匹配前面的子表达式1次以上 |
| ? | 表示匹配前面的子表达式0次或1次 |
| ( ) | 将括号里的内容看成一个整体 |
| | | 以或的方式匹配字符串 |
四.grep命令
grep命令:允许对文本文件进行模式查找。如果找到匹配模式,grep打印包含模式的所有行。grep支持基本正则表达式,也支持其扩展集。
grep命令的语法为:
| grep [选项] 基本正则表达式 [文件] |
双引号的使用:
在grep命令中输入字符串参数时,最好将其用双引号括起来。
这样做有两个原因,一是以防被误解为shell命令,二是可以用来查找多个单词组成的字符串。
常用选项作用:
| 选项 | 作用 |
| -m | 匹配几次后停止 |
| -v | 取反匹配 |
| -i | 忽略大小写 |
| -n | 显示匹配行号 |
| -c | 统计匹配行数 |
| -o | 仅显示匹配到的字符 |
| -q | 静默模式,执行命令后不输出 |
| -A | 匹配后几行 |
| -B | 匹配前几行 |
| -C | 匹配前后各几行 |
| -w | 匹配整个单词 |
grep举例:
1.显示满足匹配模式的所有行行数
| [root@shell ~]# cat sta.txt my moy mooy moooyyyy MOOOyyyy moooyyyyy abc aabbcc aabbcccc12345 [root@shell ~]# grep -n "mo" sta.txt 2:moy 3:mooy 4:moooyyyy 6:moooyyyyy |
2.显示不匹配的行
| [root@shell ~]# grep -v "mo" sta.txt my MOOOyyyy abc aabbcc aabbcccc12345 |
3.大小写敏感
| [root@shell ~]# grep -i "moo" sta.txt mooy moooyyyy MOOOyyyy moooyyyyy |
4.匹配任意字符
| [root@shell ~]# grep 'm...y' sta.txt moooyyyy moooyyyyy [root@shell ~]# grep 'm..y' sta.txt mooy [root@shell ~]# grep 'm.y' sta.txt Moy |
五.sed命令
sed命令:流编辑器,文本处理工具,本身是一个管道命令,配合正则表达式使用。可以将数据行进行替换、删除、新增、选取等特定工作。
sed命令的语法为:
| sed [选项] '[动作]' 文件名 |
常见选项和作用:
| 选项 | 作用 |
| -n | 一般sed命令会把所有数据都输出到屏幕,如果加入此选择,则只会把经过sed命令处理的行输出到屏幕. |
| -e | 允许对输入数据应用多条sed命令编辑。 |
| -f | 从指定文件中读取编辑脚本。 |
| -r | 在sed中支持扩展正则表达式。 |
| -i | 用sed的修改结果直接修改读取数据的文件,而不是由屏幕输出 |
常见动作和作用:
| 动作 | 作用 |
| num a \ | 追加,在当前行后添加一行或多行。添加多行时,除最后一行外,每行末尾需要用“\”代表数据未完结。num表示第几行 |
| c \ | 行替换,用c后面的字符串替换原数据行,替换多行时,除最后一行外,每行末尾需用“”代表数据未完结。 |
| num i \ | 插入,在当期行前插入一行或多行。插入多行时,除最后一行外,每行末尾需要用“”代表数据未完结。num表示第几行 |
| d | 删除,删除指定的行。 |
| p | 打印,输出指定的行。 |
| s | 字串替换,用一个字符串替换另外一个字符串。 |
| a | 在当前行下面增加一行指定内容。 |
| i | 在选定行上面插入一行指定内容。 |
| c | 将选定行替换为指定内容。 |
sed命令举例:
1.提取行数据
| [root@shell ~]# sed -n '2p' sta.txt moy |
2.删除行数据
| [root@shell ~]# sed '2,4d' sta.txt #删除第二行到第四行的数据 my MOOOyyyy moooyyyyy abc aabbcc aabbcccc12345 |
3.追加插入行数据
| [root@shell ~]# sed '2a hello' sta.txt #在第二行后插入hello my moy hello mooy moooyyyy MOOOyyyy moooyyyyy abc aabbcc aabbcccc12345 [root@shell ~]# sed '2i hello qiu' sta.txt #在第二行前插入hello qiu my hello qiu moy mooy moooyyyy MOOOyyyy moooyyyyy abc aabbcc aabbcccc12345 |
4.替换行数据
| [root@shell ~]$ sed 's/旧字串/新字串/g' 文件名 [root@shell ~]$ sed '行范围s/旧字串/新字串/g' 文件名 |
将第二行moy改成qwe:
| [root@shell ~]# sed '2s/moy/qwe/g' sta.txt my qwe mooy moooyyyy MOOOyyyy moooyyyyy abc aabbcc aabbcccc12345 |
将第二行前添加个注释:
| [root@shell ~]# sed '2s/^/#/g' sta.txt #这里使用正则表达式, "^"代表行首 my #moy mooy moooyyyy MOOOyyyy moooyyyyy abc aabbcc aabbcccc12345 |
六.awk命令
awk命令:是强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。
awk语法:
| awk [选项] '程序' 文件1 文件2 |
awk常用选项:
| 选项 | 作用 |
| -F | 指定输入时用到的字段分隔符 |
| -v | 自定义变量 |
| -f | 从脚本中读取awk命令 |
| -m | 对val值设置内在限制 |
awk内置变量:
| 内置变量 | 作用 |
| $0 | 代表目前awk所读入的整行数据。我们已知awk是一行一行读入数据的,$0就代表当前读入行的整行数据。 |
| $n | 代表目前读入行的第n个字段。比如,$1表示第1个字段(列),$2表示第2个字段(列),如此类推 |
| NF | 当前行拥有的字段(列)总数。 |
| NR | 当前awk所处理的行,是总数据的第几行。 |
| FS | 用户定义分隔符。awk的默认分隔符是任何空格,如果想要使用其他分隔符(如“:”),就需要FS变量定义。 |
| ARGC | 命令行参数个数。 |
| ARGV | 命令行参数数组。 |
| FNR | 当前文件中的当前记录数(对输入文件起始为1)。 |
| OFMT | 数值的输出格式(默认为%.6g) |
| OFS | 输出字段的分隔符(默认为空格) |
| ORS | 输出记录分隔符(默认为换行符) |
| RS | 输入记录分隔符(默认为换行符) |
awk命令举例:
1.打印字符串
文件有多少行,打印出来的字符串就有多少行。
| [root@shell ~]# awk '{print "hello world"}' sta.txt hello world hello world hello world hello world hello world hello world hello world hello world hello world [root@shell ~]# wc -l sta.txt 9 sta.txt |
2.打印磁盘的挂载点
| [root@shell ~]# df -h 文件系统 容量 已用 可用 已用% 挂载点 /dev/mapper/centos-root 17G 1.7G 16G 10% / devtmpfs 476M 0 476M 0% /dev tmpfs 488M 0 488M 0% /dev/shm tmpfs 488M 7.6M 480M 2% /run tmpfs 488M 0 488M 0% /sys/fs/cgroup /dev/sda1 1014M 142M 873M 14% /boot tmpfs 98M 0 98M 0% /run/user/0 [root@shell ~]# df -h | awk '{print $6}' 挂载点 / /dev /dev/shm /run /sys/fs/cgroup /boot /run/user/0 |
3.awk常用统计示例
| 1.打印文件的第一列(域): awk '{print $1}' filename 2.打印文件的前两列(域): awk '{print $1,$2}' filename 3.打印完第一列,然后打印第二列: awk '{print $1 $2}' filename 4.打印文本文件的总行数: awk 'END{print NR}' filename 5.打印文本第一行: awk 'NR==1{print}' filename 6.打印文本第二行第一列: sed -n "2, 1p" filename | awk 'print $1' 7.获取行数NR: df -h | awk 'END{print NR}' 8.对文件进行操作: awk '{print $1}' fileName |
七.字符处理命令
1.sort排序命令
sort命令语法格式:
| sort [选项] 文件名 |
sort命令常用选项:
| 选项 | 作用 |
| -f | 忽略大小写 |
| -b | 忽略每行前面的空白部分 |
| -n | 以数值型进行排序,默认使用字符串型排序 |
| -r | 反向排序 |
| -u | 删除重复行。就是uniq命令 |
| -t | 指定分隔符,默认的分隔符是制表符 |
| -k n[,m] | 按照指定的字段范围排序。从第n字段开始,m字段结束(默认到行尾) |
sort命令示例:
排序用户信息文件

反向排序

2.uniq 取消重复行
uniq命令语法:
| uniq [选项] 文件名 |
选项:
-i:忽略大小写
3.wc 统计命令
wc命令语法:
| wc [选项] 文件名 |
选项:
-l:只统计行数
-w:只统计单词数
-m:只统计字符数
更多推荐


所有评论(0)