shell高级编程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shell高级编程相关的知识,希望对你有一定的参考价值。
-
条件选择if语句
选择执行: 注意:if语句可嵌套 单分支 if 判断条件;then 条件为真的分支代码 fi 双分支 if 判断条件; then 条件为真的分支代码 else条件为假的分支代码 fi 多分支 if 判断条件1; then 条件为真的分支代码 elif 判断条件2; then 条件为真的分支代码 elif 判断条件3; then 条件为真的分支代码 else以上条件都为假的分支代码 fi 逐个条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if语 句
If示例
根据命令的退出状态来执行命令if ping -c1 -W2 station1 &> /dev/null; then
echo ‘Station1 is UP‘
elif grep "station1" ~/maintenance.txt &> /dev/null
then
echo ‘Station1 is undergoing maintenance‘
else echo ‘Station1 is unexpectedly DOWN!‘ exit 1
fi
条件判断:case语句
case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...*)
默认分支
;;
esac
case支持glob风格的通配符:
*: 任意长度任意字符
?: 任意单个字符
[]:指定范围内的任意单个字符
a|b: a或b
-
for循环
for 变量名 in 列表;do 循环体 done 执行机制: 依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直到列表中的元素 耗尽,循环结束
列表生成方式:
(1) 直接给出列表
(2) 整数列表:
(a) {start..end}
(b) $(seq [start [step]] end)
(3) 返回列表的命令$(COMMAND)
(4) 使用glob,如:.sh
(5) 变量引用;[email protected], $
-
while循环
while CONDITION; do 循环体 done CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次判断;条件 为“true”,则执行一次循环;直到条件测试状态为“false”终止循环 因此:CONDTION一般应该有循环控制变量;而此变量的值会在循环体不断地被修正 进入条件:CONDITION为true 退出条件:CONDITION为false
-
until循环
until CONDITION; do
循环体
done
进入条件: CONDITION 为false
退出条件: CONDITION 为true循环控制语句continue 用于循环体中 continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层 while CONDTIITON1; do CMD1 ... if CONDITION2; then continue fi CMDn ... done 循环控制语句break 用于循环体中 break [N]:提前结束第N层循环,最内层为第1层 while CONDTIITON1; do CMD1...if CONDITION2; then breakfiCMDn...done
示例:doit.sh
#!/bin/bash
Name: doit.sh
Purpose: shift through command line arguments
Usage: doit.sh [args]
while [ $# -gt 0 ] # or (( $# > 0 ))
doecho $*
shiftdone
示例:shift.sh
#!/bin/bash
#step through all the positional parameters
until [ -z "$1" ]
doecho "$1"
shiftdoneecho
创建无限循环
while true; do
循环体
done
until false; do
循环体
Done
特殊用法
while循环的特殊用法(遍历文件的每一行):
while read line; do
循环体
done < /PATH/FROM/SOMEFILE
依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line
练习
扫描/etc/passwd文件每一行,如发现GECOS字段为空,则填充用户名和单位电话为62985600,并提示该
用户的GECOS信息修改成功。
#!/bin/bash
while read line ;do
gecos=$(echo $line |cut -d: -f5)
if [ -z "$gecos" ];then
UserName=$(echo $line |cut -d: -f1)
usermod -c "$UserName 62985600" $UserName
echo "$UserName‘s gecos changed"
fi
done < /etc/passwd
编写个脚本,会对系统中已存在的用户进行身份判断,若为centos7,则uid大于1000的用户将判断为comm user,反之判定为sys user, 若为centos6,则uid大于500的用户判断为comm user,反之sys user.输出格式如下
root: sys user
……
liubei: comm user
#!/bin/bash
release=$(cat /etc/centos-release| sed -r ‘s/.* ([0-9]+)..*/1/‘)
while read line; do
uid=$(echo $line | cut -d: -f3)
name=$(echo $line | cut -d: -f1)
if [ $release = 6 -a $uid -lt 500 ] || [ $release = 7 -a $uid -lt 1000 ]; then
echo "$name: sys user"
else
echo "$name: comm user"
fi
done < /etc/passwd
找出分区利用率大于10%的分区,显示结果如下:
/dev/sda1 will be full : 33%
/dev/sda2 will be full : 99%
#!/bin/bash
df |grep /dev/sd |while read line;do
used=$(echo $line |tr -s " " % |cut -d% -f5)
name=$(echo $line |cut -d" " -f1)
if (( $used > 10 ));then
echo "$name will be full:$used%"
fi
done
-
特殊用法
双小括号方法,即((…))格式,也可以用于算术运算 双小括号方法也可以使bash Shell实现C语言风格的变量操作 I=10((I++)) for循环的特殊格式: for ((控制变量初始化;条件判断表达式;控制变量的修正表达式)) do循环体 done控制变量初始化:仅在运行到循环代码段时执行一次 控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断 for ((i=1;i<=100;i++));do let sum+=i done echo sum=$sum select循环与菜单 select variable in list do 循环体命令 done select 循环主要用于创建菜单,按数字顺序排列的菜单项将显示在标准错误上,并显示 PS3 提示符,等待用户输入 用户输入菜单列表中的某个数字,执行相应的命令 用户输入被保存在内置变量 REPLY 中[][][] select 是个无限循环,因此要记住用 break 命令退出循环,或用 exit 命令终止脚本。也可以按 ctrl+c 退出循环 select 经常和 case 联合使用 与 for 循环类似,可以省略 in list,此时使用位置参量
练习:为某餐馆用Shell制作一个点菜系统。
执行脚本,会列出主菜单,如下
请问吃什么?
1) 饭
2) 面
3)饺子
4)不吃等待用户选择
如选择1,则再问,选择完后报价
1) 炒饭
2)盖饭
3)木桶饭
如选择2,则再问
1)炒面
2)盖面
3)拉面
4)拌面
如选择3,则再问
1)猪肉大葱
2)素三鲜
3)韭菜鸡蛋
每项选择后,最终会报价,如
木桶饭: 10元
如选择4,则退出
#!/bin/bash
PS3="Please choose your food: "
echo "请问吃什么"
caidan() {
select menu in 饭 面 饺子 不吃;do
case $REPLY in
1) select fan in 炒饭 盖饭 木桶饭 返回;do
case $REPLY in
1) echo "炒饭: 10";break 2;;
2) echo "盖饭: 12";break 2;;
3) echo "木桶饭:15";break 2;;
4) caidan;;
esac
done
;;
2) select mian in 炒面 盖面 拉面 拌面 返回;do
case $REPLY in
1) echo "炒面: 10";break 2;;
2) echo "盖面: 12";break 2;;
3) echo "拉面:15";break 2;;
4) echo "拌面: 18";break 2;;
5) caidan;;
esac
done
;;
3) select jiaozi in 猪肉大葱 素三鲜 韭菜鸡蛋 返回;do
case $REPLY in
1) echo "猪肉大葱: 10";break 2;;
2) echo "素三鲜: 12";break 2;;
3) echo "韭菜鸡蛋:15";break 2;;
4) caidan;;
esac
done
;;
4) exit;;
esac
done
}
caidan
信号捕捉trap
trap ‘触发指令‘ 信号
自定义进程收到系统发出的指定信号后,将执行触发指令,而不会执行原操作
trap ‘‘ 信号
忽略信号的操作
trap ‘-‘ 信号
恢复原信号的操作
trap -p
列出自定义信号操作
trap示例
#!/bin/bash
trap ‘echo “signal:SIGINT"‘ int
trap -p
for((i=0;i<=10;i++));do
sleep 1
echo $i
done
trap ‘‘ int
trap -p
for((i=11;i<=20;i++));do
sleep 1
echo $i
done
trap ‘-‘ int
trap -p
for((i=21;i<=30;i++));do
sleep 1
echo $i
done
-
函数介绍
函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程 它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分 函数和shell程序比较相似,区别在于: 1.Shell程序在子Shell中运行 2. 而Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改 定义函数 ? 函数由两部分组成:函数名和函数体 ? help function ? 语法一: function f_name { ...函数体... } ? 语法二: function f_name () { ...函数体... } ? 语法三: f_name () { ...函数体... } 函数使用 ? 函数的定义和使用: – 可在交互式环境下定义函数 – 可将函数放在脚本文件中作为它的一部分 – 可放在只包含函数的单独文件中 ? 调用:函数只有被调用才会执行 调用:给定函数名 函数名出现的地方,会被自动替换为函数代码 ? 函数的生命周期:被调用时创建,返回时终止 检查载入函数 使用set命令检查函数是否已载入。set命令将在shell中显示所有的载入函 数
示例:
set findit=( ){ if [ $# -lt 1 ]; the echo "usage :findit file"; return 1 fi find / -name $1 -print }… 删除shell函数 现在对函数做一些改动后,需要先删除函数,使其对shell不可用。使用unset命令完成删除函数 命令格式为: unset function_name 示例: unset findit 再键入set命令,函数将不再显示 环境函数 使子进程也可使用 声明:export –f function_name 查看:export -f 或 declare -xf 函数可以接受参数: 传递参数给函数:调用函数时,在函数名后面以空白分隔给定参数列表即可;例如 “testfunc arg1 arg2 ...” 在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用[email protected], $*, $#等特殊变量 马函数变量 变量作用域: 环境变量:当前shell和子shell有效 本地变量:只在当前shell进程有效,为执行脚本会启动专用子shell进程;因此,本地变量 的作用范围是当前shell脚本程序文件,包括脚本中的函数 局部变量:函数的生命周期;函数结束时变量被自动销毁 注意:如果函数中有局部变量,如果其名称同本地变量,使 用局部变量 在函数中定义局部变量的方法 local NAME=VALUE
-
函数递归示例
函数递归: 函数直接或间接调用自身 注意递归层数 递归实例: 阶乘是基斯顿·卡曼于 1808 年发明的运算符号,是数学术语 一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且有0的阶乘为1,自然数n的 阶乘写作n! n!=1×2×3×...×n阶乘亦可以递归方式定义:0!=1,n!=(n-1)!×n n!=n(n-1)(n-2)...1 n(n-1)! = n(n-1)(n-2)! #!/bin/bash #fact() { if [ $1 -eq 0 -o $1 -eq 1 ]; then echo 1 else echo $[$1*$(fact $[$1-1])] fi } fact $1 ××× fork×××是一种恶意程序,它的内部是一个不断在fork进程的无限循环,实质是一个简单的递归 序。由于程序是递归的,如果没有任何限制,这会导致这个简单的程序迅速耗尽系统里面的所有资 源 函数实现 :(){ :|:& };: bomb() { bomb | bomb & }; bomb 脚本实现 cat Bomb.sh #!/bin/bash ./$0|./$0&
-
数组
变量:存储单个元素的内存空间 数组:存储多个元素的连续的内存空间,相当于多个变量的集合 数组名和索引 索引:编号从0开始,属于数值索引 注意:索引可支持使用自定义的格式,而不仅是数值格式,即为关联 索引,bash4.0版本之后开始支持 bash的数组支持稀疏格式(索引不连续) 声明数组: declare -a ARRAY_NAME declare -A ARRAY_NAME: 关联数组 注意:两者不可相互转换
数组赋值
数组元素的赋值:
(1) 一次只赋值一个元素;
ARRAY_NAME[INDEX]=VALUE
weekdays[0]="Sunday
weekdays[4]="Thursday
(2) 一次赋值全部元素:
ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)
(3) 只赋值特定元素:
ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
(4) 交互式数组值对赋值
read
-a ARRAY
显示所有数组:declare
-a
引用数组
引用数组元素:
${ARRAY_NAME[INDEX]}
注意:省略[INDEX]表示引用下标为
0的元素
引用数组所有元素:
${ARRAY_NAME[*]}
${ARRAY_NAME[@]}
数组的长度
(数组中元素的个数
)
:
${#ARRAY_NAME[*]}
${#ARRAY_NAME[@]}
删除数组中的某元素:导致稀疏格式
unset ARRAY[INDEX]
删除整个数组
unset ARRAY
数组数据处理
引用数组中的元素:
数组切片:${ARRAY[@]:offset:number}
offset: 要跳过的元素个数
number: 要取出的元素个数
取偏移量之后的所有元素
${ARRAY[@]:offset}
向数组中追加元素:
ARRAY[${#ARRAY[*]}]=value
关联数组:
declare -A ARRAY_NAME ARRAY_NAME=([idx_name1]=‘val1‘
[idx_name2]=‘val2‘...)
注意:关联数组必须先声明再调用
示例
生成10个随机数保存于数组中,并找出其最大值和最小值
#!/bin/bash
declare -a rand
declare -i max=0
declare –i min=32767
for i in {0..9}; do
rand[$i]=$RANDOM
echo ${rand[$i]}
[ ${rand[$i]} -gt $max ] && max=${rand[$i]}
[ ${rand[$i]} -lt $min ] && min=${rand[$i]}
done
echo "Max: $max Min:$min"
示例
编写脚本,定义一个数组,数组中的元素是/var/log目录下所有以.log结尾的
文件;要统计其下标为偶数的文件中的行数之和
#!/bin/bash
#declare -a files
files=(/var/log/*.log)
declare -i lines=0
for i in $(seq 0 $[${#files[*]}-1]); do
if [ $[$i%2] -eq 0 ];then
let lines+=$(wc -l ${files[$i]} | cut -d‘ ‘ -f1)
fi
done
echo "Lines: $lines."
以上是关于shell高级编程的主要内容,如果未能解决你的问题,请参考以下文章