前言
之前写过一篇关于shell脚本流程控制总结,这次继续写关于shell脚本的问题。本篇文章主要包含shell脚本中的函数以及数组的用法介绍。同时也涵盖了一些字符串处理以及shell脚本比较使用的小工具的介绍。
函数
函数function是由若干条shell命令组成的语句块,实现代码重用和模块话编程。
它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分。 函数shell程序比较相似,区别在于: shell程序在shell中运行 而shell函数在当前shell中运行。因此在当前shell中,函数可以对shell中变量进行修改。定义函数
函数由两部分组成:函数名和函数体
语法一: function f_name { ...函数体... } 语法二: function f_name () { ...函数体... } #语法三的写法相对简单,并且系统中的函数一般也使用第三种写法,所以简易使用第三中写法。语法三: f_name (){ ...函数体... }
函数返回值
函数有两种返回值:
- 函数的执行结果返回值: (1)使用echo等命令进行输出 (2)函数中调用命令的输出结果
- 函数的退出状态码 (1)默认取决于函数中执行的最后一条命令的退出状态码 (2)自定义退出状态码,其格式为,: return 从函数中返回,用最后状态命令决定返回值 return 0 无错误返回 return 1-255有错误返回
函数的使用
- 可以在交互式环境下定义函数
- 可将函数放在脚本文件中作为他的一部分
- 可以单独写在一个文件中功能模块化,使用的时候单独调用
交互式环境使用函数
交互式环境下别名的优先级高于函数,如果函数的名字与别名相同,那么优先运行别名。但是一般函数只在脚本中使用,而脚本中别名不生效,所以不用考虑这个问题。
declear -f 查看所有定义过的函数。 交互式环境下函数会一直保留到用户从系统中退出,或者unset funcname 清除。 调用:函数只有被调用才会执行 调用:给定函数名 函数名出现的地方,会被自动替换为函数代码。 函数的声明周期:被调用时创建,返回时终止函数的定义会影响当前shell环境,使用local 声明函数定义的内容只在函数内部生效当函数要定义一个数字时使用declare -i 声明要定义的函数是一个数字,声明的同时也会附带声明函数的本地效果,既只在函数内部生效。加参数-g再次声明为全局变量:declare -ig[root@CentOS6 ~]#name=wang[root@CentOS6 ~]#func1 () { name=mage; echo $name ; }[root@CentOS6 ~]#func1mage[root@CentOS6 ~]#echo $namemage[root@CentOS6 ~]#name=wang[root@CentOS6 ~]#func1 () { local name=mage; echo $name ; }[root@CentOS6 ~]#echo $namewang
在脚本中定义及使用函数
函数在使用前必须定义,因此应将函数定义放在脚本开始本分,直至shell首次发现它后才能使用。
调用函数仅仅使用其函数名即可。函数变量
变量作用域:
环境变量:当前shell和子shell有效 本地变量:只在当前shell进程有效,为执行脚本会启动 专用子shell进程;因此,本地变量的作用范围是当前shell脚本程序文件,包括脚本中的函数 局部变量:函数的生命周期;函数结束时变量被自动销毁 注意:如果函数中有局部变量,如果其名称同本地变量,使 用局部变量 在函数中定义局部变量的方法 local NAME=VALUE[root@CentOS6 bin]#vim testfunc.sh #!/bin/bashfunc1 () { echo "func1 is running" #定义函数执行echo命令 local funcvar="i am func1" #函数内定义本地变量 echo $funcvar #函数内显示本地变量}echo func1 will start...func1 #调用函数echo func1 is finishedecho "funcvar is $funcvar" #函数外显示变量"testfunc.sh" 20L, 500C written [root@CentOS6 bin]#testfunc.sh func1 will start...func1 is runningi am func1 #函数内显示函数本地变量func1 is finishedfuncvar is #函数外显示变量为空#函数中使用变量一般使用local关键字定义本地变量,否则容易影响脚本中的变量。
使用函数文件
可以将经常使用的函数存入函数文件,然后将函数文件载入shell,这样一些常用的函数就不用每次用到都要重新去写了。
函数文件的文件名可任意选取,但最后是与相关的任务有联系,例如:functions.main 一旦函数文件载入shell,就可以在命令行或者脚本中调用函数。可以使用set命令查看所有定义的函数,其输出列表包含已经载入shell的所有函数。 若要改动函数,首先用unset命令将函数从shll中删除。改动完毕后,在重新载入函数文件。创建函数文件:
函数文件示例:
[root@CentOS6 bin]#cat functions.main #!/bin/bash# Filename : functions.main#---------------+--------------------------------------+findit () { if [ $# -lt 1 ] ;then echo "Usage:findit file" return 1 fi find / -name $1 -print}
载入函数文件:
函数文件创建好之后,要将他载入shell中
定位函数文件,并载入shell的格式为: 点 空格 文件名:. filename 或 source 空格问价名:source filename文件名要带正确路径[root@CentOS6 ~]#. /etc/rc.d/init.d/functions #系统自带的函数[root@CentOS6 ~]#action "success"success [ OK ]
删除shell函数
现在对函数做一些改动后,需要先删除函数,使其对shell不可用, 使用unset命令完成删除函数
命令格式为: unset function_name[root@CentOS6 bin]#unset findit[root@CentOS6 bin]#findit-bash: findit: command not found
环境函数
当前进程导入的函数不能传给子进程
声明函数为环境函数使子进程也可以使用 声明:export –f function_name 查看:export -f 或 declare -xf函数参数
函数可以接受参数:
传递参数给函数:调用函数时在函数名后面以空白分隔给定参数列表即可:例如“testfunc arg1 arg2 ...” 在函数体中当中,可使用$1, $2, ...调用这些参数;还 可以使用$@, $*, $#等特殊变量[root@CentOS6 bin]#vim testfunc2.sh #!/bin/bashfunc1 () { echo "func1 1st var is $1" echo "func1 2st var is $2" echo "func1 3st var is $3" echo "the func1var is $*"}func1 func{$1,$2,$3}echo "the scriptvar is $*"[root@CentOS6 bin]#testfunc2.sh x y zfunc1 1st var is funcxfunc1 2st var is funcyfunc1 3st var is funczthe func1var is funcx funcy funczthe scriptvar is x y z
函数递归示例
函数递归:
函数直接或间接调用自身 注意递归层数 一般循环可以解决的问题不用递归 递归实例: 阶乘是基斯顿·卡曼于 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)![root@CentOS6 bin]#vim n\!.sh #!/bin/bashfact () { if [ $1 -eq 1 -o $1 -eq 0 ] ; then echo 1 #1和0的阶乘都是1 else echo $[$1*`fact $[$1-1]`] fi }fact $1
fork炸弹
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: 关联数组 注意:关联数组和一般数组之间不可相互转换数组元素的赋值:
- 一次只赋值一个元素 ARRAY_NAME[INDEX]=VALUE weekdays[0]="Sunday" weekdays[4]="Thursday" #双引号可以不加,但是如果元素中含有空格需要双引号
- 一次赋值全部元素 ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)
[root@CentOS6 ~]#title=(boss ceo coo cto opt)[root@CentOS6 ~]#echo ${title[*]}boss ceo coo cto opt
数组的复制支持列表格式和seq命令
- 只赋值特定元素 (稀疏格式) ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
- 交互式数组值对赋值 read -a ARRAY显示所有数组:declare -a
引用数组
引用数组元素:
${ARRAY_NAME[INDEX]} 注意:省略[INDEX]表示引用下标为0的元素 引用数组所有元素: ${ARRAY_NAME[*]} ${ARRAY_NAME[@]} 数组的长度(数组中元素的个数): ${#ARRAY_NAME[*]} ${#ARRAY_NAME[@]}数组元素个数减一等于最末端数组元素下标 遍历数组:[root@CentOS6 ~]#for i in `seq 0 $[${#title[*]}-1]` ;do echo title[$i]=${title[$i]}; donetitle[0]=bosstitle[1]=ceotitle[2]=cootitle[3]=ctotitle[4]=opt
删除数组中的某元素:导致稀疏格式
unset ARRAY[INDEX] 删除整个数组: unset ARRAY数组处理
引用数组中的元素
数组切片: ${ARRAY[@]:offset:number}
offset:要跳过的元素个数 number:要取出的元素个数 取偏移量之后的所有元素: ${ARRAU[@]:offset}向元素中追加元素
元素个数减一,是数组中最末端的元素的下标,所以在确保数组不是稀疏格式的前提下,要想数组中追加的元素的下标等于当前数组的数量。
ARRY[${#ARRY[*]}]=vale关联数组
declare -A ARRAY_NAME
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...) 注意:关联数组必须先声明再调用练习
- 生成十个随机数保存在数组中,并找出其最大值和最小值
[root@CentOS6 bin]#vim max.sh #!/bin/bashdeclare -i min max #声明max min都是数值declare -a digit #声明数组for ((i=0;i<10;i++));do #引入变量I初始值0,循环十次,每次循环以后自增1 digit[$i]=$RANDOM #取随机数赋值给数组元素 [ $i -eq 0 ] && max=${digit[$i]} && min=${digit[$i]} && continue #每次循环都给最大值和最小值重新赋值,第一次循环最大最小值都是digit[i]并且continue跳过本次循环 [ ${digit[$i]} -gt $max ] && max=${digit[$i]} #如果digit[$i]大于当前最大值$max时,就将digit[$i]重新付给max [ ${digit[$i]} -lt $min ] && min=${digit[$i]} #如果digit[$i]小于当前最小值$max时,就将digit[$i]重新付给mindoneecho ALL digit are ${digit[*]}echo Max is $maxecho Min is $min[root@CentOS6 bin]#max.sh ALL digit are 22019 3896 15981 5024 6535 4256 14632 31882 395 1400Max is 31882Min is 395
- 编写脚本,定义一个数组,数组的元素是/var/log目录下所有以.log结尾的文件;统计其下标为偶数的文件中的行数之和
[root@CentOS6 bin]#vim linesum.sh #!/bin/bashdeclare -a logfiles=(/var/log/*.log) #定义数组#echo All logfile are ${logfiles[@]}i=0linesum=0filelines=0while [ $i -lt ${#logfiles[@]} ] ; do #循环次数为数组元素个数 if [ $[$i%2] -eq 0 ] ;then #判断数组下标为偶数通过条件判断 filelines=`cat /${logfiles[$i]} | wc -l` #将下标为偶数的文件行数赋值给变量filelines let linesum+=$filelines #加总 echo ${logfiles[$i]} filelines is $filelines filet i++ #变量i也就是数组元素下标自增doneecho linesum is $linesum [root@CentOS6 bin]#linesum.sh /var/log/anaconda.ifcfg.log filelines is 219... #省略/var/log/Xorg.9.log filelines is 309linesum is 4194
字符串切片
${#var}:返回字符串变量var的长度
${var:offset}:返回字符串变量var中从第offset个字符后(不包括 第offset个字符)的字符开始,到最后的部分,offset的取值在0 到 ${#var}-1 之间(bash4.2后,允许为负值) ${var:offset:number}:返回字符串变量var中从第offset个字符后 (不包括第offset个字符)的字符开始,长度为number的部分 ${var: -length}:取字符串的最右侧几个字符 注意:冒号后必须有一空白字符 ${var:offset:-length}:从最左侧跳过offset字符,一直向右取到 距离最右侧lengh个字符之前的内容 ${var: -length:-offset}:先从最右侧向左取到length个字符开始 ,再向右取到距离最右侧offset个字符之间的内容注意:-length前空格基于模式取子串
${var#*word}:其中word可以是指定的任意字符
功能:自左而右,查找var变量所存储的字符串中,第一 次出现的word, 删除字符串开头至第一次出现word字符之间的 所有字符 ${var##*word}:同上,贪婪模式,不同的是,删除的 是字符串开头至最后一次由word指定的字符之间的所有内容 示例:[root@CentOS6]#file=var/log/messages[root@CentOS6]#echo ${file#*/}log/messages[root@CentOS6]#echo ${file##*/}messages
${var%word*}:其中word可以是指定的任意字符;
功能:自右而左,查找var变量所存储的字符串中,第一 次出现的word, 删除字符串最后一个字符向左至第一次出现 word字符之间的所有字符; file="/var/log/messages" ${file%/*}: /var/log ${var%%word*}:同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符; 示例:[root@CentOS6 ~]#url=http://www.badu.com:80[root@CentOS6 ~]#echo ${url##*:}80[root@CentOS6 ~]#echo ${url%%:*} #:*顺序有变化http
查找替换
${var/pattern/substr}:查找var所表示的字符串中,第 一次被pattern所匹配到的字符串,以substr替换之
${var//pattern/substr}: 查找var所表示的字符串中, 所有能被pattern所匹配到的字符串,以substr替换之 ${var/#pattern/substr}:查找var所表示的字符串中, 行首被pattern所匹配到的字符串,以substr替换之 ${var/%pattern/substr}:查找var所表示的字符串中, 行尾被pattern所匹配到的字符串,以substr替换之查找删除
${var/pattern}:删除var所表示的字符串中第一次被 pattern所匹配到的字符串
${var//pattern}:删除var所表示的字符串中所有被 pattern所匹配到的字符串 ${var/#pattern}:删除var所表示的字符串中所有以 pattern为行首所匹配到的字符串 ${var/%pattern}:删除var所表示的字符串中所有以 pattern为行尾所匹配到的字符串大小写转换
${var^^}:把var中的所有小写字母转换为大写
${var,,}:把var中的所有大写字母转换为小写高级变量用法-有类型变量
shell变量一般是无类型的,但是bash shell提供了declare和typest两个命令用于指定变量的类型,两个命令是等价的
declare [选项] 变量名 -r 声明或显示只读变量 -i 将变量定义为整型数 -a 将变量定义为数组 -A 将变量定义为关联数组 -f 显示已定义的所有函数名及其内容 -F 仅显示已定义的所有函数名 -x 声明或显示环境变量和函数 -l 声明变量为小写字母 declare –l var=UPPER -u 声明变量为大写字母 declare –u var=lowereval命令
eval命令将会首先扫描命令行进行所有的置换,让你后在执行该敏玲。该命令适用于那些一次扫描无法实现其功能的变量。该命令对变量进行两次扫描
示例:[root@CentOS6 ~]#CMD=whoami[root@CentOS6 ~]#echo $CMDwhoami[root@CentOS6 ~]#eval $CMDroot[root@CentOS6 ~]#n=10[root@CentOS6 ~]#echo {0..$n}{0..10}[root@CentOS6 ~]#eval echo {0..$n}0 1 2 3 4 5 6 7 8 9 10
间接变量引用
如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量应用。
variable1的值是variable2,而variable2又是变量名, variable2的值为value,间接变量引用是指通过variable1获 得变量值value的行为 variable1=variable2 variable2=value bash shell提供了两种格式实现间接变量引用 eval tempvar=$$variable1 tempvar=${!variable1}创建临时文件
mktemp命令:创建并显示临时文件,可避免冲突
mktemp [OPTION]... [TEMPLATE] TEMPLATE: filename.XXX X至少要出现三个[root@CentOS6 app]#mktemp /app/tmpfile.XXX/app/tmpfile.8Tw #文件后缀必须是大写X有多少个X就会生成多少个随机字符#赋值给变量直接引用[root@CentOS6 app]#tmpfile=`mktemp /app/tmpfile.XXX`[root@CentOS6 app]#echo $tmpfile/app/tmpfile.2AO将指定目录赋值给变量直接引用[root@CentOS6 app]#dir=/app[root@CentOS6 ~]#mktemp -p $dir testfile.XXX/app/testfile.wzQ
OPTION:
-d: 创建临时目录 -p DIR或--tmpdir=DIR:指明临时文件所存放目录位置 示例: mktemp /tmp/test.XXX tmpdir=`mktemp –d /tmp/testdir.XXX` mktemp --tmpdir=/testdir test.XXXXXX安装复制文件
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 755 –d /testdir/installdir 若有疑问以及错误指正请留言吧。