shell脚本进阶

一、for循环

for 变量名 in 列表;do

循环体

done

 

列表生成方式:

(1) 直接给出列表

(2) 整数列表:

(a) {start..end}

(b) $(seq [start [step]] end)

(3) 返回列表的命令

$(COMMAND)

(4) 使用glob,如:*.sh

(5) 变量引用;

$@, $*

 

例1:

[root@centos6 data]#for i in /data/*.sh;do echo the filename is $i;done

the filename is /data/67_createuser.sh

the filename is /data/checkint.sh

the filename is /data/createuser.sh

the filename is /data/filetype.sh

the filename is /data/if.sh

 

 

例2:求100以内的偶数和

#!/bin/bash

#

declare -i sum=0

 

for i in seq 2 2 100;do

let sum+=i

done

echo sum=$sum

 

 

例3:脚本内多行重定向写法

for i in {1..10};do

cat >> f1 <<-EOF

aa

bb

EOF

done

EOF前加上-

 

例4:创建10个用户,分别为user1到user10,密码分别为12345

#!/bin/bash

#

for i in user{1..10};do

if id $i &>/dev/null;then

echo “$i is exist”

else

useradd $i

echo “$i has created”

echo 12345 |passwd –stdin $i &>/dev/null

fi

done

 

 

例5:扫描某个网段的所有主机,在线显示up,不在线显示down,把在线的主机ip保存到文件中hostup.list

#!/bin/bash

#

net=192.168.67

for i in {1..254};do

{ if ping -c1 -w1 $net.$i &>/dev/null;then

echo “$net.$i is up”

echo $net.$i >> /data/hostup.list

else

echo “$net.$i is down”

fi } &

done

wait

加上{ }加快运行速度,多个程序并行执行,wait表示执行结束自动弹出

 

 

 

二、let命令中 i++与++i 的区别

i++:

[root@centos6 data]#i=0

[root@centos6 data]#let i++

[root@centos6 data]#echo $?

1

++i:

[root@centos6 data]#i=0

[root@centos6 data]#let ++i

[root@centos6 data]#echo $?

0

 

 

三、eval命令

[root@centos6 data]#n=3;for i in eval echo {1..$n};do echo num is $i ;done

num is 1

num is 2

num is 3

eval命令会先引用变量$n,让后在执行echo命令

 

 

四、for循环嵌套

for i in {1..10};do

for i in {1..10};do

done

done

 

例1:打印矩形

 

length=$1

width=$2

for i in seq 1 $length;do

for j in seq 1 $width;do

color=$[RANDOM%7+31]

bk=$[RANDOM%7+41]

echo -e “\e[1;5;${bk};${color}m*\e[0m\c”

done

echo

done

 

 

例2:打印9*9乘法表

for i in {1..9};do

for j in seq 1 $i;do

sum=$[i*j]

echo -e “${j}*${i}=${sum}\t\c”

done

echo

done

 

例3:打印国际象棋

for i in {1..8};do

for k in {1..2};do

if [ $[i%2] -eq 0 ];then

for j in {1..4};do

echo -e “\e[1;42m    \e[0m\c”

echo -e “\e[1;43m    \e[0m\c”

done

echo

else

for j in {1..4};do

echo -e “\e[1;43m    \e[0m\c”

echo -e “\e[1;42m    \e[0m\c”

done

echo

fi

done

done

 

 

 

例4:打印等腰三角形

#!/bin/bash

#

#********************************************************************

#Author:                wqf

#QQ:                    88888888

#Date:                  2018-05-08

#FileName:             sanjiaoixng.sh

#URL:                   http://www.magedu.com

#Description:          The test script

#Copyright (C):         2018 All rights reserved

#********************************************************************

read -p “please input line num: ”  line

declare -i linenum=0

 

for i in seq 1 $line;do

let ++linenum

star=$[linenum*2-1]

space=$[line-linenum]

for k in seq $space;do

echo -e ” \c”

done

 

for j in seq $star;do

echo -e “*\c”

done

echo

done

 

[root@centos6 data]#sanjiaoxing.sh

please input line num: 6

*

***

*****

*******

*********

***********

 

 

五、while循环

while CONDITION; do

循环体

done

 

CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为“true”,则执行一次循环;直到条件测试状态为“false”终止循环

因此:CONDTION一般应该有循环控制变量;而此变量的值会在循环体不断地被修正

进入条件:CONDITION为true

退出条件:CONDITION为false

 

 

例1:求100以内奇数和

#!/bin/bash

#

declare -i sum=0

declare -i i=1

while [ $i -le 100 ];do

if [ $[i%2] -eq 1 ];then

sum+=i

fi

let i++

done

echo sum=$sum

 

 

例2:每7分钟执行某个命令

[root@centos6 data]#vim ping.sh

 

#!/bin/bash

#

while true;do

echo task

sleep 420

done

如果要防止脚本意外终端,可以用执行nohup scripts

 

例3:监控http服务,每10秒检查一次服务是否重启,如果没在线,就重启

#!/bin/bash

#

SLEEP=10

while true;do

if killall -0 httpd &>/dev/null;then

:

else

systemctl restart httpd

echo “at date "+%F %T" restart the httpd” >>/data/httpd.log

sleep $SLEEP

fi

done

killall -0 httpd 判断httpd服务是否在运行

 

 

 

六、until循环

until CONDITION; do

循环体

done

进入条件: CONDITION 为false

退出条件: CONDITION 为true

 

例1:如果wang用户登陆,则退出

#!/bin/bash

#

until w |grep wang &>/dev/null;do

sleep 1

done

echo “find wang login”

 

 

七、循环控制语句continue

用于循环体中

continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层

while CONDTIITON1; do

CMD1

if CONDITION2; then

continue

fi

CMDn

done

 

 

例1:continue

#!/bin/bash

#

for i in {1..10};do

if [ $i -eq 5 ];then

continue

fi

echo $i

done

执行结果是:当$i=5时,

[root@centos6 data]#continue.sh

1

2

3

4

6

7

8

9

10

 

例2:continue2

#!/bin/bash

#

for i in {1..3};do

for j in {1..10};do

if [ $j -eq 5 ];then

continue 2

fi

echo $j

done

echo

done

执行结果为:

[root@centos6 data]#continue.sh

1

2

3

4

1

2

3

4

1

2

3

4

continue2 退出的是外层的for循环语句,外层循环的echo执行也被终端

 

 

八、循环控制语句break

用于循环体中

break [N]:提前结束第N层循环,最内层为第1层

while CONDTIITON1; do

CMD1

if CONDITION2; then

break

fi

CMDn

done

 

例1:break

#!/bin/bash

#

for i in {1..3};do

for j in {1..10};do

if [ $j -eq 5 ];then

break

fi

echo $j

done

echo

done

执行结果为:

[root@centos6 data]#break.sh

1

2

3

4

 

1

2

3

4

 

1

2

3

4

此时break中断的是内部循环

例2:break2

 

#!/bin/bash

#

for i in {1..3};do

for j in {1..10};do

if [ $j -eq 5 ];then

break 2

fi

echo $j

done

echo

done

执行结果为:

[root@centos6 data]#break.sh

1

2

3

4

此时break 2中断的是外部循环

 

例3:猜数字,生成一个随机数,提示输入1到10以内的数字,然后比较大小,

#!/bin/bash

#

rand=$[RANDOM%11]

while read -p “please input a digit in 0 to 10: ” n;do

if  [[ ! “$n” =~ ^[0-9]+$ ]];then

echo “please in put a digit 1-10”

continue

fi

if [ “$n” -lt “$rand” ];then

echo “less”

elif [ “$n” -gt “$rand” ];then

echo “more”

else

echo “right”

break 2

fi

 

done

 

 

九、循环控制shift命令

shift [n]

用于将参量列表 list 左移指定次数,缺省为左移一次。

参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。while 循环遍历位置参量列表时,常用到 shift

./doit.sh a b c d e f g h

./shfit.sh a b c d e f g h

 

例1:创建用户,

#!/bin/bash

#

until [ -z “$1” ];do

useradd $1

echo “$1 has created”

shift 1

done

echo done

 

例2:打印所有的

#!/bin/bash

while [ $# -gt 0 ] # or (( $# > 0 ))

do

echo $*

shift

done

 

十、while特殊用法

while循环的特殊用法(遍历文件的每一行):

while read line; do

循环体

done < /PATH/FROM/SOMEFILE

依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line

 

例1:文件/etc/passwd中uid为偶数的行,显示其用户名和id号

while read  line;do

id=echo $line|cut -d: -f3

user=echo $line|cut -d: -f1

 

if [ $[id%2] -eq 0 ];then

echo “$user:$id”

fi

done </etc/passwd

 

 

例2:检查分区利用率,利用率大于5报警,且显示其设备名

#!/bin/bash

df | while read line;do

if echo $line | grep “^/dev/sd” &>/dev/null;then

used=echo $line | sed -nr "s/.*[ ]+([0-9]{1,3})%.*/\1/p"

device=echo $line | cut -d" " -f1

if [ $used -gt 5 ];then

echo “$device will full,used $used%”

fi

fi

done

 

 

十一、for循环的特殊格式:

for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))

do

循环体

done

 

例1:1到100的和

#!/bin/bash

#

for ((sum=0,i=1;i<=100;i++ ));do

sum=$[sum+i]

done

echo sum=$sum

 

十二、select循环与菜单

select variable in list

do

循环体命令

done

select 循环主要用于创建菜单,按数字顺序排列的菜单项将显示在标准错误上,并显示 PS3 提示符,等待用户输入

用户输入菜单列表中的某个数字,执行相应的命令

用户输入被保存在内置变量 REPLY 中

select与case

select 是个无限循环,因此要记住用 break 命令退出循环,或用 exit 命令终止脚本。也可以按 ctrl+c 退出循环

select 经常和 case 联合使用

与 for 循环类似,可以省略 in list,此时使用位置参量

 

例1:

PS3=”please select scripts (1-4):”

select menu in etcbakup yum.sh alias.sh reset.sh;do

case $menu in

etcbakup)

echo “$REPLY:start bakup”

break

;;

yum.sh)

echo “$REPLY:start configuure yum”

break

;;

alias.sh)

echo “$REPLY:start configure alias”

break

;;

reset.sh)

echo “$REPLY:start reset the system environment”

break

;;

*)

echo “$REPLY:nothing to do”

;;

esac

done

 

 

十三、信号捕捉trap

trap ‘触发指令’ 信号

自定义进程收到系统发出的指定信号后,将执行触发指令,而不会执行原操作

trap ” 信号

忽略信号的操作

trap ‘-‘ 信号

恢复原信号的操作

trap -p

列出自定义信号操作

 

 

例1:捕捉信号2

trap “press the ctrl+c” 2                     ###使2信号失效,且提示press the ctrl+c

for (( i=0;i<=10;i++ ));do

echo $i

sleep 1

done

 

trap “” 2                                  ###使2信号失效,不提示

for (( i=11;i<=20;i++ ));do

echo $i

sleep 1

done

 

trap “-” 2

for (( i=21;i<=30;i++ ));do

echo $i

sleep 1

done

 

十四、定义函数

函数由两部分组成:函数名和函数体

help function

语法一:

f_name (){

…函数体…

}

语法二:

function f_name {

…函数体…

}

语法三:

function f_name () {

…函数体…

}

 

十五、查看函数、删除函数

declare -f 查看系统所有函数

declare -f function_name 查看函数

unset function_name 删除函数

 

十六、函数中变量生效范围

默认函数和当前shell的变量使通用的

[root@centos6 ~]#func1 () { name=wang;echo “func1:$name”; }

[root@centos6 ~]#func1

func1:wang

[root@centos6 ~]#echo $name

wang

 

让函数中变量只在函数中生效加上local,如:func1 () { local name=wang;echo “func1:$name”; }

 

declare -i 在函数中声明变量相当赋予了local的特性,只在函数中生效

[root@centos6 ~]#func2 () { declare -i num=100;echo “$num”; }

[root@centos6 ~]#func2

100

[root@centos6 ~]#echo $num

 

declare -ig 在函数中声明变量的相当于普通变量,在函数和当前shell生效(在centos6上不支持,支持centos7)

[root@centos7 boot]#func1 () { declare -ig num=100;echo $num; }

[root@centos7 boot]#func1

100

[root@centos7 boot]#echo $num

100

 

 

十七、函数调用

例1:根据参数选择

#!/bin/bash

green_yellow (){

if [ “$1” = “-r” ];then

echo -e “\e[1;43m \e[1;42m \e[0m\c”

else

echo -e “\e[1;42m \e[1;43m \e[0m\c”

fi

echo

}

green_yellow

green_yellow -r

 

例2:写一个函数,显示当前系统版本,如果是6则显示oldversion,否则显示最新版本

方法一

[root@centos7 boot]#version () {

> ver=sed -r "s/.*[ ]+([0-9]+)\..*/\1/" /etc/centos-release

> if [ $ver -eq 6 ];then

>     echo “oldversion”

> else

>     echo “newversion”

> fi

> echo $ver

> }

 

方法二

[root@centos7 boot]#version() { ver=sed -r "s/.*[ ]+([0-9]+)\..*/\1/" /etc/centos-release;echo “$ver”; }

[root@centos7 boot]#version

7

[root@centos7 boot]#if [ version -eq 6 ];then echo version is old;else echo version is new;fi

version is new

 

方法三:根据return返回值判断

[root@centos7 boot]#version() { ver=sed -r "s/.*[ ]+([0-9]+)\..*/\1/" /etc/centos-release;return $ver; }

[root@centos7 boot]#version

[root@centos7 boot]#echo $?

7

[root@centos7 boot]#if [ version -eq 6 ];then echo version is old;else echo version is new;fi

version is new

 

 

十八、实现多个脚本调用一个函数

编辑一个文件,如functions,把写好的函数都写入文件。然后在别的脚本中调用

写入脚本格式简单如下:

#!/bin/bash

source /data/functions

函数1

函数2

………..

 

十九、action函数

[root@centos7 boot]#source /etc/init.d/functions

[root@centos7 boot]#action “command successd”

command successd                                           [  OK  ]

[root@centos7 boot]#action “command failed” /bin/false

command failed                                             [FAILED]

 

二十、定义全局函数

全局函数在子shell生效,在脚本调用脚本时可用

[root@centos7 boot]#func1 () { local num=100;echo $num; }

[root@centos7 boot]#func1

100

[root@centos7 boot]#export -f func1

 

 

二十一、数组

变量:存储单个元素的内存空间

数组:存储多个元素的连续的内存空间,相当于多个变量的集合

数组名和索引

索引:编号从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

 

1.一次只赋值一个元素

[root@centos6 data]#declare -a name[1]=a

[root@centos6 data]#declare -a name[2]=b

[root@centos6 data]#declare -a name[3]=c

显示所有数组元素

[root@centos6 data]#echo ${name[*]}

a b c

 

2.一次赋值多个元素(可以生成列表)

例1:

[root@centos6 data]#declare -a name=(“c” “d” “e”)

[root@centos6 data]#echo ${name[*]}

c d e

 

例2:生成列表

[root@centos6 data]#number=({1..10})

[root@centos6 data]#echo ${number[*]}

1 2 3 4 5 6 7 8 9 10

 

例3:

[root@centos6 data]#filename=(/data/*.sh)

[root@centos6 data]#echo ${filename[@]}

/data/3.sh /data/67_createuser.sh /data/9×9.sh /data/a.sh /data/checkint.sh

 

3.赋值特定的元素

[root@centos6 data]#name=([1]=x [2]=y)

[root@centos6 data]#echo ${name[*]}

x y

4.交互式数组赋值

例1:

[root@centos6 data]#read -a name

wang li zhang zhao

[root@centos6 data]#echo ${name[*]}

wang li zhang zhao

 

显示所有数组:

declare -a

引用数组

引用数组元素:

${ARRAY_NAME[INDEX]}

注意:省略[INDEX]表示引用下标为0的元素

引用数组所有元素:

${ARRAY_NAME[*]}

${ARRAY_NAME[@]}

数组的长度(数组中元素的个数):

${#ARRAY_NAME[*]}

${#ARRAY_NAME[@]}

删除数组中的某元素:导致稀疏格式

unset ARRAY[INDEX]

删除整个数组:

unset ARRAY

 

二十三、在已有的数组后面添加新元素

  首先不知道哪些下标被占用,可以这样定义declare -a name[${#name[*]}]=wang

${#name[*]}代表元素的个数

 

 

二十四、生成20个随机数保存于数组中,并找出其最大值和最小值

#!/bin/bash

#

declare -a rand

for ((i=0;i<20;i++));do

rand[$i]=$RANDOM

if [ $i -eq 0 ];then

max=${rand[$i]}

min=${rand[$i]}

else

[ $max -lt ${rand[$i]} ] && max=${rand[$i]}

[ $min -gt ${rand[$i]} ] && min=${rand[$i]}

fi

done

echo ${rand[*]}

echo $max $min

 

二十五、数组赋值

#!/bin/bash

#

declare -A disk

df |grep ^/dev/sd |while read line;do

device=echo $line|cut -d" " -f1

used=echo $line |sed -r "s/.*[ ]([0-9]{1,3}).*/\1/"

disk[$device]=$used

echo ${disk[$device]}

done

 

二十六、字符串切片

${#var}:返回字符串变量var的长度

例1:

[root@centos6 ~]#str=echo {a..z}|tr -d " "

[root@centos6 ~]#echo $str

abcdefghijklmnopqrstuvwxyz

[root@centos6 ~]#echo ${#str}

26

 

${var:offset}:表示跳过几个开始取值

例:跳过5个开始取值

[root@centos6 ~]#echo $str

abcdefghijklmnopqrstuvwxyz

[root@centos6 ~]#echo ${str:5}

fghijklmnopqrstuvwxyz

 

${var:offset:N }:表示跳过前几个子浮窗,然后取N个子浮窗

例:

[root@centos6 ~]#echo $str

abcdefghijklmnopqrstuvwxyz

[root@centos6 ~]#echo ${str:3:4}

defg

 

${var: -offset}:表示取后面倒数几个字符,注意-offset前面要加空格

例:

[root@centos6 ~]#echo $str

abcdefghijklmnopqrstuvwxyz

[root@centos6 ~]#echo ${str: -3}

xyz

 

${var:offset:-length}:从最左侧跳过offset字符,一直向右取到距离最右侧lengh个字符之前的内容(去头去尾取中间)

例:

[root@centos7 ~]#echo $str

abcdefghijklmnopqrstuvwxyz

[root@centos7 ~]#echo ${str:3:-3}

defghijklmnopqrstuvw

 

例2:取后面4个,再去掉最后两个

[root@centos7 ~]#echo $str

abcdefghijklmnopqrstuvwxyz

[root@centos7 ~]#echo ${str: -4:-2}

wx

 

 

命令matemp

创建随机文件

[root@centos6 data]#mktemp  /data/tmpXXXX

/data/tmp4YEW

创建随机目录-d

[root@centos6 data]#mktemp -d /data/tmpXXXXX

/data/tmpq9VGE

-p指定生成文件后放置的目录

[root@centos6 data]#mktemp -p /data/ tmpXXXXXXX

/data/tmpLCNalET

 

-p指定目录可以用变量替代,执行的结果也可以放置到一个变量之中,方便以后查找删除

tmpfile=`mktemp -p “$dir” tmpXXXX

rm -rf $tempfile

 

 

install命令:

install [OPTION]… [-T] SOURCE DEST 单文件

install [OPTION]… SOURCE… DIRECTORY

install [OPTION]… -t DIRECTORY SOURCE…

install [OPTION]… -d DIRECTORY…创建空目录

选项:

-m MODE,默认755

-o OWNER

-g GROUP

示例:

install -m 700 -o wang -g admins srcfile desfile

install –m 770 –d /testdir/installdir

 

     

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/98464

联系我们

400-080-6560

在线咨询

工作时间:周一至周五,9:30-18:30,节假日同时也值班

QR code