目录
一、Shell概述
Shell是Linux系统连接用户和操作系统的外壳程序,将用户的输入和请求选择性传递给操作系统,然后处理来自操作系统的结果输出,简单来说Shell就是一个用户和操作系统之间的命令解释器。
Shell是一个外壳程序,同时也是一个脚本语言。
Shell脚本本质上是Linux命令,一条一条命令组合在一起,实现某一个功能,就形成了Shell脚本。
最常用的Shell是Bash(/bin/bash),是大多数Linux系统默认的Shell环境。
shell编程注意事项:
- shell文件通常以后缀 .sh 结尾;
- 不能使用特殊符号,有严格的空格控制;
- shell文件以 #!/bin/bash 开头
- shell脚本变量命名规则与C语言一样
二、输入输出
Shell脚本语言只有一种数据类型,就是字符类型,即所有变量的类型都是字符,输入和输出的都是字符。
Shell变量分为用户变量、环境变量、系统变量,环境变量分为用户级、系统级和脚本级,用户级变量重启Shell外壳就会自动清除,系统变量用于实现某一个功能而存在。
变量的可用 = 进行赋值创建,用 $ 获取变量值,用 unset 清除变量。
# Shell常见的环境变量:
PATH 命令所示路径,以冒号为分割;
HOME 打印用户家目录;
SHELL 显示当前Shell类型;
USER 打印当前用户名;
ID 打印当前用户id信息;
PWD 显示当前所在路径;
TERM 打印当前终端类型;
HOSTNAME 显示当前主机名;
PS1 定义主机命令提示符的;
HISTSIZE 历史命令大小,可通过 HISTTIMEFORMAT 变量设置命令执行时间;
RANDOM 随机生成一个 0 至 32767 的整数;
HOSTNAME 主机名
# Shell常见的系统变量:
$0 当前脚本的名称;
$n 当前脚本的第n个参数,n=1,2,…9;
$* 当前脚本的所有参数(不包括程序本身);
$# 当前脚本的参数个数(不包括程序本身);
$? 令或程序执行完后的状态,返回0表示执行成功;
$$ 程序本身的PID号。
变量也可通过 read 进行键盘输入赋值,通过 echo 输出。
在使用 read 输入数据时,可配合 echo 使用进行输出提示,如 echo 提示字符串; read 变量 ,也可直接使用 read -p 提示字符串 变量 进行输入提示。
在用 $ 对变量取值时,为了其他字符串对变量的影响,可用 { } 跟在 $ 后面将变量括起来。
#!/bin/bash
# 颜色输出格式 echo -e "\033[(显示方式);(字体色);(背景色)m(输入内容)\033[0m"
testStr1="hello world!\n"
echo $testStr1
# 显示方式:
# 1 粗体高亮
# 4 下划线
# 5 闪烁
# 8 隐藏
# 字体颜色:
# 31 红色
# 32 绿色
# 33 黄色
# 34 蓝色
# 35 紫色
# 36 天蓝
# 37 白色
# 背景颜色:
# 41 红色
# 42 绿色
# 43 黄色
# 44 蓝色
# 45 紫色
# 46 天蓝
# 47 白色
echo "请输入一个测试字符串:"; read testStr2
echo -e "\033[1;31;46m$testStr2\033[0m"
# 最后的0表示恢复默认系统输出默认设置
# echo 加上 -e 选项之后支持转移字符:
# \n 回车换行
# \r 回车
# \v 换行
# \t Tab制表符距离
# \c 在字符串输入末尾不换行
echo -e "请输入一个测试字符串:\c"; read testStr3
echo -e "\033[4;33;42m$testStr3\033[0m"
#echo 加上 -n 选项,输出不换行
read -p "请输入一个测试字符串:" testStr4
echo -e -n "\033[5;35;41m${testStr4}\033[0m"
三、分支控制
1. 表达式
Shell脚本中的表达式分为算术表达式和逻辑表达式,算术表达式常用 let 进行赋值和计算,也可以用 ((表达式)) 双小括号进行算术表达式的赋值和计算。
除了Shell自身的算术表达式处理方式,还可以通过下载 bc 工具进行处理,也可用通过Shell脚本将字符串通过管道 | 传递给bc工具,让bc工具进行计算。
逻辑表达式常用 test 命令进行逻辑判断,test命令的退出码为0表示逻辑为true,否则为false,通常在test命令的后面跟上 echo $? 语句显示test命令的判断结果。
test命令在进行判断的时候,= 两边都需要空一格。
test命令可用 [ ] 中括号进行等价替换,注意 [ 和 ] 前后中括号之间都要有空格。
#!/bin/bash
echo "算术计算:"
a=1
b=2
let c=$a+$b
echo $c
((d=($a+$b)*$c))
echo $d
echo $(($c/$b))
yum install -y bc
echo "$c/$b" | bc
echo "scale=2;$c/$b" | bc
# bc工具通过scale指定精度
echo "逻辑判断:"
test $a = $b; echo $?
test $c = $(($a+$b)); echo $?
# 复合条件判断
# -gt 大于,greater than
# -lt 小于,less than
# -ge 大于等于,greater or equal
# -le 小于等于,less or equal
# -eq 数值等于,equal
# -a 与,可用&&替换,and
# -o 或,可用||替换,or
echo "复合条件判断:"
age=15
test $age -gt 10 -a $age -lt 20; echo $?
test $age -lt 10 -o $age -gt 20; echo $?
[ $age -gt 10 ] && [ $age -lt 20 ]; echo $?
[ $age -le 10 ] || [ $age -ge 20 ]; echo $?
# 文件和目录属性判断:
# -e 判断文件或目录是否存在
# -f 判断是否为文件以及是否存在
# -d 判断是否为目录以及是否存在
# -r -w -x 判断权限
chmod u-x _02_expression.sh
[ -r _02_expression.sh ]; echo $?
[ -w _02_expression.sh ]; echo $?
[ -x _02_expression.sh ]; echo $?
chmod u+x _02_expression.sh
2. if 分支
逻辑判断语句通常会搭配 if 条件分支控制语句使用,if条件控制语句有单分支、双分支和多分支三种不同的格式。
#!/bin/bash
# 单分支格式
if [ -f _01_hello.sh ]; then
echo "01FILE EXIST!"
fi
# 双分支格式
if [ -f _02_hello.sh ]; then
echo "02FILE EXIST!"
else
echo "02FILE NOT EXIST!"
fi
# 多分支格式
if [ -d _01_hello.sh ]; then
echo "01DIR EXIST!"
elif [ -f _01_hello.sh ]; then
echo "01FILE EXIST!"
else
echo "01FILE 01DIR NOT EXITS"
fi
# 判断crond进程是否正常运行
name=crond
num=$(ps -ef|grep $name|grep -vc grep)
if [ $num -eq 1 ];then
echo "$num running!"
else
echo "$num is not running!"
fi
3. case 分支
case 变量值 in
模式 1)
命令序列1;;
模式 2)
命令序列2;;
.......
*)
默认命令序列;;
esac
case值得注意的特点:
- case行尾必须为单词"in",每一模式必须以右括号“)”结束
- 双分号";;"表示命令序列结束
- 模式字符串中,可以用方括号表示一个连续的范围,如"[0-9]";还可以用管道符号" | “表示或,如"A|B”
- 最后的 " *) " 表示默认模式,其中的 星号 相当于通配符
#!/bin/bash
read -p "请输入一个字符:" input
case "$input" in
[a-z]|[A-Z])
echo "您输入的是一个字母";;
[0-9])
echo "您输入的是一个数字";;
*)
echo "您输入的是一个空格、功能键或其他按键";;
esac
四、循环控制
1. for 循环
for val in valuelist...
do
commands
done
for((exp1;exp2;exp3))
do
commands
done
# exp1 初始化
# exp2 变量取值范围
# exp3 变量增量
#!/bin/bash
for i in {1..5};
do
echo -e "\033[${i}mhello world\033[0m"
done
for i in 1 3 5;
do
for j in 31 33 35 37;
do
echo -e "\033[${i};${j}mhello world\033[0m"
done
done
2. while 循环
while 条件判断
do
command
...
done
# while后面的命令执行成功则循环,否则跳出循环
#!/bin/bash
count=3
while [ $count -ge 0 ]
do
echo $count
let count=$count-1
done
count=3
while (($count >= 0))
do
echo $count
((count--))
done
# 通过while实现shell脚本给文件内容添加行号
if [ -f $1 ]; then
i=1
cat $1 | while read text
do
echo "$i:$text"
((i++))
done
else
echo "文件不存在"
fi
3. select 循环
select var in menu...
do
commands
done
# select 后面跟的是菜单
# in 关键字后面是菜单取值列表
# 这些值以空格隔开
# 用户选择数字对应菜单值
# 变量var获取值后执行循环一次
#!/bin/bash
PS3="Please Select[1-4]"
select m in one two three four exit;
do
if [ $m = exit ]; then
break
fi
echo $m
done
# PS3是一个环境变量,是select的提示符
五、函数
Shell的函数定义与C语言类似,需要先定义再调用原则,函数名后面接小括号,可以有参数或返回值,但是没有返回值类型。
#shell中的函数定义
#其中function为关键字,FUNCTION_NAME为函数名
function FUNCTION_NAME(){
command1 #函数体中可以有多个语句,不允许有空语句
command2
...
}
#省略关键字function,效果一致
FUNCTION_NAME(){
command1
command2
...
}
# 函数名后的小括号中没有参数
# 函数function关键字可以不写
# 函数必须遵循先定义再调用原则
#!/bin/bash
echo -n "请输入文件名:"
read FILE
function checkFileExist(){
if [ -f $FILE ]; then
return 0
else
return 1
fi
}
echo "call function checkFileExist"
checkFileExist
if [ $? -eq 0 ]; then
echo "$FILE exist"
else
echo "$FILE not exist"
fi
#!/bin/bash
Hello(){
echo "hello world"
}
Hello
# 写一个函数判断文件是否存在
function CheckFileExist(){
if [ -f $1 ]; then
return 0
else
return 1
fi
}
read -p "请输入文件名:" filename
CheckFileExist $filename
if [ $? -eq 0 ]; then
echo "$filename is exits!"
else
echo "$filename is not exits!"
fi
由于Shell是一门面向过程的脚本型语言,而且用户主要是Linux系统管理人员,所以并没有非常活跃的社区,这也造成了Shell缺乏第三方函数库,所以在很多时候需要系统管理人员根据实际工作的需要自行开发函数库。下面建立一个叫 funclib 的函数库,该函数库目前只有一个函数,用于判断文件是否存在。
_checkFileExists(){
if [ -f $1 ]; then
echo "File:$1 exists"
else
echo "File:$1 not exist"
fi
}
其他脚本在希望直接调用_checkFileExists函数时,可以通过直接加载 funclib 函数库的方式实现。加载方式有如下两种:
#使用“点”命令
$ . /PATH/TO/LIB
#使用source命令
$ source /PATH/TO/LIB
假设现在有个脚本想要直接调用_checkFileExists函数,可以通过加载 funclib 函数库来实现。
可以通过 set 和 unset 命令查看当前内存中已经定义和载入的函数,使用 unset 清除函数,函数修改后必须重新载入。