Linux-Shell脚本教学

Linux-Shell脚本教学Shell脚本编写规则我们来看一段代码:#!/bin/shcd~mkdirshell_tutcdshell_tutfor((i=0;i10;i++));dotouchtest_$i.txtdo

Shell脚本编写规则

我们来看一段代码:

#!/bin/sh
cd ~
mkdir shell_tut
cd shell_tut

for ((i=0; i<10; i++)); do
    touch test_$i.txt
done

实例解析:
第1行:指定脚本解释器,这里是用/bin/sh做解释器的 (必须在写,而且必须在文件第一行)
第2行:切换到当前用户的home目录
第3行:创建一个目录shell_tut
第4行:切换到shell_tut目录
第5行:循环条件,一共循环10次
第6行:创建一个test_1…10.txt文件
第7行:循环体结束

从上面代码能看出来在shell脚本中既可以使用Linux自身命令,也可以shell语法结合使用
Linux默认安装就带了shell解释器,就是上面的/bin/sh
#!是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。

第一个shell脚本

创建一个hello.sh文件,内容如下:

#!/bin/sh
echo 'hello world'

运行脚本

# 给脚本授权,否则运行脚本就会没有权限
chmod u+x ./hello.sh
# 运行脚本,注意必须是./hello.sh 而不是hello.sh
./hello.sh

或者直接是Bash命令运行脚本

bash hello.sh 

输出重定向

就是将文件执行的结果输入到文件中,以便之后查看:

以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中。

命令 > 文件 2>&1

以追加的方式,把正确输出和错误输出都保存到同一个文件当中。

命令 >> 文件 2>&1

异步输出只需要在最后面添加&

ls >ls.log 2>&1 &

如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到/dev/null中(就好比windows中的垃圾站一样)
列:

./hello.sh >  /dev/null 2>&1

多命令顺序执行

多命令执行符 作用 格式
命令1 ;命令2;命令3 多个命令顺序执行,命令之间没有任何逻辑联系,假设命令1失败那么命令2…也都会执行
&& 命令1 && 命令2;命令3 当命令1正确执行,那么命令2才会执行…
ll 命令1 || 命令2||命令3 当命令1执行不成功那么执行命令2…

列:

cd ~ && ls && touch a.txt

shell中常用的特殊符号

符号 作用
’ ’ 单引号。在单引号中所有的特殊符号,如“$”和”(反引号)都没有特殊含义。单引号括起来的都是普通字符,会原样输出
“ ” 双引号。在双引号中特殊符号都没有特殊含义,但是“$”,”`”(esc键下面)和“\”是例外,拥有“调用变量的值”、“引用命令”和“转义符”的特殊含义。
· · 反引号。反引号括起来的内容是系统命令,在Bash中会先执行它。和( ) 作 用 一 样 , 不 过 推 荐 使 用 ()作用一样,不过推荐使用()作用一样,不过推荐使用(),因为反引号非常容易看错。
$() 和反引号“作用一样,用来引用系统命令。(推荐使用)
() 用于一串命令执行时,()中的命令会在子Shell中运行
{} 用于一串命令执行时,{ }中的命令会在当前Shell中执行。也可以用于变量变形与替换。
[ ] 和[[ ]] 用于变量的测试。 和test命令是等同的 , [[ ]] 结构比[ ]结构更加通用,因为[]结构有很多限制,比如[[]]结构可以直接使用if [[ $a != 1 && $a != 2 ]], 如果不适用双括号, 则为if [ $a -ne 1] && [ $a != 2 ]或者if [ $a -ne 1 -a $a != 2 ]。 而且[[ ]] 中匹配字符串或通配符,不需要引号。 使用[]和[[]]的时候不要吝啬空格,每一项两边都要有空格,[[ 1 == 2 ]]的结果为“假”,但[[1==2]] 就会报错,因为没空格
# 在Shell脚本中,#开头的行代表注释。
$ 用于调用变量的值,如需要调用变量name的值时,需要用$name的方式得到变量的值。
\ 转义符,跟在\之后的特殊符号将失去特殊含义,变为普通字符。如 将 输 出 “ 将输出“ ”符号,而不当做是变量引用。

单引号和双引号以及反引号区别

定义变量时,变量的值可以由单引号’ ‘包围,也可以由双引号” “包围,它们到底有什么区别呢?

以单引号’ ‘包围变量的值时,单引号里面是什么就输出什么,即使内容中有变量和命令(命令需要反引起来)也会把它们原样输出。这种方式比较适合定义显示纯字符串的情况,即不希望解析变量、命令等的场景。

以双引号” “包围变量的值时,输出时会先解析里面的变量和命令,而不是把双引号中的变量名和命令原样输出。这种方式比较适合字符串中附带有变量和命令并且想将其解析后再输出的变量定义。

我的建议:如果变量的内容是数字,那么可以不加引号;如果真的需要原样输出就加单引号;其他没有特别要求的字符串等最好都加上双引号,定义变量时加双引号是最常见的使用场景。

反引号:
反引号的作用等同于$()
列:

echo `ls`
echo $(ls)

变量的分类

用户自定义变量: 这种变量是最常见的变量,由用户自由定义变量名和变量的值。

环境变量: 这种变量中主要保存的是和系统操作环境相关的数据,(懂得都懂,比如配置java的JDK环境变量)

位置参数变量: 这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的。

预定义变量: 是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的。

变量的使用

注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。同时,变量名的命名须遵循如下规则:

  • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
  • 中间不能有空格,可以使用下划线 _。
  • 不能使用标点符号。
  • 不能使用bash里的关键字(可用help命令查看保留关键字)。

有效的 Shell 变量名示例如下:

RUNOOB
LD_LIBRARY_PATH
_var
var2

无效的变量命名:

?var=123
user*name=runoob

用户自定义变量

# 定义变量
name = "hello world"
# 输出变量
set | grep age | awk '{split($0,a,"="); print a[2]}'
#或者
echo $name

查询全部变量

set | grep name

变量删除

unset 变量名

环境变量

查询全部环境变量

env 

环境变量设置

export age=18

取指定环境变量的值

 env | grep age | awk '{split($0,a,"="); print a[2]}'
 # 或者
 echo $age

删除环境变量

unset age

位置参数变量

位置参数变量 作用
$n n为数字,$0表示当前 Shell 脚本程序的名称,$1-9 代 表 第 一 到 第 九 个 参 数 , 十 以 上 的 参 数 需 要 用 大 括 号 包 含 , 如 9代表第一到第九个参数,十以上的参数需要用大括号包含,如9代表第一到第九个参数,十以上的参数需要用大括号包含,如{10}
$* 这个变量代表命令行中所有的参数,$把所有的参数看成一个整体
$@ 这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待
$# 这个变量代表命令行中所有参数的个数

使用方式:

#!/bin/sh

# 去第一个参数
echo  $1
# 取第二个参数
echo  $2

echo  $*
echo  $@
echo  $#

效果如下:

$1 是你给你写的shell脚本传的第一个参数,$2 是你给你写的shell脚本传的第二个参数…

[root@bogon ~]# ./hello.sh a1 a2
a1
a2
a1 a2
a1 a2
2

预定义变量

预定义变量 作用
$? 最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值为非О(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。
$$ 当前进程的进程号(PID)
$! 后台运行的最后一个进程的进程号(PID)

先来看看”$?”这个变量,举个例子说明


#ls命令正确执行
[root@localhost sh]$ ls
count.sh hello.sh parameter2.sh parameter.sh

#预定义变量“$?”的值是0,证明上一个命令执行正确(每一条命令的预定义变量可能都不同这需要自己测试)
[root@localhost sh]$ echo $?
0

[root@localhost sh]$ ls install.log
ls:无法访问install.log:没有那个文件或目录

#当前目录中没有install.log文件,所以ls命令报错了
[root@localhost sh]$ echo $?
2

再来说明下”$ ” 和 ” ”和” !”这两个预定义变量

[root@localhost sh]$ vi variable.sh
#!/bin/bash
#输出当前进程的PID. 这个PID就是variable.sh这个脚本执行时,生成的进程的PID
echo "The current process is $$"

#使用find命令在root目录下查找hello.sh文件
#符号&的意思是把命令放入后台执行,工作管理我们在系统管理章节会详细介绍
find /root -name hello.sh &
#输出这个后台执行命令的进程的PID,也就是输出find命令的PID号
echo "The last one Daemon process is $!"

只读变量

先定义一个变量,然后在声明这个变量是只读

[root@localhost sh]$ vi readonly.sh
#!/bin/bash
a=10
#语法:readonly 变量名
readonly a
a=20   #会报错readonly variable
echo $a

接受键盘输入

[root@localhost ~]$ read [选项][变量名]
选项:
	-a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
	-p: “提示信息”:在等待read输入时,输出提示信息
	-t: 秒数:read命令会一直等待用户输入,使用此选项可以指定等待时间
	-n: 数字:read命令只接受指定的字符数,就会执行
	-s: 隐藏输入的数据,适用于机密信息的输入
    -d: 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志。
    -e: 在输入的时候可以使用命令补全功能。
变量名:
变量名可以自定义,如果不指定变量名,会把输入保存入默认变量REPLY.
如果只提供了一个变量名,则整个输入行赋予该变量.
如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,而命令行上的最后一个变量取得剩余的所有字

写个例子来解释下read命令:

[root@localhost sh]$ vi read.sh
#!/bin/bash
#提示“请输入姓名”并等待30 秒,把用户的输入保存入变量name 中
read -t 30 -p "Please input your name: " name
#(换行,不然和上面的信息叠在一起了)
echo -e "\n"
#看看变量“$name”中是否保存了你的输入
echo "Name is $name"

#提示“请输入年龄”并等待30秒,把用户的输入保存入变量age中
#年龄是隐私,所以我们用“-s”选项隐藏输入
read -s -t 30 -p "Please enter your age: " age
#(换行,不然和上面的信息叠在一起了)
echo -e "\n"
# 输出内容
echo "Age is $age"

#提示“请选择性别”并等待30秒,把用户的输入保存入变量gender
#使用“-n1”选项只接收一个输入字符就会执行(都不用输入回车)
read -n 1 -t 30 -p "Please select your gender[M/F]:" gender
#(换行,不然和上面的信息叠在一起了)
echo -e "\n"
# 输出内容
echo "Sex is $gender"

shell 运算符

在shell中,运算符和其他编程脚本语言一样,常见的有算数运算符、关系运算符、逻辑运算符、字符串运算符、文件测试运算符等

算数运算符

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk , expr, let

expor教程

awk教程

let教学

下表列出了常用的算术运算符,假定变量 a 为 10,变量 b 为 20

运算符 说明 举例
+ 加法 expr $a + $b 结果为 30。
减法 expr $a - $b 结果为 -10。
* 乘法 expr $a \* $b 结果为 200。
/ 除法 expr $b / $a 结果为 2。
% 取余 expr $b % $a 结果为 0。
= 赋值 a=$b 将把变量 b 的值赋给 a。
== 相等。用于比较两个数字,相同则返回 true(真)。 [[ $a == $b ]] 返回 false(假)。
!= 不相等。用于比较两个数字,不相同则返回 true。 [[ $a != $b ]] 返回 true。
[root@localhost ~]$ vi computers.sh
#!/bin/bash
a=10
b=20
#判断是否相等
# 注意: 每一项都要有空格,否则报错
if [[ $a == $b ]]
then
	echo 'a等于b'
else
	echo 'a不等于b'
fi
#!/bin/bash
let a=5+4
let b=9-3
let a++
let b--
echo $a $b

关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:

运算符 单词 说明 举例
-eq equal 检测两个数是否相等,相等返回 true。 [[ $a -eq $b ]] 返回 false。
-ne not equal 检测两个数是否相等,不相等返回 true。 [[ $a -ne $b ]] 返回 true。
-gt great than 检测左边的数是否大于右边的,如果是,则返回 true。 [[ $a -gt $b ]] 返回 false。
-lt less than 检测左边的数是否小于右边的,如果是,则返回 true。 [[ $a -lt $b ]] 返回 true。
-ge great than or equal 检测左边的数是否大于等于右边的,如果是,则返回 true。 [[ $a -ge $b ]]返回 false。
-le less than or equal 检测左边的数是否小于等于右边的,如果是,则返回 true。 [[ $a -le $b ]] 返回 true。

列:

[root@localhost ~]# [[ 20 -eq 201 ]]
[root@localhost ~]# echo $?

返回0那么就是true ,返回1那么就是false

逻辑运算符

下表列出了常用的布尔运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
! 非运算,表达式为 true 则返回 false,否则返回 true。 [[ ! false ]] 返回 true。
-o 或(或者)运算,有一个表达式为 true 则返回 true。 [[ $a -lt 20 -o $b -gt 100 ]] 返回 true。
-a 与(并且)运算,两个表达式都为 true 才返回 true。 [[ $a -lt 20 -a $b -gt 100 ]] 返回 false。

列:

[root@localhost ~]# [[ ! (20 -eq 20) ]]
[root@localhost ~]# echo $?
1

返回0那么就是true ,返回1那么就是false

字符串运算符

下表列出了常用的字符串运算符,假定变量 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。
str 检测字符串是否为空,不为空返回 true。 [[ $a ]] 返回 true。

列:

[root@localhost ~]# [[ 'a'='a' ]]
[root@localhost ~]# echo $?
0

返回0那么就是true ,返回1那么就是false

文件运算符

文件测试运算符用于检测 Unix/Linux 文件的各种属性。

操作符 说明 举例
-b file 检测文件是否是块设备文件,如果是,则返回 true。 [[ -b $file ]]
-c file 检测文件是否是字符设备文件,如果是,则返回 true。 [[ -c $file ]]
-d file 检测文件是否是目录,如果是,则返回 true。 [[ -d $file ]]
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ]]
-g file 检测文件是否设置了 SGID 位,如果是,则返回 true。 [[ -g $file ]]
-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [[ -k $file ]]
-p file 检测文件是否是有名管道,如果是,则返回 true。 [[ -p $file ]]
-u file 检测文件是否设置了 SUID 位,如果是,则返回 true。 [[ -u $file ]]
-r file 检测文件是否可读,如果是,则返回 true。 [[ -r $file ]]
-w file 检测文件是否可写,如果是,则返回 true。 [[ -w $file ]]
-x file 检测文件是否可执行,如果是,则返回 true。 [[ -x $file ]]
-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [[ -s $file ]]
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [[ -e $file ]]

列:

[root@localhost ~]# [[ -f '/root/hello.sh' ]]
[root@localhost ~]# echo $?
0

返回0那么就是true ,返回1那么就是false

流程控制

if条件判断

注意: then之前要换行或者使用;

语法:

if [[  条件判断式  ]];then
		程序
fi
if [[ 条件判断式 ]];then
		条件成立时,执行的程序
else
		条件不成立时,执行的另一个程序
fi
if [ 条件判断式1 ];  then
		当条件判断式1成立时,执行程序1
elif [ 条件判断式2 ]; then
		当条件判断式2成立时,执行程序2
elif [ 条件判断式3 ]; then
		当条件判断式3成立时,执行程序3
…省略更多条件…
else
	当所有条件都不成立时,最后执行此程序
fi

案例 : 判断mysql是否启动,如果没有启动则自动启动

[root@localhost ~]$ vi sh/autostart.sh
#!/bin/bash
# 判断nmap是否存在
nmap=`nmap>/dev/null 2>&1`
# 如果不存在那么就安装
if [[ $? != 255 ]] ;then
	yum -y install nmap > /dev/null 2>&1
	echo 'install nmap'
else
	echo 'nmap exist'
fi

#判断mysql是否启动,如果没有启动则自动启动
port=$(nmap -sT 192.168.42.141|grep tcp |grep mysql | awk '{print $2}')
#使用nmap命令扫描服务器,并截取 apache服务的状态,赋予变量port
#只要状态是open,就证明正常启动
if [[ $port == 'open'  ]]; then
	#则证明apache 正常启动,在正常日志中写入一句话即可
	echo "$(date) httpd is ok!>> /tmp/autostart-acc.log
else
	#否则证明mysql没有启动,后台自动启动mysql, 然后将日志写入到文件中
	/etc/init.d/mysql.sh start > /tmp/mysql-start.log  2>&1 & 
fi

案例:判断用户输入的是什么文件

[root@localhost ~]$ vi sh/if-elif.sh
#!/bin/bash

#接收键盘的输入,并赋予变量file
read -p "Please input a filename: " file
#判断用户输入的是什么文件
if [[  -z "$file"  ]]
then
#判断file变量是否为空,如果为空返回状态码1,可以在外面通过echo $? 来接收错误码
		echo "Error, please input a filename or directory "
		exit 1
elif [[ !(-e "$file")  ]]
#判断file的值是否存在
then
		echo "Your input is not a file or directory!"
		#如果不存在,返回状态码2
		exit 2
elif [[  -f  "$file"  ]]
#判断file的值是否为普通文件
then

		#如果是普通文件,返回状态码3
		echo "$file is a regulare file!"
		exit 3
elif [[ -d  "$file" ]]
#到断file的值是否为目录文件
then
		#如果是目录文件,返回状态码4
		echo "$file is a directory!"
		exit 4
else
	#如果以上判断都不是,返回状态码4
	echo "$file is unknown type!"
	exit 5

fi

多分支case条件语句

case语句和if…elif…else语句一样都是多分支条件语句,不过和if多分支条件语句不同的是,case语句只能判断一种条件关系,而if语句可以判断多种条件关系。
case语句语法如下:

case $变量名 in
	"值1")
	如果变量的值等于值1,则执行程序1
	;; 
	"值2")
	如果变量的值等于值2,则执行程序2
	;;
	…省略其他分支…
	*)
	如果变量的值都不是以上的值,则执行此程序
	;;
esac

这个语句需要注意以下内容:

  1. case语句,会取出变量中的值,然后与语句体中的值逐一比较。
  2. 如果数值符合,则执行对应的程序,如果数值不符,则依次比较下一个值。
  3. 如果所有的值都不符合,则执行 “*)” 而(*代表所有其他值)相当于else。
  4. case语句以“case”开头,以“esac”结尾。
  5. 每一个分支程序之后要通过“;;”双分号结尾,代表该程序段结束(千万不要忘记,每次写case语句,都不要忘记双分号)

案例:

#!/bin/bash
read -p "请输入一个字符,并按Enter确认:" KEY
echo $KEY
case "$KEY" in
	[a-z]|[A-Z])
		echo "您输入的是字母"
		;;
	[0-9])
		echo "您输入的是数字"
		;;

	*)
		echo "您输入的是其他字符(特殊符号)"
		;;
esac


read -p "请输入(start,restart,stop)命令,并按Enter确认:" command 
echo $command
case "$command" in
	start)
		echo "启动程序"
		;;
	restart)
		echo "重启程序"
		;;
	stop)
		echo "关闭程序"
		;;	
	*)
		echo "您输入的命令错误"
		;;
esac

for循环

for循环是固定循环,也就是在循环时已经知道需要进行几次的循环,有时也把for循环称为计数循环。for的语法有如下两种:
语法一:

for 变量 in 值1 值2 值3 …(可以是一个文件等)
	do
		程序
	done
	
	这种语法中for循环的次数,取决于in后面值的个数(空格分隔,或者换行),有几个值就循环几次,并且每次循环都把值赋予变量。
	也就是说,假设in后面有三个值,for会循环三次,第一次循环会把值1赋予变量,第二次循环会把值2赋予变量,以此类推。

语法二:

for (( 初始值;循环控制条件;变量变化 ))
	do
		程序
	done
	
语法二中需要注意:
初始值:在循环开始时,需要给某个变量赋予初始值,如i=1;

循环控制条件:用于指定变量循环的次数,如i<=100,则只要i的值小于等于100,循环就会继续;

变量变化:每次循环之后,变量该如何变化,如i=i+1。代表每次循环之后,变量i的值都加1。

列: 遍历时间单词

#!/bin/bash
# 注意必须空格隔开,才能循环
var='morning noon afternoon evening'
for time in $var
	do
		echo "This time is $time!"
	done
}
# 换行也行
var="a.txt hello.sh ll.log ls.log"
for time in $var
	do
		echo "This time is $time!"
	done
}

列:从1加到100

#!/bin/bash
s=0
for (( i=1;i<=100;i=i+1 ))
#定义循环100 次
do
	s=$(( $s+$i ))
	#每次循环给变量s赋值
	done
echo "The sum of 1+2+...+100 is : $s"

批量解压缩脚本

#!/bin/bash

# 获取启动参数 ,压缩包所在的目录
pack=$1
#进入压缩包目录
cd /$pack

# 查询当前目录下全部.tar.gz文件,如果没有那么就会报错,也不会将信息写入ls.log中
ls *.tar.gz > ls.log

#读取ls.log文件的内容,文件中有多少个值,就会循环多少次,每次循环把文件名赋予变量i ,如果没内容那么就不循环
for i in `cat ls.log`
do
		tar -zxf $i >/dev/null 2>&1
		#加压缩,并把所有输出都丢弃
	done
#删除临时文件ls.log
rm -rf /$pack/ls.log

while 循环

对while循环来讲,只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止。
语法:

while [[ 条件判断式 ]]
	do
		程序
	done

列:

#!/bin/bash
# 随机0-999数
PRICE=$(expr $RANDOM % 1000)
TIMES=0

echo "商品的价格为0-999之间,猜猜看是多少?"
while true
do
  read -p "请输入您猜的价格:" INT
    #let 命令是 BASH 中用于计算的工具,用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量。
    # 如果表达式中包含了空格或其他特殊字符,则必须引起来。常用于自增和自减
	let TIMES++
	if [[ $INT -eq $PRICE ]] ; then
	  echo "恭喜您猜对了,实际价格是 $PRICE"
	  echo "您总共猜了 $TIMES 次"
	exit 0
	elif [[  $INT -gt $PRICE  ]] ; then
	  echo "太高了"
	else
	  echo "太低了"
	fi
done

until 循环

和while循环相反,until循环时只要条件判断式不成立则进行循环,并执行循环程序。一旦循环条件成立,则终止循环。(用的不多)

语法:

until [ 条件判断式 ]
	do
		程序
	done

案例一:1加到100

[root@localhost ~]$ vi sh/until.sh
#!/bin/bash
#从1加到100

i=1
s=0
#t给变量i和变量s赋值

until [[  $i -gt 100  ]]
#循环直到变量i的值大于100,就停止循环
	do
		s=$(( $s+$i ))
		i=$(( $i+1 ))
	done
echo "The sum is: $s"

函数

所谓的函数就是将一些代码,整合到一起,便于复用和使用

函数定义语法:

function name() { 
   
	statements
	[return value]	
}

对各个部分的说明:

  • function是shell中的关键字,专门用来定义函数;
  • name是函数名;
  • statements是函数要执行的代码,也就是一组语句;
  • return value表示函数的返回值,此处用方括号括起来表示这部分可以写也可以不写;
  • 由{ }包围的部分称为函数体,调用一个函数,实际上就是执行函数体中的代码

函数定义的简化写法:
函数定义时可以不写function关键字:

name() { 
   
	statements
	[return value]
}

如果写了function关键词,可以省略函数名后面的括号:

function name { 
   
	statements
	[return value]
}

函数的调用
调用shell函数时可以给他传递参数,也可以不传递。
不传递参数时,直接给出函数名即可:

name

如果要传递参数,多个参数之间以空格分开:

name param1 param2 param3

注意:不管是哪种形式,函数名后都不需要带括号

函数的参数
和其他编程语言不同的是,shell函数在定义时不能指明参数,但在调用时却可以传递参数,并且给它传递什么参数它就接收什么参数

函数参数是shell位置参数的一种,在函数内部可以使用$n来接收
例如:$1表示第一个参数,$2表示第二个参数,以此类推

除了$n,还有另外几个比较重要的变量:

  • $#可以获取传递参数的个数;
  • $@或$*可以一次性获取所有的参数
  • $?可以获取函数的退出状态(接收返回值,返回的类型必须是数字)

在函数内部定义变量,需要使用local进行修饰

local 变量

各种案例演示:

#!/bin/bash

# 定义空函数
function hello(){ 
   

	echo "hello world"
}

# 函数调用
hello


# 定义带返回值的函数
function hello_r(){ 
   
  return 12
}

# 带返回值的函数调用
hello_r
echo $?



# 定义带返回值和使用参数的函数
function hello_r_p(){ 
   
  return `expr $1 + $2`
}
hello_r_p  5  10
echo $?

# 定义带返回值和使用参数的函数,并使用局部变量
function hello_r_p(){ 
   
	#局部变量
	local age=18  
	let age=age+$1
  return $age
}
hello_r_p  2
echo $?


# 使用全局变量作为返回值
returnHell=''
function  pub(){ 
   
	returnHell="你好 $1"
}
pub  huanm
echo $returnHell



# 返回数组的方式
returnArr=()
function  pubArr(){ 
   

	returnArr="a $@"
}
pubArr b c d e f
 
echo $returnArr

for i in  $returnArr
do
	echo $i
done
	

特殊流程控制语句

exit语句

系统是有exit命令的,用于退出当前用户的登录状态。可是在Shell脚本中,exit语句是用来退出当前脚本的。也就是说,在Shell脚本中,只要碰到了exit语句,后续的程序就不再执行,而直接退出脚本。

exit的语法如下:

exit [返回值]

如果exit命令之后定义了返回值,那么这个脚本执行之后的返回值就是我们自己定义的返回值。可以通过查询$?这个变量,来查看返回值
列:

#!/bin/bash

#接收用户的输入,并把输入赋予变量num
read -p "Please input a number: " -t 30 num



#如果变量num 的值是数字,则把num的值替换为空,否则不替换
#把替换之后的值赋予变量y
y=$(echo $num | sed 's/[0-9]//g')


#判断变量y的值如果不为空,输出报错信息,退出脚本,退出返回值为18
[ -n "$y" ] && echo "Error! Please input a number!" && exit 18


#如果没有退出加班,则打印变量num中的数字
echo "The number is: $num"

执行脚本

[root@localhost ~]# ./hello.sh 
Please input a number: aa
Error! Please input a number!
[root@localhost ~]# echo $?
18
break语句

当程序执行到break语句时,会结束整个当前循环。
案例:

#!/bin/bash
for (( i=1;i<=10; i=i+1 ))
#循环十次
do
		if [[  $i -eq 4  ]]
		#如果变量i的值等于4
		then
			break
			#退出整个循环
		fi
	echo $i
	#输出变量i的值
done
continue

continue也是结束流程控制的语句。如果在循环中,continue语句只会结束单次当前循环。
案例:

#!/bin/bash
for (( i=1;i<=10;i=i+1 ))
#循环十次
	do
		if [[ $i -eq 4 ]]
		#如果变量i的值等于4
			then
			continue
			#退出换成continue
		fi
	echo $i
	#输出变量i的值
done

字符串处理

# awk引用外部变量
test='awk code'
test1='awk code1'
echo | awk -v var="$test" -v var1="$test1" '{ print var,var1}'

x="xxx"
y="yyy"
z="zzz"
echo $x $y $z| awk '{print $1,$2,$3}'

# awk 内部变量的定义
echo | awk '{a=1;print a}'
echo | awk  -v a=1 -v b=2 '{c=3 ;print a,b,c}'
#计算字符串长度
string='abcdefg'
echo ${#string}
echo   $string | awk '{print length($0)}'
echo  $(expr length $string)
# 字符串截取
string='abcdefg'
 echo ${string:0:3}
 echo  $(expr substr $string 1 3)
 echo $string | awk '{print substr($0, 0, 3)}'
# 字符串替换
string='ababcddcdefg'
# 正则表达式替换 替换满足条件的全部值 (特殊字符需要使用\\来转义,参数1和参数2都是这样)
echo $string | awk '{gsub(/fg$/, " 你好",$0); print $0 }' # 匹配开头
echo $string | awk '{gsub(/^ab/, " 你好",$0); print $0 }' # 匹配结尾
echo $string | awk '{gsub(/cd/, " 你好",$0); print $0 }' # 匹配包含替换(全部替换)
echo $string | awk '{sub(/cd/, " 你好",$0); print $0 }' # 匹配包含替换(替换第一个)
# 字符串大小写转换
string='Abcdefg'
# 字符串转大写
echo  $string| awk '{ print toupper($0)}'
# 字符串转小写
echo  $string| awk '{ print tolower($0)}'
# 字符串分割 (以逗号分割)
string='Ab,cd,ef,g'
string=$(echo $string| awk -v var="" '{ 
     split($0,arr,",");  for (i = 1; i <= length(arr); ++i){ 
   var=var""arr[i]" 12"}; print var }')
echo $string
# 查询之地字符串是否存在,不存在返回false,存在返回true
string="absdakjwaj"
exists=`echo | awk -v string="$string" '{ exists=index(string,"sd");if(exists!=0){print "true"}else{ print "false"} }'`
echo $exists

# 查询之地字符串是否存在,不存在返回0,存在返回下标(从1开始)
index=`echo | awk -v string="$string" '{ print index(string,"sd"); }'`
echo $index

数组定义

#!/bin/bash
# 数组中可以存放多个值。Bash Shell 只支持一维数组(不支持多维数组),初始化时不需要定义数组大小
# 与大部分编程语言类似,数组元素的下标由 0 开始。
# Shell 数组用括号来表示,元素用"空格"符号分割开,语法格式如下:
# array_name=(value1 value2 ... valuen)
#实例

my_array=(A B "C" D)
echo "第一个元素为: ${my_array[0]}"
echo "第二个元素为: ${my_array[1]}"
echo "第三个元素为: ${my_array[2]}"
echo "第四个元素为: ${my_array[3]}"

# 获取数组中的所有元素
echo "数组的元素为: ${my_array[*]}"
# 获取数组的长度
echo "数组元素个数为: ${#my_array[*]}"

# 遍历数组
#for i in ${my_array[*]} ; do
# echo $i
#done


#for (( i = 0; i < ${#my_array[*]}; i++ )); do
# echo ${my_array[i ]}
#done



# 小提示 直接使用字符串加空格也是数组 ,但是这种数组只能用于循环,数组的其他特性都不能使用
#my_array1="a b c d 1"
#for i in $my_array1 ; do
# echo $i
#done

常用的操作

  • grep 更适合单纯的查找或匹配文本
  • sed 更适合编辑匹配到的文本
  • awk 更适合格式化文本,对文本进行较复杂格式处理

脚本基础模板

#!/bin/bash
#
# 简要描述您的脚本
# 版权 huanmin

function main() { 
   
    :
}

main "$@"

检测包是否安装
rpm包安装的 rpm -qa | grep ruby

yum方法安装的 yum list installed | grep ruby

以上只是一种方式但是不准,我们还可以这样做使用状态码来判断,列如: nmap 他的错误状态码是127 正确状态码是255 ,这个状态码需要自己去测试 echo $?
列:

#!/bin/bash
# 判断nmap是否存在
nmap=`nmap>/dev/null 2>&1`
# 如果不存在那么就安装
if [[ $? != 255 ]] ;then
	yum -y install nmap > /dev/null 2>&1
	echo 'install nmap'
else
	echo 'nmap exist'
fi

判断变量是否是空

# var="" 和var= 都是空
var=
if [ $var ]; then
    echo "NOT NULL"

else
  echo "IS NULL"

fi

去除字符串空格
去除字符串所有空格

var=" 111 112 "
echo ${var} | sed 's/ //g'

去掉开头和结尾的空格

var=" 111 112 "
echo $var | sed -e 's/^[ ]*//g' | sed -e 's/[ ]*$//g'

awk获取字符串长度

echo   "aaaaaa" | awk '{print length($0)}'

var="abxcd"
echo   ${#var}  #必须是变量才行

查询本地端口是否存在

echo $( netstat -lntu | grep 11034 | grep LISTEN)
# 获取长度 如果没有那么长度为0
netstat -lntu | grep 3306 | grep LISTEN | sed s/[[:space:]]//g | awk '{print length($0)}'

获取指定程序的所有PID

PID=`ps -ef|grep "java"|grep -v grep|awk '{print $2}'`
echo $PID

判断当前主机指定端口是否可用

 # 获取当前主机ip
localhostIp=$(/sbin/ifconfig -a |grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:"|head -1)
port=$1  #获取调用文件时,输入的第一个参数 
tel=`echo "" | telnet $localhostIp $port 2> /dev/null`
result=`echo $tel | grep "Escape" `
echo $result
if [[ "$result" != "" ]]
then
    echo "包含"
else
    echo "不包含"
fi


点赞 -收藏-关注-便于以后复习和收到最新内容


有其他问题在评论区讨论-或者私信我-收到会在第一时间回复


如有侵权,请私信联系我


感谢,配合,希望我的努力对你有帮助^_^

Linux-Shell脚本教学

今天的文章Linux-Shell脚本教学分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/65525.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注