linux shell 高级编程,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) 变量引用;$@, $

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, ...调用这些参数;还可以使用$@, $*, $#等特殊变量

马函数变量

变量作用域:

环境变量:当前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."

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值