1. 简介
这篇文章主要是为了记录一下Shell脚本的使用语法,前几天写了一个shell脚本,其中,也遇到了一些语法不清楚的情况,在此记录一下已备后续使用。
2. 什么是Shell脚本
Shell脚本(英语:Shell script)是一种电脑程序与文本文件,内容由一连串的shell命令组成,经由Unix Shell直译其内容后运作。被当成是一种脚本语言来设计,其运作方式与直译语言相当,由Uninx shell扮演命令行解释器的角色,在读取shell script之后,依次运行其中的shell命令,之后输出结果。利用Shell script可以进行系统管理、文件操作等。
2.1. 来个实例
创建一个名为demo1.sh的shell脚本文件,在该shell脚本中添加如下内容:
#!/bin/sh cd ~ mkdir shell_tut cd shell_tut for((i=0;i<3;i++));do touch test_$i.txt done
点击保存之后,在通过命令sh demo1.sh
来执行该脚本。即可达到我们预期的效果。
实例解析:
第1行:指定脚本解析器,这里是用/bin/sh 做解析器的。
第2行:切换到当前用户的home目录,因为我是用root用户登录的,所以我这边的用户目录是/root
第3行:创建一个目录shell_tut
第4行:切换到shell_tut目录
第5行:循环条件,一共循环10次
第6行:创建一个test_1....2.txt文件
第7行:循环体结束
cd, mkdir, touch都是系统自带的程序,一般在/bin或者/usr/bin目录下。for, do, done是sh脚本语言的关键字。
3. 环境
shell编程跟java、php编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。当前主流的操作系统都支持shell编程,本文所述的shell编程是指Linux下的shell,讲的基本都是POSIX标准下的功能,所以,也适用于Unix及BSD(如Mac OS)。
系统 | 说明 |
Linux | Linux默认安装就带了shell解释器。 |
Mac OS | Mac OS 不仅带了sh、bash这两个最基础的解释器,还内置了ksh、csh、zsh等不常用的解释器 |
Windows | windows出厂时没有内置shell解释器,需要自行安装,为了同时能用grep, awk, curl等工具,最好装一个cygwin或者mingw来模拟linux环境。 |
4.脚本解释器
4.1. sh
即Bourne shell,POSIX(Portable Operating System Interface)标准的shell解释器,它的二进制文件路径通常是/bin/sh
,由Bell Labs开发。
4.2. bash
Bash是Bourne shell,属GNU Project,二进制文件路径通常是/bin/bash。业界通常混用bash、sh、和shell
5. 第一个shell脚本
5.1. 编写
打开文件编辑器,新建一个文件,扩展名为sh(sh代表shell),在脚本前面一般会加上#!/bin/bash
或者#!/bin/sh
用于指定解释器。
输入一些代码,第一行一般是这样的:
#!/bin/sh #!/bin/bash
#!
是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行。
需要注意的是编码格式是需要是Unix(LF) 下的UTF-8格式。
5.2. 运行
运行Shell脚本有两种方法:
第一种方法,sh 【shell脚本名称】
sh demo1.sh
第二种方法./【shell脚本名称】
chmod +x demo1.sh ./demo1.sh
需要注意的是,一定要写成./demo1.sh,而不是demo1.sh,运行其他二进制的程序也一样,直接写demo1.sh,Linux系统会去PATH里寻找有没有叫demo1.sh的,而只有/bin,/sbin,/usr/bin,/usr/sbin等在PATH里。你的当前目录通常不在PATH里,所以写成demo1.sh是会找不到命令的,要用./demo1.sh告诉系统说,就在当前目录找。
6. shell变量
6.1.定义变量
定义变量时,变量名不加美元符合,如:
project_path=/data/server/demo_project
注意,变量名和等号之间不能有空格,变量的命令需要遵循如下原则:
命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
中间不能有空格,可以使用下划线_。
不能使用标点符号。
不能使用bash里的关键字
6.2. 使用变量
使用定义的变量,只需要在变量名前面加美元符号即可,如:
project_path=/data/server/demo_project echo $project_path echo ${project_path}
变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:
7. Shell 字符串
字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。
7.1. 单引号
str='这是一个单纯的字符串' echo $str
7.2. 双引号
name="码农飞哥" echo "我的名字是${name}"
双引号与单引号的区别在于,双引号里面可以有变量,而单引号里面添加变量无效,它只会原样输出单引号里的内容。
7.3. 获取字符串长度
string="abcd" echo ${#string} # 输出 4
提取子字符串
以下实例从字符串第 2 个字符开始截取 4 个字符:
7.4. 定义数组
在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:数组名=(值1 值2 ... 值n)
例如:
array_name=( "张三", "李四", "王五", ) echo ${array_name[1]}
8. 运算符
8.1. 算术运算符
原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk和expr,expr最常用,expr是一款表达式计算工具,使用它能完成表达式的求值操作。
例如,两个树相加(注意使用的是反引号**`,而不是单引号’**)
例如:
#!/bin/bash val=`expr 2 + 2` echo "两数之和为 : $val"
执行脚本,输出结果是:两数之和为 : 4
PS:需要注意的是:
1.表达式和运算符之间要用空格,例如2+2是不对的,必须写成 2 + 2,
下表列出常用的算术运算符,假定变量a为10,变量b为10;
运算符 | 说明 | 举例 |
+ | 加法 | val=expr $a + $b 结果为30 |
- | 减法 | val=expr $a - $b 结果为0 |
* | 乘法 | expr $a \* $b 结果为 100 |
/ | 除法 | expr $b / $a 结果为 1 |
% | 取余 | expr $b % $a 结果为 0 |
= | 赋值 | a=$b 把变量 b 的值赋给 a |
== | 相等。用于比较两个数字,相同则返回 true。 | [ $a == $b ] 返回 true |
!= | 不相等。用于比较两个数字,不相同则返回 true | [ $a != $b ] 返回 false |
注意:条件表达式要放在方括号之间,并且要有空格,例如: [a = = a==a==b] 是错误的,必须写成 [ $a == $b ]。
实例:
#!/bin/bash #author:码农飞哥 #url:https://feige.blog.csdn.net/ a=10 b=10 val=`expr $a + $b` echo "a + b : $val" val=`expr $a - $b` echo "a - b : $val" val=`expr $a \* $b` echo "a * b : $val" val=`expr $b / $a` echo "b / a : $val" val=`expr $b % $a` echo "b % a : $val" if [ $a == $b ] then echo "a 等于 b" fi if [ $a != $b ] then echo "a 不等于 b" fi
8.2 关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字,下表 列出了常用的关系运算符,假定变量a为10,变量b为10
运算符 | 说明 | 举例 |
-eq | 检测两个数是否相等,相等返回true | [ $a -eq $b ] 返回 true |
-ne | 检测两个数是否不相等,不相等返回 true。 | $a -eq $b ] 返回 false |
-gt | 检测左边的数是否大于右边的,如果是,则返回true | [ $a -gt $b ] 返回 false |
-lt | 检测左边的数是否小于右边,如果是,则返回true | [ $a -gt $b ] 返回 false |
-ge | 检测左边的数是否大于等于右边的,如果是,则返回 true。 | [ $a -ge $b ] 返回 true |
-le | 检测左边的数是否小于等于右边的,如果是,则返回 true。 | [ $a -le $b ] 返回 true |
实例: |
#!/bin/bash #author:码农飞哥 #url:https://feige.blog.csdn.net/ a=10 b=10 if [ $a -eq $b ] then echo "$a -eq $b":"a 等于 b" else echo "$a -eq $b":"a 不等于 b" fi if [ $a -ne $b ] then echo "$a -ne $b: a 不等于b" else echo "$a -ne $b a 等于 b" fi if [ $a -gt $b ] then echo "$a -gt $b: a 大于 b" else echo "$a -gt $b: a 不大于 b" fi if [ $a -lt $b ] then echo "$a -lt $b: a 小于 b" else echo "$a -lt $b: a 不小于 b" fi if [ $a -ge $b ] then echo "$a -ge $b: a 大于或等于 b" else echo "$a -ge $b: a 小于 b" fi if [ $a -le $b ] then echo "$a -le $b: a 小于或等于 b" else echo "$a -le $b: a 大于 b" fi
8.3 逻辑运算符
假设变量a为10,变量b为15;
运算符 | 说明 | 举例 |
&& | 逻辑的AND | [[ $a -lt 100 && $b -gt 100 ]] 返回 false |
实例:
a=10 b=15 if [[ $a -lt 100 && $b -gt 100 ]] then echo "返回 true" else echo "返回 false" fi if [[ $a -lt 100 || $b -gt 100 ]] then echo "返回 true" else echo "返回 false" fi
8.4 字符串运算符
下表列出了常用的字符串运算符,假定变量a为"abc",变量b为"efg":
运算符 | 说明 | 举例 |
= | 检测两个字符串是否相等,相等返回true | [ $a = $b ] 返回 false |
!= | 检测两个字符串是否不相等,不相等返回 true | [ $a != $b ] 返回 true |
-z | 检测字符串长度是否为0,为0返回 true | [ -z $a ] 返回 false |
-n | 检测字符串长度是否不为 0,不为 0 返回 true。 | [ -n “$a” ] 返回 true。 |
$ | 检测字符串是否不为空,不为空返回 true。 | [ $a ] 返回 true。 |
实例: |
实例:
a="abc" b="efg" if [ $a = $b ] then echo "$a = $b : a 等于 b" else echo "$a = $b: a 不等于 b" fi if [ $a != $b ] then echo "$a != $b : a 不等于 b" else echo "$a != $b: a 等于 b" fi if [ -z $a ] then echo "-z $a : 字符串长度为 0" else echo "-z $a : 字符串长度不为 0" fi if [ -n "$a" ] then echo "-n $a : 字符串长度不为 0" else echo "-n $a : 字符串长度为 0" fi if [ $a ] then echo "$a : 字符串不为空" else echo "$a : 字符串为空" fi
8.5 文件测试运算符
文件测试运算符用于检测 Unix 文件的各种属性。这里指定file为 /data/server/xiang/script/testscript/demo.sh
操作符 | 说明 | 举例 |
-d file | 检测文件是否是目录,如果是,则返回true | [ -d $file ] 返回false |
-f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回true | [ -f $file ] 返 true |
-r file | 检测文件是否可读,如果是,则返回true。 | [ -r $file ] 返回 true |
-w file | 检测文件是否可写,如果是,则返回 true。 | [ -w $file ] 返回 true。 |
-s file | 检测文件是否为空(文件大小是否大于0),不为空返回 true。 | [ -s $file ] 返回 true |
-e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | [ -e $file ] 返回 true |
实例: |
#!/bin/bash file="/data/server/xiang/script/testscript/demo.sh" ile="/var/www/runoob/test.sh" if [ -r $file ] then echo "文件可读" else echo "文件不可读" fi if [ -w $file ] then echo "文件可写" else echo "文件不可写" fi if [ -f $file ] then echo "文件为普通文件" else echo "文件为特殊文件" fi if [ -d $file ] then echo "文件是个目录" else echo "文件不是个目录" fi if [ -s $file ] then echo "文件不为空" else echo "文件为空" fi if [ -e $file ] then echo "文件存在" else echo "文件不存在" fi
9. shell echo命令
9.1. 显示普通字符
echo "这是一个普通字符"
9.2. 显示转义字符
echo "\"这是一个转义字符 \""
9.3. 显示变量
name="张三三" echo "我的名字是${name}"
9.4. 显示结果定向至文件
echo "我是一个测试文本">myfile
10. Shell流程控制
10.1 if fi
if 语句语法格式:
if condition then command1 .... commandN fi
写成一行
if [ $(ps -ef|grep -c "nginx") -gt 1 ];then echo "true";fi
10.2 if else fi
if else fi 语法格式:
if condition then command1 command2 ... commandN else command fi
实例:
a=10 b=10 if [ $a -eq $b ] then echo "$a -eq $b":"a 等于 b" else echo "$a -eq $b":"a 不等于 b" fi
10.3 if elif fi
if elif fi的语法结构是:
a=10 b=20 if [ $a -gt $b ] then echo "a大于b" elif [ $a -eq $b ] then echo "a等于b" elif [ $a -lt $b ] then echo "a小于b" else echo "没有符合的条件" fi
11. for 循环
Shell支持for循环
for循环一般格式为:
for var in item1 item2 ... itemN do command1 command2 ... commandN done
写成一行
for var in item1 item2 ... itemN; do command1;command2...done;
当变量值在列表里,for循环即执行一次所有命令,使用变量名获取列表中的当前取值。命令可为任何有效的shell命令和语句。in列表可以包含替换、字符串和文件名。
in列表示可选的,如果不用它,for循环使用命令行的位置参数。
for str in 我是 码农飞哥, 做一个 堂堂正正的中国人 do echo $str done
通过空格区分每一个item。
12. while循环
while循环用于不断执行一系列命令,也用于从输入文件中读取数据。其语法格式为;
while condition do command done
如果a小于等于6,a从1开始,每次循环都对a加1,运行上述脚本,返回数字1到6,然后终止。
a=1 while (( $a<=6 )) do echo $a let "a++" done
这里的判断条件需要用(()),运行脚本,输出:
break 命令
break 命令允许跳出所有循环(终止执行后面的所有循环)。
13. 获取服务器IP地址
如果通过shell命令获取服务器的IP地址呢?
我们都知道通过 ifconfig 来获取服务器的网络配置情况,但是当有多个网卡的情况下,如果获取某个网卡的IP地址呢?如下图所示:如何获取网卡eth0的IP地址呢?
命令有点点小复杂,如下所示:
current_ip=$(ifconfig eth0|grep inet|grep -v inet6|awk '{print $2}')
命令解释:
ifconfig eth0 用于查询eth0网卡
grep inet 用于模糊查询inet开头的网络地址
grep -v inet6 用于剔除掉inet6
awk '{print $2}' 用于打印第二个参数。
14. 交互式的命令
shell同样支持交互式命令,shell脚本可以接收我们控制台输入的文本。
echo -n "input {文件名} :" read echo "输入的文件名是:$REPLY"
15. 查看文件中是否存在某内容
查找文件中是否存在某内容,可以通过cat 命令进行查找,其中/dev/null 通常被用于丢弃不需要的输出流,或作为用于输入流的空文件,这些操作通常由重定向完成,任何你想丢弃的数据都可以写入其中
丢弃标准输出
在写shell脚本的时候,只想通过命令的结果执行后面的逻辑,而不想命令执行过程中有一大堆中间结果输出,这时候可以把命令执行过程中的输入全部写入 /dev/null
if cat name.txt|grep 李三三 >/dev/null then echo "文本已经存在不再添加" else echo "文本不存在进行添加" sed -i '$a\李三三' name.txt fi
16. 对jar包进行解压&压缩
jar包是Java中一种可以直接运行的程序包,如果我们需要修改jar包中的某个文件的话,可以执行先执行解压命令
16.1 jar包解压
jar -xvf [jar包名称] [需要解压的文件的在jar中的相对路径]
比如需要修改app.jar包中的 application.yml 文件
jar -xvf app.jar BOOT-INF/classes/application-prod.yml
这里的BOOT-INF/classes/application-prod.yml 是application.yml 文件在jar包中路径,注意不要写成绝对路径
16.2 将修改后的文件压缩进jar包中
可以通过sed命令对application.yml 进行修改,然后通过jar命令将修改后的application.yml 文件打进压缩包中
jar -ufjar包名称] [需要打进压缩包中的文件]
还是以上面的app.jar包为例
jar -uf app.jar BOOT-INF/classes/application-prod.yml
17. 判断文件或者目录是否存在
判断文件或者目录是否存在,不存在则创建,比如首先判断/data/server/train 目录是否存在,不存在则创建
if [ -e /data/server/train ];then echo '目录已经存在' else echo '目录不存在' mkdir -p /data/server/train fi
18. war包的解压与压缩
18.1 解压war包
unzip train.war -d train
将train.war的内容解压到train目录中
18.2 压缩war包
压缩war包需要注意的是需要进入到目标目录的里面,然后对里面的文件进行压缩,不要在目标目录压缩,不然在war中就会多一层目录。
cd train jar -cvf coep-train.war *
总结
本文详细介绍了Shell脚本的一些基础命令,下文将主要介绍sed命令