1.shell概叙
本文将从shell执行、语法、实战三个方面来讲解shell编程,其实shell编程就是个批处理,将你平时在服务器上单独执行的命令,按照一定要求组织起来,写在一起,然后统一执行,就完事了。
对于运维人员来说,收集一下服务器信息、写个巡检报告、静默安装oracle、批量安装jdk、nginx、redis、mysql啥的还是很有用的。
开发人员掌握一些shell编程(java进程启动脚本、maven打包编译脚本、docker服务编排脚本等等),也便于在linux服务器上处理问题。
只要你能静下来,把本文看完,并将里面的示例敲一遍,执行一遍,可以百分百保证你已经学会了shell编程。话不多说,直接开干。
1.1 什么是shell?
shell是一个用C 语言编写的程序,它是用户使用Liunx的桥梁。shell既是一种命令语言,又是一种程序设计语言。shell是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问Liunx内核服务。
在计算机科学中,Shell俗称壳(用来区别于核),是指“为使用者提供操作界面”的软件(command interpreter,命令解析器)。它类似于DOS下的COMMAND.COM和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。
图形界面shell 和命令行式shell
shell从操作界面来分,可以分成:图形界面shell 、命令行式shell。
一:图形界面shell (Graphical User Interface shell 即 GUI shell) 例如:应用最为广泛的Windows Explorer(微软的Windows系列操作系统),还有也包括广为人知的Linux shell,其中Linux shell包括X Window Manager(BlackBox和FluxBox),以及功能更强大的CDE、GNOME、KDE、 Xfce。
二:命令行式shell (Command Line Interface shell ,即CLI shell)。命令式shell就代表了shell,也是我们平常口中的shell,所以本文的重点就是讲命令行式shell。
交互式shell和非交互式shell
交互式模式就是shell等待用户的输入,并且执行用户提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、签退。当用户签退后,shell也终止了。
shell也可以运行在另外一种模式:非交互式模式。在这种模式下,shell不与用户进行交互,而是读取存放在文件中的命令,并且执行它们。当它读到文件的结尾,shell也就终止了。
sh、ksh、bash
shell从解释器来分:sh、ksh、bash、csh等等。将在1.3节中再重点讲。
1.2什么是shell脚本?
shell脚本(shell script) ,是一种为shell编写的脚本程序,一般后缀名为 .sh
业界所说的shell 通常都是指shell脚本,但shell 和 shell script 是两个不同的概念。
shell脚本文件命名格式:*.sh 后缀一般是".sh",当然也可以不写,比如:MySQL的mysqld_safe就没有带后缀。
shell脚本文件执行方式:
sh -n /path/to/some-script : 只检测脚本中的语法错误,但无 法检查出命令错误,但不真正执行脚本
sh -x /path/to/some_script :调试并执行
bash -n /path/to/some-script : 只检测脚本中的语法错误,但无 法检查出命令错误,但不真正执行脚本
bash -x /path/to/some_script :调试并执行
直接加执行权限运行:chmod +x
直接执行网上的脚本: curl http:\\sdss 2>/dev/null | bash
shell脚本文件内容格式:
包括 文件头、注释、 变量、命令、 流程控制、函数等6部分。
-
文件头:Shell脚本通常以一行特殊的注释作为文件头,称为Shebang行。Shebang行用于告诉操作系统该脚本应该用哪种解释器执行,通常写为
#!/bin/sh
或#!/bin/bash
,表示使用sh或bash解释器。 -
注释:Shell脚本支持两种注释方式,分别是单行注释和多行注释。单行注释以
#
开头,直到行末结束。多行注释使用:<<
开始,接着是注释内容,最后以EOF
结束。 -
变量:变量是Shell脚本中的基本数据类型,可以用于存储数字、字符串等信息。Shell脚本中的变量名通常是大写字母,变量值可以使用等号
=
进行赋值。在Shell脚本中,可以使用$
符号引用变量的值。 -
命令:在Shell脚本中,命令可以是Shell命令或其他可执行程序。命令可以通过直接调用或使用变量调用。变量调用的语法为
$(...)
。 -
流程控制语句:Shell脚本支持多种流程控制语句,例如if语句、for循环语句和while循环语句等。
-
函数:即自定义函数,和C、Java中的函数定义一样,就是对重复、可复用功能的一种封装。
shell脚本示例如下:
#!/bin/bash #文件头
source /etc/profile #命令 引入环境变量
# This is a comment #注释
:<<EOF #多行注释
This is a multi-line comment.
EOF
# Define a variable
CURRENT_DATE=$(date) #变量 定义本地变量
# Print the current date
echo "The current date is: $CURRENT_DATE" #命令 打印当前时间
MY_VAR = "Hello World" #流程控制
if [ $MY_VAR = "Hello World" ]; then
echo "The variable is equal to Hello World"
else
echo "The variable is not equal to Hello World"
fi
#函数 定义函数
function my_function() {
echo "This is my function"
}
my_function #执行函数
shell脚本格式规范:
一、脚本基本格式
1、首行为一些命令或声明
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
#!/usr/bin/ruby
#!/usr/bin/lua
2、脚本注释规范
第一行一般为调用
程序名
版本号
更改后的时间
作者相关信息
该程序的作用
简要说明
一、注释
头部注释
#!/bin/bash
# vim:sw=4:ts=4:et
<<INFO
SCRIPYT:test.sh
AUTHOR:anqixiang<邮箱号>
DATE:2021-09-12
DESCRIBE:描述脚本主要功能
SYSTEM:适配哪些操作系统
WARNING:警告信息
VERSION:1.1.0<可选>
MODIFY:记录修改信息,方便查看和维护
INFO
单行注释与多行注释
单行注释以#号开头,如
#修改IP地址
多行注释表示方法(INFO可以用别的标识代替,但需与结尾保持一致)
<<INFO
SCRIPYT:test.sh
AUTHOR:anqixiang<邮箱号>
DATE:2021-09-12
INFO
二、排版规范
1.程序块采用缩进,缩进为4个空格
修改vim中Tab键的距离
举例
while true
do
sleep 5
done
2.函数
函数功能注释
所有的函数注释应该包含:
函数的描述
全局变量的使用和修改
使用的参数说明
返回值,而不是上一条命令运行后默认的退出状态
示例:
#######################################
# Cleanup files from the backup dir
# Globals:
# BACKUP_DIR
# BACKUP_SID
# Arguments:
# None
# Returns:
# None
#######################################
cleanup() {
...
}
函数编写
函数首字母大写,并用“_”隔开,如Modify_Ip
函数名后面必须加小括号()
第一个大括号与小括号之间保留一个空格
第二个大括号顶格单独占一行
同级别的代码块要左对齐
举例
Modify_Ip() {
command1
command2
if 表达式;then
command 3
else
command 4
fi
}
3.命令较长需分行书写,在低优先级操作符处划分新行,用'\'标识
command 1 | command 2 \
&& command 3 \
|| command 4
4.一行只写一条语句
command 1;command 2 #不推荐
command 1 #推荐
command 2
5.逻辑运算符&&、||和重定向、管道符前后要留空格
command 1 && command 2
command 1 | command 2
# 长命令管道换行连接,管道放置于下一个命令开头,缩进4个空格
command1 \
| command2 \
| command3 \
| command4
6.一个函数只完成一个功能,且不能超过100行
7.case语句格式
case $value in
val1)
command 1
;;
a|b)
command 2
;;
*)
command 3
esac
8.注释与上面的代码用空行隔开
command 1
[空一行]
#注释内容
command 2
9.循环和判断
for循环
for i in 1..3
do
if [[ $i -eq 1 ]];then
echo "等于1"
elif [[ $i -eq 2 ]];then
echo "等于2"
else
echo "等于3"
fi
done
while
while read line
do
...
done < file.txt
或者
command | while read line
do
...
done
三、变量规范
1.变量名由字母、数字、下划线组成, 只能以字母、下划线开头
2.尽量减少全局变量,可在变量前面加local使其成为局部变量
local name=""
for name in a b
do
echo ${name}
done
全局变量仅在当前Shell有效,使用export定义的全局变量在所有子进程中依然有效
3.环境变量和全局变量大写,局部变量小写(使用下划线连接,如host_ip )
readonly PATH_TO_FILES='/some/path' #不能修改的变量添加readonly 属性
declare -xr BACKUP_SID='PROD'
declare解释
功能介绍:声明变量的属性,如果使用declare,后面没有任何参数,那么bash就会主动将所有变量名与内容都调出来,just as set.
语 法:declare [-aixr] variable
参数说明:
-a :将后面的variable定义为数组
-i :将后面的variavle定义为整数数字
-x :用法与export一样,就是将后面的variable变成环境变量
-r :将一个variable的亦是设置成只读,读变量不可更改内容,也不能unset
4.引用变量用\${value}
val=${value}
[[ ${value} == "test" ]] && command 1
5.不能被清除和修改的变量通过readonly variable声明
6.常用变量集中写在脚本开头,便于修改
7.对变量赋空值时,建议使用unset
unset sum
8.用ln创建软链接文件,必须先判断文件是否存在,存在时必须先删除,然后再创建软链接
[ -h /data ] && rm -rf /data
ln -sf /home/data /data
9.命令替换推荐使用$(),并用双引号括起来,在groovy中建议使用``(反撇号)
local_ip="$(ip addr | grep ......)"
四、安全清理
1.环境变量和history不能有敏感信息
五、建议
判断推荐使用[[ ]]
判断命令是否存在用command -v代替which(command是内部命令,系统自带;如果命令不存在不会输出任何信息)
路径尽量保持绝对路径,不易出错,如果非要用相对路径,最好用./修饰
简单的if尽量使用&& ||,写成单行。比如[[ x > 2]] && echo x
使用rm删除目录的时候建议先cd到父目录,再删除子目录
1.3什么是shell解释器?
前面虽然两次提到了#! ,但是本着重要的事情说三遍的精神,
这里再强调一遍:在 shell 脚本,#! 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 解释器。#! 被称作shebang(也称为 Hashbang )。
#! 决定了脚本可以像一个独立的可执行文件一样执行,而不用在终端之前输入sh, bash, python, php等。
# 以下两种方式都可以指定 shell 解释器为 bash,第二种方式更好
#!/bin/bash
#!/usr/bin/env bash ## 推荐
各主要操作系统下缺省的shell:
- AIX下是Korn Shell。
- Solaris缺省的是Bourne shell。
- FreeBSD缺省的是C shell。
- HP-UX缺省的是POSIX shell。
- Linux是Bourne Again shell。
十五年或者二十年前,一些服务器监控系统,为了获取服务器的资源使用情况,统一用sh解释器来执行和编写服务器信息收集脚本,因为sh可以在bash、ksh、csh下面都能运行,而bash、ksh、csh之间有兼容性问题,写出来的脚本不一定能通用。
2.shell变量
1、变量类型
内置变量:PS1,PATH, UID HOSTNAME $$ BASHPID PPID $? HISTSIZE
2、变量数据类型:
字符
数值:整型、浮点型,bash不支持浮点数
3、变量全名法则
区分大小写、不能使用程序中的保留字和内置变量,如:if ,for;
只能使用数字、字母及下划线,且不能以数字开头,
4、变量定义
普通变量:生效范围为当前SHELL进程,对当前SHELL之外的其它SHELL进程,均无效
环境变量:生效范围为当前SHELL进程及其子进程
本地变量:生效范围为当前SHELL进程中某代码片断,通用指函数
变量赋值:name='value',,,赋值为临时生产,退出后变量自动删除;脚本中的变量会随着脚本结束而删除
直接字串:name='root'
变量引用:name="$USER"
命令引用:name=`COMMAND` 或者 name=$(COMMAND)
5、引用
弱引用:"$name" 其中的变量引用会被替换为变量值
强引用:'$name' 其中的变更引用不会被替换为变量值,而保持原字符串
追加:NAME +=:DJY
6、变量显示和删除
set 显示所有变量
unset <name> 删除变量
7、环境变量
可以使子进程,继承父进程的变量,但是无法让父进程使用子进程的变量
一旦子进程修改从父进程继承的变量,将会新的值传递给孙子进程
一般只在系统配置文件中使用,在脚本中较少使用
显示所有环境变量:env printenv export declare -x
8、bash内建的 环境变量
PATH SHELL USER UID HOME PWD SHLVL LANG MAIL HOSTNAME HISTSIZE
9、只读变量
readonly name declare -r name
10、位置变量
在bash shell中内置的变量,在脚本代码中调用通过命令行传递给脚本的参数
清空所有位置变量:set --
11、退出状态码变量:
$?的值为0 代表成功 ; $ ? 取值范围 0-255
?的值为1-255 代表失败
自定义退出状态码: exit [n]
12、展开命令行
13、脚本安全和SET
set命令:可以用来定制shell环境
3.格式化输出 printf
.
1、常用格式替换符
2、常用转义字符
3、格式化输出示例
# 单引号
printf '%d %s\n' 1 "abc"
# Output:1 abc
# 双引号
printf "%d %s\n" 1 "abc"
# Output:1 abc
# 无引号
printf %s abcdef
# Output: abcdef(并不会换行)
# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出
printf "%s\n" abc def
# Output:
# abc
# def
printf "%s %s %s\n" a b c d e f g h i j
# Output:
# a b c
# d e f
# g h i
# j
# 如果没有参数,那么 %s 用 NULL 代替,%d 用 0 代替
printf "%s and %d \n"
# Output:
# and 0
# 格式化输出
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
# Output:
# 姓名 性别 体重kg
# 郭靖 男 66.12
# 杨过 男 48.65
# 郭芙 女 47.99
4.shell运算符
一、算术运算
SHELL允许在某些情况下对算术表达式进行求值; bash 只支持整数,不支持小数
1、实现算术运算
2、内建的随机数生成器变量
$RANDOM 取值范围:0-32767
3、增强型赋值:let varOPERvalue
二、逻辑运算
与:& 或:| 非:! 异或:^ 相同为假,不同为真
1、 短路运算
短路与 && CMD1 && CMD2 当1为真时,再计算2值; 当1为假时,结果为0,不再执行2
短路或 ||
短路与 和短路或 组合 : CMD1 && CMD2 || CMD3 先执行与再执行或,反过来不使用。
三、条件测试命令
1、条件测试命令格式
test EXPRESSION
[EXPRESSION] 和test等价,建议使用【】
[[EXPRESSION]] 相当于增强版的【】,支持【】的用法 ,且支持扩展正则表达式和通配符
2、变量测试
【 -v NAME】 判断NAME变量是否定义
3、数值测试
-eq 是否等于 ==
-ne 是否不等于 !=
-gt 是否大于 >
-ge 是否大于等于 >=
-lt 是否小于 <
-le 是否小于等于 <=
4、字符串测试
5、文件测试
四、关于()和{}
(CMD1; CMD2;...)和{CMD1;CMD2; ....;}都可以将多个命令组合在一起,批量执行
五、组合测试条件
1、第一种方式
2、第二种方式
六、使用read命令来接受输入
使用read来把输入值分配给一个或多个shell变量,read从标准输入中读取值,给每个单词分配一个变量,所有剩余单词都被分配给最后一个变量,如果变量名没有指定,默认标准输入的值赋值给系统内置变量REPLY
格式: read [options] [name...]
常见选项:
-p 指定要显示的提示 -s 静默输入, 一般用于密码 -n N 指定输入的字符长度N -d '字符' 输入结束符 -t N TIMEOUT为N秒
5.Shell流程控制
Shell流程控制是Shell脚本编程中的重要组成部分,它允许我们根据条件、循环或其他逻辑结构来控制脚本的执行流程。
通过掌握条件语句、循环语句、选择语句和其他控制语句的用法,我们可以编写出更加智能、灵活、高效、可靠的自动化脚本,以满足各种复杂的自动化需求。
在实际应用中,我们可以根据具体的任务需求选择合适的流程控制结构,并结合Shell的其他功能(如变量、函数、管道等)来实现更加复杂的自动化任务。
1、条件语句:
条件语句根据指定的条件来决定脚本的执行路径。在Shell脚本中,常用的条件语句有if、elif和else。这些语句允许我们根据条件表达式的值来执行不同的命令或命令组。
-
-
if语句:当条件为真时执行相应的命令或命令组。
-
elif语句:在if语句中,当第一个条件不满足时,可以使用elif来指定另一个条件进行判断。
-
else语句:在if或elif条件都不满足时,执行else块中的命令或命令组。
-
2、循环语句:
循环语句允许我们重复执行某个命令或命令组,直到满足某个条件为止。Shell脚本中常用的循环语句有for、while和until。
-
-
for循环:遍历一个列表或序列中的元素,并对每个元素执行相应的命令或命令组。
-
while循环:当条件为真时,重复执行命令或命令组。
-
until循环:与while循环相反,当条件为假时重复执行命令或命令组。
-
3、选择语句:
-
-
case语句:用于基于多个可能的值进行条件选择。
-
4、其他控制语句:
除了条件语句、选择语句和循环语句外,Shell还提供了其他一些控制语句,如break(跳出循环)、continue(跳过当前循环迭代)、exit(退出脚本)等。这些语句用于更精细地控制脚本的执行流程。
if语句
1、基本语法:
if [ 条件表达式 ]
then
# 当条件为真时执行的命令
elif [ 另一个条件表达式 ]
then
# 当另一个条件为真时执行的命令
else
# 当所有条件都不为真时执行的命令(可选)
fi
2、注意事项:
-
-
[ 条件表达式 ] 中的空格是必需的。方括号与条件表达式之间以及方括号内部的操作符和变量之间都需要有空格。
-
条件表达式通常使用测试命令(如 [ -f file ] 检查文件是否存在)或比较操作符(如 [ $a -eq $b ] 比较两个数字是否相等)。
-
then、elif、else 和 fi 是 if 语句的关键字,用于定义条件块。
-
3、示例:
声明一个名为AGE的变量,根据这个变量的值来输出不同的消息。
#!/bin/bash
AGE=25
if [ $AGE -lt 18 ]
then
echo "You are a teenager."
elif [ $AGE -ge 18 ] && [ $AGE -lt 65 ]
then
echo "You are an adult."
else
echo "You are a senior citizen."
fi
# 输出:You are an adult.
在这个示例中,使用了两个条件表达式:$AGE -lt 18(检查年龄是否小于18)和 $AGE -ge 18 && $AGE -lt 65(检查年龄是否在18到65岁之间)。根据AGE变量的值,脚本会输出相应的消息。
在比较操作符(如-lt、-ge)和变量之间必须添加空格。还使用了逻辑操作符&& 来组合两个条件表达式。
for循环
在Shell脚本中,for循环是一种常用的控制结构,用于迭代一系列的值或元素。Shell脚本中的for循环有几种不同的形式,但最常见的是基于列表或数组的循环,以及基于范围的循环。
1、基于列表或数组的循环
-
-
使用空格分隔的值列表
-
for element in value1 value2 value3 do echo "$element" done # 输出 value1 value2 value3
-
-
-
使用数组(注意:不是所有的Shell都支持数组)
-
-
array=("value1" "value2" "value3") for element in "${array[@]}" do echo "$element" done # 输出 value1 value2 value3
-
-
从文件中读取每一行
-
for line in $(cat filename.txt)
do
echo "$line"
done
-
-
使用通配符迭代文件或目录
-
for file in /path/to/dir/*.txt
do
echo "$file"
done
2、基于范围的循环
在Bash中,可以使用seq命令或花括号扩展。
-
-
使用seq命令
-
for i in $(seq 1 5)
do
echo "$i"
done
# 输出:
1
2
3
4
5
-
-
使用花括号扩展(Bash 4+)
-
for i in {1..5}
do
echo "$i"
done
# 输出:
1
2
3
4
5
3、遍历命令的输出
-
-
遍历ls命令的输出(注意:不要在生产环境中对ls的输出进行解析)
-
for file in $(ls /path/to/dir/)
do
echo "$file"
done
-
-
更安全的方法是使用find命令与-print0和read -d $'\0'组合
-
find /path/to/dir/ -type f -print0 | while IFS= read -r -d $'\0' file; do
echo "$file"
done
注意:当处理文件名或路径时,特别是当它们可能包含空格、换行符或其他特殊字符时,使用$(command)或命令替换可能会产生问题。在这种情况下,使用find命令与-print0和read -d $'\0'组合是更安全的选择。
while循环
在Shell脚本中,while循环用于在条件为真时重复执行一系列命令。当条件变为假时,循环停止执行。
1、基本语法
while [ 条件表达式 ]
do
# 当条件为真时执行的命令
done
2、示例
-
-
使用简单的条件检查
-
#!/bin/bash
counter=1
while [ $counter -le 5 ]
do
echo "这是循环的第 $counter 次迭代"
((counter++))
done
# 输出:
这是循环的第 1 次迭代
这是循环的第 2 次迭代
这是循环的第 3 次迭代
这是循环的第 4 次迭代
这是循环的第 5 次迭代
在这个例子中,初始化了一个变量counter,并使用while循环检查该变量的值是否小于或等于5。在每次循环迭代中,我们都打印出一条消息并增加counter的值。当counter的值大于5时,条件不再为真,循环停止。
-
-
读取文件内容
-
#!/bin/bash
while IFS= read -r line
do
echo "$line"
done < filename.txt
在这个例子中,我们使用read命令从文件filename.txt中逐行读取内容,并将每一行存储在变量line中。IFS=用于防止文件名中的空格或特殊字符被错误地解释为字段分隔符。然后,我们打印出每一行的内容。
-
-
无限循环(需要手动退出)
-
有时,你可能想要创建一个无限循环,直到用户手动退出。这可以通过在循环条件中使用true来实现,因为true始终返回真。
#!/bin/bash
while true
do
echo "这是一个无限循环,按Ctrl+C退出"
sleep 1 # 暂停1秒,以避免过快地输出
done
在这个例子中,我们使用true作为while循环的条件,因此循环将无限期地继续执行。我们使用sleep 1命令在每次迭代之间暂停1秒,以避免过快地输出消息。要退出这个无限循环,你可以按Ctrl+C。
3、注意事项
-
-
确保在while循环中正确地更新条件表达式中使用的变量,以避免无限循环。
-
当从文件中读取内容时,使用read命令的-r选项可以确保按原样读取行,而不会解释反斜杠字符为转义字符。
-
使用IFS=可以确保文件名中的空格、换行符和其他特殊字符不会被错误地解释为字段分隔符。
-
如果在while循环中使用read命令从管道或重定向中读取输入,请确保在done关键字之后没有额外的输入重定向操作符(如<或|),因为这可能会导致语法错误。
-
until循环
在Shell脚本中,until循环与while循环类似,但它会在条件表达式为假时执行循环体,直到条件表达式变为真时停止。也就是说,until循环会一直执行,直到满足某个条件为止。
1、基本语法
until [ 条件表达式 ]
do
# 当条件为假时执行的命令
done
2、示例
-
-
使用简单的条件检查
-
#!/bin/bash
counter=0
until [ $counter -ge 5 ]
do
echo "这是循环的第 $counter 次迭代"
((counter++))
done
在这个例子中,我们初始化了一个变量counter,并使用until循环检查该变量的值是否大于或等于5。在每次循环迭代中,我们都打印出一条消息并增加counter的值。当counter的值达到或超过5时,条件变为真,循环停止。
-
-
等待某个文件出现
-
假设你正在等待一个名为result.txt的文件在某个目录下出现,你可以使用until循环配合文件测试操作符[ -f file ]来实现。
#!/bin/bash
until [ -f /path/to/result.txt ]
do
echo "result.txt 文件尚未出现,等待..."
sleep 1 # 暂停1秒,以避免过快地检查
done
echo "result.txt 文件已出现,继续执行后续操作..."
在这个例子中,until循环会一直检查/path/to/result.txt文件是否存在。如果不存在,它会打印一条消息并暂停1秒,然后再次检查。一旦文件出现,条件变为真,循环停止,并打印出相应的消息。
3、注意事项
-
-
与while循环类似,确保在until循环中正确地更新条件表达式中使用的变量,以避免无限循环。
-
使用sleep命令可以在循环迭代之间添加延迟,以避免过快地检查条件或执行其他操作。
-
如果你的Shell脚本中同时使用了while和until循环,请确保你清楚它们之间的区别,并正确地使用它们来满足你的需求。
-
case语句
在Shell脚本中,case 语句用于基于多个可能的值进行条件选择。它与C、C++、Java等编程语言中的 switch 语句类似,但Shell中使用的是 case 而不是 switch。case 语句允许脚本根据变量的值来执行不同的代码块。
1、基本语法:
case 变量 in
模式1)
# 当变量匹配模式1时执行的命令
;;
模式2)
# 当变量匹配模式2时执行的命令
;;
...
*)
# 当变量不匹配任何模式时执行的命令(可选)
;;
esac
2、注意事项:
-
-
每个 case 分支都以 ;; 结尾,这表示该分支的结束。
-
*) 是一个特殊的模式,用于捕获所有不匹配前面模式的值。
-
可以在模式中使用通配符,如 *(匹配任何字符串)、 ?(匹配任何单个字符)等。
-
3、示例:
声明一个名为 DAY 的变量,它包含了星期几的缩写(如Mon、Tue等),根据这个变量的值来输出不同的消息。
#!/bin/bash
DAY="Wed"
case $DAY in
Mon)
echo "Today is Monday."
;;
Tue)
echo "Today is Tuesday."
;;
Wed)
echo "Today is Wednesday."
;;
Thu)
echo "Today is Thursday."
;;
Fri)
echo "Today is Friday."
;;
Sat|Sun)
echo "Today is the weekend!"
;;
*)
echo "Invalid day: $DAY"
;;
esac
# 输出:Today is Wednesday.
在这个示例中,我们根据DAY变量的值来匹配不同的case分支,并输出相应的消息。注意,在Sat|Sun)这个分支中,我们使用了 | 来表示“或”的关系,这意味着当DAY的值为Sat或Sun时,都会执行这个分支中的命令。如果DAY的值不匹配任何已知的模式,那么就会执行*)这个默认分支中的命令。
其他控制语句
1、break
break 语句用于立即终止当前的循环(如 for、while 或 until 循环)。当遇到 break 语句时,循环将不再继续执行,而是跳转到循环之后的下一条语句。
#!/bin/bash
for i in {1..5}
do
if [ $i -eq 3 ]; then
break
fi
echo $i
done
# 输出:1 2
2、continue
continue 语句用于跳过当前循环的剩余部分,直接开始下一次循环迭代。当遇到 continue 语句时,循环的当前迭代将立即结束,但不会终止整个循环。
#!/bin/bash
for i in {1..5}
do
if [ $i -eq 3 ]; then
continue
fi
echo $i
done
# 输出:1 2 4 5
3、exit
exit 语句用于立即终止脚本的执行。可以为其提供一个状态码,用于指示脚本是否成功执行。状态码 0 通常表示成功,而非零值表示某种错误或异常情况。
#!/bin/bash
if [ ! -f "somefile.txt" ]; then
echo "somefile.txt does not exist!"
exit 1
fi
echo "Processing somefile.txt..."
# 如果 somefile.txt 不存在,则脚本会输出错误消息并退出,状态码为 1
注意:exit 语句不仅会终止当前脚本的执行,还会终止任何调用该脚本的父进程(如果它是在子shell中运行的)。因此,在脚本中使用 exit 时要小心,确保你了解其潜在影响。
6.shell示例
学习脚本
#! /bin/bash
#--------------------------------------------
# shell shell脚本学习
# author:zhouxx
#--------------------------------------------
# if语句
if [ $1="input" ]
then
echo "Hello Shell"
elif [ $1!="output" ]
then
echo exit
else
echo "Good bye"
fi
# while循环
temp=$2
while [ $temp -lt 10 ]
do
echo "whileing"
temp=$(($temp+1))
done
# case的使用
case $temp in
"10")
echo "while正常执行"
;;
*)
echo "while执行异常,temp=$temp"
;;
esac
# 使用read进行终端读取
read -p "请随便输入:" input
echo "input=$input"
# 当前文件名为
echo "当前文件名为:$(basename $0)"
# 更改文件后缀
echo "文件名更换为txt:$(basename $0 .sh).txt"
# 当前文件的路径为
echo "当前文件的路径为:$(dirname $0)"
# 编写一个函数
func(){
echo "函数func正在被调用"
echo "传入func的参数为:$1,即文件路径"
return 1;
}
# 调用这个函数
func "$0"
echo "函数的返回值为$?"
服务器巡检脚本
#!/bin/bash
#--------------------------------------------
# shell 服务器巡检示例
# author:zhouxx
#--------------------------------------------
#系统信息
os_system(){
os_type=`uname`
echo "当前系统是:$os_type"
os_banben=`cat /etc/redhat-release`
echo "当前系统版本是:$os_banben"
os_neihe=`uname -r`
echo "当前系统内核为:$os_neihe"
os_time=`date +%F_%T`
echo "当前系统实际为:$os_time"
os_last=`uptime |awk '{print $4 $5}'| awk -F , '{print $1}'`
echo "当前系统最后重启时间为:$os_last"
os_hostname=`hostname`
echo "当前系统主机名为:$os_hostname"
}
#网络信息
os_network(){
##主机IP地址
ip_addr=`ifconfig ens33| grep broadcast | awk '{print $2}'`
echo "当前系统ip为:$ip_addr"
##判断主机是否可以连通
ping -c1 www.baidu.com
if [ $? -eq 0 ];then
echo "当前系统IP可以连通"
else
echo "当前系统IP不通请联系管理员"
fi
##统计网卡流量
#流入流量
RX=`ifconfig ens33 | grep RX | head -n1 | awk '{print $3/1024/1024}'`
echo "网卡流入流量为:$RX"
#流出流量
TX=`ifconfig ens33 | grep TX | head -n1 | awk '{print $3/1024/1024}'`
echo "网卡流出流量为:$TX"
}
#硬件信息
cpu_info(){
#cpu的物理个数
cpu_phy=`cat /proc/cpuinfo | grep "physical id" | wc -l`
echo "当前系统物理cpu为:$cpu_phy"
#cpu的核心数
cpu_core=`cat /proc/cpuinfo | grep "core" | wc -l`
echo "当前系统cpu核心数为:$cpu_core"
#cpu的型号
cpu_model=`cat /proc/cpuinfo | grep "model" | sed -n '2p' | awk -F : '{print $2}'`
echo "当前系统cpu型号是:$cpu_model"
}
##内存信息
mem_info(){
mem_total=`free -m | grep Mem | awk '{print $2}'`
echo "内存总大小为:$mem_total"
mem_used=`free -m | grep Mem | awk '{print $3}'`
echo "已使用内存为:$mem_used"
mem_free=`free -m | grep Mem | awk '{print $4}'`
echo "剩余内存为:$mem_free"
#百分比
shiyong_free=`echo "scale=2;$mem_used/$mem_total*100"|bc`
echo "已使用内存百分比为:$shiyong_free"
shengyu_free=`echo "scale=2;$mem_free/$mem_total*100"|bc`
echo "剩余内存百分比为:$shengyu_free"
}
os_system
os_network
cpu_info
mem_info