shell script是利用shell的功能所写的一个程序(program),这个程序是使用纯文本文件,将一些shell的语法与指令(含外部指令)写在里面,搭配正则表达式、管线命令与数据流重导向等功能,让使用者能够one touch的方法去处理复杂的动作,以达到我们所想要的处理目的。
使用文本工具创建脚本文件,推介文本格式后缀为.sh非强制
第一行:
首行必须shebang机制(也就是#!跟上shell类型),声明这个脚本使用的shell名称。当这个程序被执行时,就能够加载bash的相关配置文件,使我们底下的指令能够执行。查看当前Linux系统可以使用的shells,可以检查/etc/shells文件
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
批注:
整个脚本中,除了第一行的#!是必须的之外,其他的以#开头的都可以理解为批注,来说明这个脚本的基本信息,可根据个人需要定制,推荐有以下内容
#程序名称
#版本号
#作者信息
#更改时间
#该程序的作用及注意事项
#各版本的更新简要说明
主要环境变量的说明
将一些重要的环境变量设定好,提高脚本的灵活性。
主要程序
脚本内容,就是为达到脚本目的将语法与指令(含外部指令)写在里面。写较为特殊的代码时,加上批注,便于之后查阅
进程使用退出状态来报告成功或失败
命令:$? 保存了前一个执行结果
返回值0代表成功 , 1-255代表失败。保存最近的命令状态,也就是前一个命令的成功或失败
命令:exit [n]
自定义退出状态码,n可以给定数值,以根据返回值区别不同原因,包括0,范围为0-255。
命令:true和false
返回结果为总是成功与总是失败
bash中不支持浮点运算,仅支持整数数据
运算符号:+、-、、/、%(取余)、*(乘方)
应 用:得到了退出状态返回值,通过算术运算判断结果
通过例题来说明bash中的算术运算方法
例如:运算1+2,赋值 x=1、y=2
方法1: let sum=x+y echo $sum
方法2: echo $[x+y]
方法3: echo $((x+y))
方法4: 命令expr expr 1 + 2中间要加空格,特殊符号要转译
方法5: declare -i x=1; declare -i y=2; sum=m+n; declare -i sum =x+y; echo $sum
方法6: echo 1+2|bc
方法7: ((s=10+20)) echo $s
增强型赋值:+=、-=、*=、/=、%=
n+=2 意思是 n=n+2 得用let定义先
n++意思是 n=n+1
n--意思是n=n-1
例1:[root@V9centos7 /data]#n=10
[root@V9centos7 /data]#let n+=2 <==先用let定义
[root@V9centos7 /data]#echo $n
12 <==运算结果
例2:先++和后++是有区别的
[root@V9centos7 /data]#n=10
[root@V9centos7 /data]#let m=++n;echo $m
11 <==++先给n赋值
[root@V9centos7 /data]#n=10
[root@V9centos7 /data]#let m=n++;echo $m;echo $n
10 <== m先=n,n再赋值
11
与 关系(&)
1与1=1 <==1代表真
1与0=0
0与1=0
0与0=0
或关系(|)
1或1=1
1或0=1
0或1=1
0或0=0
非关系(!)
可以判断逻辑表达式,取反
!1=0
!0=1
例:赋值a=10、b=10,判断是否相等[ $a -eq $b ],返回值echo $为0,[ ! $a -eq $b ]返回值echo $为1。
异或:(^)
相同为0,不同为1
例1:12^8将数字转换成二进制
12 1100 <==第1行
08 1000 <==第2行
异或 04 0100 <==行1与行2,上下对比,相同取0,不同取1
例2:赋值a=15、b=10,调换a和b的值
#a=$[a^b];b=$[a^b];a=$[a^b];echo $a $b
(1) (2) (3)
10 15 <==结果
PS:步骤(1)a和b异或得到中间值c,将a定义为c
步骤(2)现在的a=c,也就是b=[c^b],得到b=a
步骤(3)含义为[c^a],得到a=b
短路与 (&&)
cmd1 && cmd2 如果命令1成功,则执行命令2;如果cmd1失败,则不执行cmd2
短路或(||)
cmd1 || cmd2 如果命令1成功,则不执行命令2;如果cmd1失败,则执行cmd2
应用:利用逻辑关系达成判断式
例1:判断IP地址是否连通,通则显示up,否则显示down
#!/bin/bash <==脚本第一行
read -p "please input ip:" ip
ip=${ip:-ip} <==定义变量
ping -c1 $ip &>/dev/null && echo $ip is up || echo $ip is down <==利用或与逻辑关系达成判断式
PS:指令是一个一个往后执行的,因此在上面的例子中,我们将关系式简写为(1)&&(2)||(3),第一部分执行结果如果回传$?≠0,则'&&'判断不执行第二部分,将$?≠0传递给'||'判断,则执行第三部分
例2:承上例,写一个错误的逻辑关系以对比
ping -c1 $ip &>/dev/null || echo $ip is down && echo $ip is up
172.16.211.220 is down
172.16.211.220 is up
PS:第一部分执行回传结果若为$?≠0,经过'||'判断执行第二部分,第二部分必定是成功的,回传$?=0,经'&&'判断执行3。就同时出现了通与不通。
逻辑处理需要配合测试命令,来判断需求是否满足
[[ expression ]]
支持扩展正则表达式与通配符
变量加双引号,为了避免空值造成语法错误,通配符正则表达式不要加双引号,
比对用“=~”(匹配的意思),支持扩展正则表达式
判断相等使用‘==’,支持通配符,支持字符串和整数
例:判断用户输入的是否为ip
#!/bin/bash
read -p 'please input the IP: ip
[[ "$ip" =~ ^(([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5]) ]] && echo "true" || echo "false"
ps:[[]]括号中的表达式两边要加空格
ip地址判断式解释:整体编组带'.'出现3次,结尾再加一组0-255
选项 | 含义 |
---|---|
-eq | 等于,仅支持整数 |
-ne | 不等 |
-lt | 小于 |
-le | 小于等于 |
-gt | 大于 |
-ge | 大于等于 |
例:判断centos版本,根据版本使用响应命令启动网络
#!/bin/bash <==脚本行首
version=`grep -Eo '[[:digit:]]+' /etc/centos-release |head -n1 ` <==取出版本号
[ $version -eq 6 ] && `service network restart` <==判断版本
[ $version -eq 7 ] && `systemctl restart network`
选项 | 含义 |
---|---|
-b | 是否存在且为块设备文件 |
-c | 是否存在且为字符设备文件 |
-d | 是否存在且为目录文件 |
-f | 是否存在且为普通文件 |
-h | 是否存在且为符号链接文件 |
-p | 是否存在且为字命名管道文件 |
-S | 是否存在且为套接字文件 |
-L | 是否为软链接文件 |
-r | 是否存在且可读 |
-w | 是否存在且可写 |
-x | 是否存在且可执行 |
-u | 是否存在且拥有suid权限 |
-g | 是否存在且拥有sgid权限 |
-k | 是否存在且拥有sticky权限 |
-s | 是否存在且为非空文件 |
-nt | (newer than)判断file1是否比file2新 |
-ot | (older than)判断file1是否比file2旧 |
-ef | file1是否为file2的硬链接 |
选项 | 含义 |
---|---|
-a | 两个条件同时成立 |
-o | 两个条件任何一个成立 |
! | 取反状态 |
例1:判断文件是否为普通文件并且具有写权限,就去掉文件写权限
[ -f "$file" -a -w "$file" ] &&chmod -w fild
例2:判断文件是否为普通文件或者具有写权限,就去掉文件写权限。
[ -f "$file" -o -w "$file" ] &&chmod -w fild
例3:利用非(!)表示如果文件不具有执行权限,就加上执行权限
[ ! -x "$file" ]&& chmod +x $file
修改配置文件,理论上放哪里都可以执行,推荐有侧重的分类放置,方便查找。
按生效范围划分,存在两类:
按功能划分
bash退出的配置文件
在登录Linux时,用户ID就有了一个默认的shell,也就是登录shell。如果这个shell是bash,那么它就会在控制系统前执行几个脚本,然后按顺序执行。等用户退出是,如果主目录中有~/.bash_logout脚本,bash就会执行。
配置文件的生效
1、重新启动shell
2、source
交互式登录
1、直接通过终端输入账号密码登录
2、使用su -UserName切换账户
执行顺序:
/etc/profile ==> /etc.profile.d/*.sh ==> ~/.bash_profile ==> ~/.bashrc ==> /etc/bashrc
非交互式登录
1、su UserName
2、图形界面下打开终端
3、执行脚本
4、任何其他的bash实例
执行顺序:
~/.bashrc ==> /etc/profile ==> /etc.profile.d/*.sh
()与{}可以执行组合命令
{}最后一个命令也要加";",前后要加空格,不开子进程影响当前环境。
()最后一个命令可以不加";",前后可以不加空格,开子进程不影响当前环境
以一组文字或符号等,来取代一些设定或者是一串 保留的数据
1、不能使用程序中的保留字,比如if for do done这些内部命令的关键字
2、可以使用数字、字母和下划线,且不能以数字开头
3、见名知意,不要用汉字
4、统一命名规则:驼峰命名法 userName 将两个单词用大写字母隔开,或者user_name
5、变量内容若有空格使用“”或‘’将内容括起来。单引号视变量内容为字符串,双引号会保留特殊字符如$等的原本特性,使用\将特殊符号转译也行。
6、变量内容需要调用其他指令信息时,使用反向单引号[`指令`]或[$(指令)]
7、若变量为扩增变量内容时,则可用“$变量”或${变量}累加内容。[PATH="$PATH":/home/bin]
8、若变量需要在其他程序执行,则需要以export来使变量变成环境变量
局部变量:生效范围为当前窗口,shell当前进程中,对当前shell之外其他脚本无效,不能传给子进程及上级进程。
环境变量:可以给当前shell用,也可以传递给子进程,不能传递给父进程。
位置变量:在运行脚本时调用通过命令行传递给脚本的参数
只读变量:不可删除的变量,可用命令readonly定义
$1、$2...:对应第1、第2...参数,shift[n]换位置
$0:命令本身
$*:传递给脚本的所有参数,视为一个整体
$@:传递给脚本的所有参数,每个参数为独立字符串
$@和$*在被双引号抱起来的时候才会有差异
$#:传递给脚本的参数个数
set-- 清空所有位置变量
例1:根据位置变量介绍,编写一个测试脚本lication.sh,执行脚本后面跟10个参数,观察执行结果
#!/bin/bash <==脚本第一行
echo 1st arg is $1 <==位置变量$1
echo 2st arg is $2
echo 3st arg is $3
echo 10st arg is $10 <==位置变量$10,这么写会有问题。
echo all args are $@
echo all args are $*
echo the args num is $#
echo the scriptname is $0
[root@V9centos7 /data]#./location.sh a b c d e f g h i j
1st arg is a <==对应变量$1
2st arg is b
3st arg is c
10st arg is a0 <==对应变量10,但是系统会认为是1和0,所以显示为a0,这里要在脚本中该为${10}
all args are a b c d e f g h i j
all args are a b c d e f g h i j
the args num is 10
the scriptname is ./location.sh
命令:shift [n]
将位置变量向左移动1个参数, 1就会去掉,执行 1 就 会 去 掉 , 执 行 2,再执行shift去掉 2,执行 2 , 执 行 3. shift 2一次过两个。
应用:将来编脚本用,处理完就shift,然后就执行下一个,让执行的对象始终放置第一位
例 2:将例1的脚本加上shift命令,观察输出结果
#!/bin/bash
echo all args are $@
echo 1st arg is $1
shift 2 <==一次移动2个参数
echo all args are $@
echo 1st arg is $1
shift 2
echo all args are $*
echo 3st arg is $3
[root@V9centos7 /data]#./location.sh a b c d e f g h i j
all args are a b c d e f g h i j
1st arg is a
all args are c d e f g h i j
1st arg is c <==移动2个参数,c变成第一个
all args are e f g h i j
3st arg is g
变量=值(会覆盖之前的值),值也可以引用命令,文件,变量
PS:在bash中没有设定的变量去echo,会显示空值。
例:变量赋值后,被另一个变量调用,此时再将第一个变量重新赋值,调用它的那个变量仍然显示之前变量。说明变量赋值会有缓冲期,将不使用的变量值当做垃圾文件暂存。
title=’ceo’
name=$title 赋值变量记得加$
echo $name => ceo
title=’boss’ 将title重新赋值
echo $name => ceo name仍然显示ceo
利用命令echo读出变量,只需要在变量前加$
echo $$ 查看当前在那个进程中
echo $PPID 查看父进程
pastree -p 看进程数
set 可以查看当前所有的变量和函数
declare -x 看环境变量
declare –x name=xx 定义环境变量
export 看环境变量,可以将局部变量定义为环境变量
unset 删除变量 只读变量不能删
readonly 查看所有只读变量 加上变量等式定义为只读变量
declare-r 看所有只读变量
env 环境变量与常见环境变量说明
变量设定方式 | 说明 |
---|---|
${变量#关键词} | 若变量内容从头开始的数据符合关键词,则将符合的最短数据删除 |
${变量##关键词} | 若变量内容从头开始的数据符合关键词,则将符合的最长数据删除 |
${变量%关键词} | 若变量内容从尾向首的数据符合关键词,则将符合的最短数据删除 |
${变量%%关键词} | 若变量内容从尾向首的数据符合关键词,则将符合的最长数据删除 |
${变量/旧字符串/新字符串} | 若变量符合旧字符串,则第一个旧字符串会被新的取代 |
${变量//旧字符串/新字符串} | 若变量符合旧字符串,则全部旧字符串会被新的取代 |
例:将root的MAIL变量分别只留基名和目录名,再将root替换成大写
[root@V9centos7 /data]#echo ${MAIL##*/}
root <==双#号匹配最长的字符串到/
[root@V9centos7 /data]#echo ${MAIL%/*}
/var/spool/mail <==双%号匹配从后向前最短的字符串到/
[root@V9centos7 /data]#echo ${MAIL/root/ROOT}
/var/spool/mail/ROOT
变量设定方式 | 变量未设定 | 变量为空字符 | 变量已设定 |
---|---|---|---|
${变量1-变量2} | 变量2 | 空字符 | 变量1 |
${变量1:-变量2} | 变量2 | 变量2 | 变量1 |
${变量1=变量2} | 变量2 | 空字符 | 变量1 |
${变量1:=变量2} | 变量2 | 变量2 | 变量1 |
${变量1+变量2} | 未设定 | 变量2 | 变量2 |
${变量1:+变量2} | 未设定 | 空字符 | 变量2 |
${变量1?提示语} | 打印错误输出 | 空字符 | 变量1 |
${变量1:?提示语} | 打印错误输出 | 打印错误输出 | 变量1 |
PS:如果只想知道,变量1是否存在,不存在就打印提示语,就可以用?判断。
应用:把输入值分配给一个或多个shell变量,不加选项直接加变量,就是给这个变量赋值
格式:read [选项] [变量]
选项:-p 指定要提示的内容
-s 静默输入,一般用于密码
-t 可以接等待的秒数
-n # 指输入的字符长度#
-d ‘字符’输入结束符
例1:可以对变量赋值,可以对多个
[root@V9centos7 ~]#read name date path<<"v9 20180810 /var/bin/data"
[root@V9centos7 ~]#echo $name
v9
[root@V9centos7 ~]#echo $date
20180810
[root@V9centos7 ~]#echo $path
/var/bin/data
应用:宣告变量的类型
格式:declare [选项] [变量]
选项:-a 将变量定义为数组(array)类型
-i 将变量定义为整数数字(integer)类型
-x 同export,定义为环境变量
-r 将变量定义只读类型,需要注销再登录才能复原
-p 单独列出变量类型
选项:
-E:(默认)不支持\解释功能
-n:不自动换行
-e:若字符串中出现以下字符,则特别加以处理,而不会当成一般文字输出
参数
变量:指定要打印的变量
示例
1.输出www.baidu.com,以蓝色,闪烁格式
echo -e "\033[34;5mwww.baidu.com\e[0m"
echo -e "ok! a\b \nsunday"
ok!
sunday