shell 编程语言脚本总结

发布时间 2023-06-14 10:57:18作者: m_zhuang

一、简单了解shell语言脚本

1. 执行环境

sh bash等

shell 默认为 /bin/bash

2. 执行流程

输入指令 ------> shell解释 ------> 内核 ------> 结果还回shell ------> 用户

例:

打开音乐 --- shell --- 播放音乐 --- shell --- 二进制 --- 内核 --- 调度cpu、内存、网卡、硬盘、调度硬件 --- 声卡 --- 音乐播放器

3. 执行方法

命令 执行效果和要求
sh 文件路径 只运行脚本中的代码
bash 文件路径 只运行脚本中的代码,选项 -x 表示详细运行时显示详细的步骤
source 文件路径 全量执行脚本中的代码,例:cd /root,脚本执行完成之后会进入/root目录
./文件名 只运行脚本中的代码,但是脚本需要执行权限,使用chmod命令进行赋权

二、重定向输入

1. 命令

echo 输入的内容 符号 目标文件

符号 作用 示例
< 文件名 将文件中的内容输出 更改用户user1密码:
passwd --stdin user1 < passwd.txt
输入的内容 > 文件名 将输入的内容输入到目标文件(覆盖) 这是test1文件 > test1.txt
输入的内容 >> 文件名 将输入的内容输入到目标文件(追加) 这是追加的内容 >> test1.txt
&> 文件名 将错误信息和标准信息重定向到文件中(覆盖) 将输出的冗余信息重定向到黑洞文件:
yum mkcache &> /dev/null
2> 文件名 将错误信息重定向到文件中(覆盖,2>> 追加) yum mkcache &> /dev/null

2. 实例

(1)将"123"重定向到passwd.txt文件

echo 123 > passwd.txt

(2)将"456"重定向追加到passwd.txt文件

echo 456 >> passwd.txt 

(3)用"123123"将passwd文件内容覆盖

echo 123123 > passwd.txt

(4)将passwd.txt文件中的内容设置为用户user1的密码

systemctl stop firewalld.service
setenforce 0
passwd --stdin user1 < passwd.txt 

(5)将 ls / 的输出结果重定向到黑洞文件/dev/null 和 test1.txt文件

ls / &> /dev/null
ls / &> test1.txt

三、变量

1. 基本概念

作用:

用于赋值、传参

使用:

打印和引用变量时变量 $ 符号,$a

定义:

不能使用命令和中文作为变量名

变量名不能用特殊字符字符开头

2. 变量类型

(1)基本变量

整数型:int

字符串:string

布尔:真 非0 假 0

(2)自定义变量

可以自己定义、修改、使用

a=1
b=2

echo $a $b
1 2

a=2

echo $a $b
2 2

(3)只读变量

不可以重新赋值,不可删除,重启消失

readnonly 变量名

a=1
readnoly a

(4)全局变量

所有用户自定义变量在子shell中也可以使用

临时:

export 变量名

b=1
export b

永久:

优点

① 全局范围

② 持久性

③ 共享性

副作用

① 如果相同的变量名,直接引用会导致不确定性

②在脚本中一般使用自定义变量

进入 /etc/profile 配置文件进行添加

export 变量名=值

echo "export b=1" >> /etc/porfile
source /etc/passwd

(5)位置变量

命令行参数,运行脚本程序时,传递它们的值,位置变量用于将外部数据传递给脚本执行

传参:从左到右

脚本名 参数1 参数2 参数3 ……

vim test1.sh

#!/bin/bash
sum=$[$1 + $2]
echo $sum

sh test1.sh 1 2
3


(6)预定义变量

变量名 作用
$# 显示传参个数
$? 判断真假(0为真,1为假),判断上一行的命令是否执行成功(成功0,不成功非0)
$* 将出输内容分行处理(加引号" "表示不分行)
$@ 将输出内容分行处理(无论加不加引号" "都分行)
$0 表示当前脚本或命令
$1 $2 $3 表示脚本或命令的参数
$HOME 返回当前登录用户家目录
$PWD 返回当前所在位置
$USER 返回当前登录用户
$SHELL 返回当前正在使用的shell程序路径
$PATH 返回可执行文件所在目录

3. read 输入赋值

命令 作用
read -p "提示符" 变量名 指定提示符,用于提示用户输入信息
read -a 变量名 将输入的内容存储到数组中(每个值之间用空格隔开),调用第一个值 ${变量名[0]},调用第二个值 ${变量名[1]}

(1)用read -p 写一个两个数任意相加求和的脚本

vim test2.sh

#!/bin/bash

# 两个数任意相加求和

read -p "请输入第一个数:" num1
read -p "请输入第二个数:" num2

sum=$(($num1+$num2))

echo "$num1 与 $num2 的和为 $sum"


sh test2.sh 
请输入第一个数:1
请输入第二个数:2
1 与 2 的和为 3

(2)用read -a 写一个两个数任意相加求和的脚本

vim test3.sh

#!/bin/bash

# 两个任意的数相加求和


echo "请输入两个数字(用空格隔开):"
read -a  num

sum=$[${num[0]}+${num[1]}]

echo "合为:$sum"


sh test3.sh 
请输入两个数字(用空格隔开):
1 2
合为:3

四、运算

1. 运算法则

运算符(只支持整数) 含义
+
-
\*
/
% 取余
i++ 先赋值再自增1
i-- 先赋值再自减1
++i 先自增1再赋值
--i 先自减1再赋值

2. 整数运算

$(($a+$b))

$[$a+$b]

let c=$a+$b

vim test4.sh

#!/bin/bash

# 三种运算方法

a=1
b=2


# 第一种

c1=$(($a+$b))
echo "第一种合为:$c1"


# 第二种

c2=$[$a+$b]
echo "第二种合为:$c2"


# 第三种

let c3=$a+$b
echo "第三种合为:$c3"


sh test4.sh 
第一种合为:3
第二种合为:3
第三种合为:3

五、环境变量

将有执行权限的脚本添加到环境变量,可以在任意目录下输入脚本名,即可运行脚本

命令 env 可以查看当前所有环境变量

将 /data/revise 目录下的脚本test4.sh添加到环境变量

ll test4.sh 
-rw-r--r--. 1 root root 216 6月   5 22:29 test4.sh

chmod 777 test4.sh 
ll test4.sh
-rwxrwxrwx. 1 root root 216 6月   5 22:29 test4.sh

pwd
/data/revise

echo "expot PATH=$PATH:/data/revise" ~/.bashrc
expot PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/data/revise/test4.sh:/data/revise/test4.sh:/data/revise/test4.sh:/data/revise/test4.sh:/data/revise:/data/revise /root/.bashrc

source ~/.bashrc

env | grep "test4.sh"
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/data/revise/test4.sh:/data/revise/test4.sh:/data/revise/test4.sh:/data/revise/test4.sh:/data/revise

test4.sh 
第一种合为:3
第二种合为:3
第三种合为:3

六、条件判断

1. 测试命令

test [条件表达式]

条件表达式 作用
-e 测试目录是否存在
-d 测试是否为目录
-f 测试是否为文件
-r 测试文件是否可读
-w 测试文件是否可写
-x 测试文件是否可执行
-z 测试字符串是否为空
-n 测试字符串是否存在

2. 比较符

字符(只支持整数) 符号(既支持整数也支持较字符串) 含义
-eq == 等于
-ne != 不等于
-lt < 小于
-le <= 小于等于
-gt > 大于
-ge >= 大于等于

3. 逻辑运算符

[ 表达式1 ] 操作符 [ 表达式2 ]

[[ 表达式1 操作符 表达式2 ]]

字母操作符 符号操作符 含义 成立条件
-a && 前后两个条件都要成立
-o || 前后两个条件只需成立一个
! 前后两个条件都不成立

七、判断

1. if ... fi

如果……就……

格式

if [[ 条件表达式 ]]

then

​ 条件成立执行语句

fi

aa=1

if [[ $aa -eq 1 ]]
> then
> echo "aa的值为1"
> fi

aa的值为1

2. if ... else ...fi

如果……就……否则……

格式

if [[ 条件表达式 ]]

then

​ 条件成立执行语句

else

​ 条件不成立执行语句

fi

aa=2

if [[ $aa == 1 ]]
> then
> echo "aa的值为1"
> else
> echo "aa的值不为1"
> fi

aa的值不为1

3. if ... elif ... else ... fi

如果……就……除此之外……就……否则……

格式

if [[ 条件1表达式 ]]

then

​ 条件1成立执行语句

elif [[ 条件2表达式 ]]

then

​ 条件2成立表达式

else

​ 条件不成立执行语句

fi

aa=2

if [[ $aa -eq 1 ]]
> then
> echo "aa的值为1"
> elif [[ $aa == 2 ]]
> then
> echo "aa的值为2"
> else
> echo "aa的值即不为1也不为2"
> fi

aa的值为2

4. case

格式

case 变量(标签) in

变量值1)

​ 变量值1成立执行语句

​ ;;

变量值2)

​ 变量值2成立执行语句

​ ;;

变量值3)

​ 变量值3成立执行语句

​ ;;

*)

​ 以上变量值未成立执行语句

esac

aa=3

case $aa in
> 1)
> echo "aa的值为1"
> ;;
> 2)
> echo "aa的值为2"
> ;;
> 3)
> echo "aa的值为3"
> ;;
> *)
> echo "aa的是不是1 2 3"
> esac

aa的值为3

5. 实例

(1) 检查用户家目录中的 test.sh 文件是否存在,并且检查是否有执行权限

[root@localhost data]# vim test1.sh 

#!/bin/bash

# 检查用户家目录中的 test.sh 文件是否存在,并且检查是否有执行权限

test -f ~/test.sh

if [ $? == 0 ]
then
        echo "家目录的test.sh存在"

        test -x ~/test.sh
        if [ $? == 0 ]
        then
                echo "并且拥有执行权限"
        else
                echo "无执行权限"
        fi

else
        echo "家目录中无test.sh文件"

fi


(2) 对成绩进行判断并分组

[root@localhost data]# vim test2.sh 

#!/bin/bash

# 提示用户输入100米赛跑的秒数,要求判断秒数大于0且小于等于10秒的进入选拔赛,
# 大于10秒的都淘汰,如果输入其它字符则提示重新输入;
# 进入选拔赛的成员再进一步判断男女性别,
#男生进男生组,女生进女生组,如果输入错误请提示错误

read -p "请输入姓名:" name


read -p "请输入您的性别:" sex
if [ $sex == "男" ]
then
        group="男生组"
elif [ $sex == "女" ]
then
        group="女生组"
else
        echo "输入错误,请重新输入!"
        exit
fi


read -p "输入100米赛跑的成绩:" num
if [[ $num -gt 0 && $num -le 10 ]]
then
        echo "恭喜$name 已成功进入选拔赛!"
        echo "所在组为:$group"

elif [ $num > 10 ]
then
        echo "很遗憾,$name 没有进入选拔赛!"
else
        echo "输入错误,请重新输入!"
fi

(3)用case语句解压根据后缀名为 .tar.gz 或 .tar.bz2 的压缩包到 /opt 目录

[root@localhost data]# vim test3.sh 

#!/bin/bash

# 用case语句解压根据后缀名为 .tar.gz 或 .tar.bz2 的压缩包到 /opt 目录

read -p "输入文件路径:" a

case $a in
*.tar.gz)
        tar -zxf $a -C /opt
        echo "$a 文件已成功解压到 /opt 目录下"
;;
*.tar.bz2)
        tar -jxf $a -C /opt
        echo "$a 文件已成功解压到 /opt 目录下"
;;
*)
        echo "$a 文件不存在!"
esac


(4)判断是否为整数并判断奇偶性

[root@localhost data]# vim test4.sh 

#!/bin/bash

# 提示用户输入内容,使用if 语句判断输入的内容是否为整数。
# 若是整数判断是奇数还是偶数。

read -p "输入一个数:" num

if [ $num -eq $num ] &> /dev/null
then
        echo "$num 是一个整数"

        let i=$num%2
        if [ $i == 0 ]
        then
                echo "$num 是偶数"
        else
                echo "$num 是奇数"
        fi

else
        echo "$num 是不是整数"
fi

(5)用if 语句判断主机是否存活

[root@localhost data]# vim test6.sh 

#!/bin/bash

# 用if 语句判断主机是否存活

read -p "输入需要测试的主机ip地址:" ip

ping -c 5 -w 5 $ip &> /dev/null


if [ $? -eq 0 ]
then
        echo "主机 $ip 为存活状态"
else

        echo "主机 $ip 为死亡状态"
fi




(6)管理防火墙

[root@localhost data]# vim test7.sh 

echo "4.status"

read -p "请输入控制号:" num

case \$num in

1)
        systemctl start firewalld.service
        echo "firewalld 已成功开启!"
;;

2)
        systemctl stop firewalld.service
        echo "firewalld 已成功关闭!"
;;

3)
        systemctl restart firewalld.service
        echo "firewalld 已成功重启!"
;;

4)
        systemctl status firewalld.service
        echo "firewalld 状态已成功显示!"
;;

*)

        echo "输入错误,请重新输入!"

esac

" > /etc/init.d/firewalld.sh

八、循环

1. for 循环

格式

(1)bash 格式

for 变量名 in {范围}

do

​ 循环体(循环语句)

done

(2)类 C 格式

for ((变量赋值;变量范围控制;变量变化))

do

​ 循环体(循环语句)

done

# bash 格式
for i in {1..5}
> do
> echo "i的值为:$i"
> done

i的值为:1
i的值为:2
i的值为:3
i的值为:4
i的值为:5
# 类C格式
for ((i=1;i<=5;i++))
> do
> echo "i的值为:$i"
> done

i的值为:1
i的值为:2
i的值为:3
i的值为:4
i的值为:5

2. whlie 循环

格式

while [[ 循环条件 ]]

do

​ 循环条件成立执行语句

done

i=0

while [[ i -lt 5 ]]
> do
> i=$[$i+1]
> echo "这是第 $i 次循环"
> done

这是第 1 次循环
这是第 2 次循环
这是第 3 次循环
这是第 4 次循环
这是第 5 次循环

3. 死循环

(1)for 死循环

格式

for (;; )

do

​ 循环体(循环语句)

done

(2)while 死循环

常用

格式

while true

do

​ 循环体(循环语句)

done

4. 循环控制

(1)终止 break

满足条件运行break终止循环

用法

for 变量名 in {范围}

do

​ if [[ 结束循环条件 ]]

​ then

​ break

​ fi

​ 循环体(循环语句)

done

# 当i=3是终止循环
for i in {1..5}
> do
> echo "i的值为:$i"
> if [[ $i -eq 3 ]]
> then
> break
> fi
> done

i的值为:1
i的值为:2
i的值为:3

(2)继续 continue

满足条件循行continue不执行(跳过)此次循环,继续下一次循环

用法

for 变量名 in {范围}

do

​ if [[ 结束循环条件 ]]

​ then

​ continue

​ fi

​ 循环体(循环语句)

done

# 当i=3时跳过此次循环,继续执行下一次循环
for i in {1..5}
> do
> if [[ $i -eq 3 ]]
> then
> continue
> fi
> echo "i的值为:$i"
> done

i的值为:1
i的值为:2
i的值为:4
i的值为:5

5. 实例

(1)计算从1到100所有整数的和

img

img

(2)提示用户输入一个小于100的整数,并计算从1到该数之间所有整数的和

img

img

(3)求从1到100所有整数的偶数和、奇数和

img

img

(4) 用户名存放在users.txt文件中,每行一个,判断文件里的用户是否存在,若该用户存在,输出提示该用户已存在;用户存在但没设密码,则提示用户并让用户设置码;若该用户不存在,提示用户输入密码,建立用户并设立其密码

img

img

(5)检测指定范围主机是否通信,并将通信的主机ip输出到文件host_ip中

img

(6) 用户输入密码,脚本判断密码是否正确,正确密码为123456,输入正确提示正确信息,连续输错3次则报警

img

img

(7)使用循环语句将一个 0到255 之间的十进制数转换成8位数二进制数

img

img

……

……

……

img

九、函数

1. 定义函数

格式

函数名(){

​ 函数体(函数要运行的语句)

}

2. 调用函数

格式

函数名 [参数1] [参数2] ……

vim test5.sh

#!/bin/bash

test(){

        a=1
        b=2
        echo "$[$a+$b]"

}

test


sh test5.sh 
3
# 函数传参
vim test5.sh

#!/bin/bash

test(){
        
        echo "$[$1+$2]"

}

test 1 2

sh test5.sh 
3


# 在另一个文件中调用函数
vim test6.sh
#!/bin/bash
. /test5.sh

test 1 2

3. 实例

① 函数能够接受一个参数,参数为用户名

判断一个用户是否存在 如果存在,就返回此用户的shell 和 UID

并返回正常状态值

如果不存在,就说此用户不存在;并返回错误状态值

② 在主程序中调用函数



十、数组

1. 介绍

作用:一次性定义多个变量,充当数据存储的库

类型:数值类型、字符类型

数值型

arr=(1 2 3)

字符型

arr=(a b c)

2. 定义数组

定义普通数组:(索引从0开始)

arr=()

declare -a arr

定义关联数组:(自定义索引)

declare -A arr

3. 定义索引

普通索引:

arr=(a b c d)

索引从0开始,0 1 2 3

自定义索引:

arr=([aa]=a [bb]=b [cc]=c [dd]=d)

索引为:aa bb cc dd

4. 引用数组

(1)获取数组的值

${arr[@]}

${arr[*]}

(2)获取数组的索引

${!arr[@]}

${!arr[*]}

(3)获取数组指定索引的值

${arr["索引"]}

(4)获取数组指定索引范围的值

获取数组arr索引为1-3的值

${arr[@]:1:3}

(5)索取数组长度

${#arr[@]}

${#arr[*]}

(6)临时更改数组的值

${arr[*]/被更改值/新值}

将数组arr中的a值改为aa

${arr[*]/a/aa}

(7)永久更改

arr["已有索引"]=新值

将数组arr索引为0的值改为a

arr[0]=a

(注:若数组中没有该索引,则会在此索引处创建一个该索引所对应的值)

arr=${arr[@]/a/aa}

(8)添加值

普通索引:

arr+=(a b c)

arr[${#arr[@]}]=(a b c)

自定义索引:

arr+=([aa]=a [bb]=b [cc]=c)

5、实例