首页 > 代码库 > bash脚本编程进阶篇

bash脚本编程进阶篇

  bash脚本编程进阶篇

  函数、数组、字符串操作


    一.函数:脚本编程中的函数与我们数学中的函数有着根本区别。这里的函数主要是为了实现过程式编程代码重用的作用。比如一个用于计算数字的函数,当我们需要使用计算数字的时候,直接调用这个函数过来而不必每一次计算数字都要自己重新写一次。因此,函数的主要功能可以概括为:便于实现模块化编程;便于代码的重用;使程序简洁。我们定义函数,可以分两种两种结构。 第一种函数结构,用function声明一个函数,后跟函数名。函数体用花括号括起来。第二种结构是用函数名跟小括号(),依然是把函数体写在花括号中,两种任选其一。

function f_name {
函数体
}

或者
f_name() {
函数体
}

    了解了函数的结构,看一下我们具体如何使用函数。函数本身是为了实现某种功能的功能模块。因此在写函数体时,只要能完整实现特定功能;且能满足多方调用即可。我这里给个例子比如:我们要用函数实现如下功能,脚本运行时输出一段提示,要求用户给个用户名。如果用户不给用户名,错误提示返回值为1,如果给了一个错误的用户名,也给出错误提示。继续让用户出入用户名。如果用户给予一个用户名正确,就输出该用户的shell类型,用户输入quit可以实现退出。

#!/bin/bash
#
showuserinfo() {
    [ $# -lt 1 ] && return 1
    ! id $1 &> /dev/null && return 2
    grep "^$1\>" /etc/passwd | cut -d: -f7
    [ $? -eq 0 ] && return 0 || return 3
}
while true; do
    read -p "Enter a username: " username
    [ "$username" == ‘quit‘ ] && break
    showuserinfo $username
    [ $? -ne 0 ] && echo "There is something wrong."
done

    这里详细分析一下这个例子:1.函数中出现了return 1,3之类,这些其实是函数的返回值。他反应的是函数的执行结果。另外函数也有退出状态码,函数的退出状态码与脚本的退出状态码是相同的选取方法。都是取各自最后一个语句的执行状态结果(注意是执行状态结果)。我们也可以自定义。形如我们相面的例子所写return #.这里#要求在[0-255]之间。函数体在执行过程中,一旦遇到return就会停止运行。2.我们这里用于显示用户shell类型的的函数,用户名是由脚本执行时交互式输入的。很显然函数也是可以实现传递参数的。$1,$#,$*等变量要熟练掌握哦。3.这里函数的调用看到案例,就是直接使用函数名即可实现调用。

    注意:我们在函数中使用变量时,要注意声明本地变量。local VAR=VALUE ,不然在函数体中如果函数变化,会引起其他同名函数的数值变化。

    函数的递归调用,递归调用实际就是函数自身调用自身。这么说起好像有点抽象。想象一下数学里的阶乘表达式。10! n(n-1)! n(n-1)(n-2)! ... 这表达式就是最典型的递归。我们下面来看一下,如何用函数的递归调用来实现。

fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
   echo $[$1*$(fact $[$1-1])]
fi
}

    注意:函数的递归调用对于计算机而言是运算效率是相当低的,因为他是把所有的运行结果先存储在内存中,直到运行到最后一个表达式,再从头输出数据。

    函数的数组:函数的数组可以理解是连续多个的独立内存空间,每个内存空间其实就相当于一个比变量。这些所谓的比变量我们称之为函数的数组元素。

    二.数组元素的组成:数组名+索引号的(索引号是从零编号的)例如:ARRAY[0]="hello" 这里的数组名就是ARRAY,[0]即表示索引号。

    数组的用法:首先要先声明一个数组,declare -a ARRAR_NAME。这是很关键不可省略的,因为如果不声明。因为如果不声明,函数就会把这里当做一个变量来处理了。数组既然是一个连续的空间存储单元,他就需要赋值。数组的赋值方式多种:

(1) 一次只赋值一个元素
ARRAY[index]=VALUE
a[0]="abc"
(2) 一次赋值全部元素
ARRAY=("mon" "tue" "wed")
(3) 指定索引进行赋值
ARRAY=([0]="sun" [1]="mon" [5]="fri")
(4) read -a ARRAY

        赋值实现之后,那么即正常调用了。引用数组元素,${数组元素}=${arrsy[0]}。这是对数组中具体某个数组元素的调用。数组的长度我们是使用:${#ARRAY[*]}或者来实现的。来个例子:定义一个数组,数组元素为/var/log目录下,所有以.log结尾的文件的名字;而后显示其索引为奇数的元素的内容;

#!/bin/bash
#
declare -a files
files=(/var/log/*.log)
for i in `seq 0 $[${#files[*]}-1]`; do
    [ $[$i%2] -ne 0 ] && echo "$i: ${files[$i]}"
done

    数组的元素的相关处理问题:

        1.挑选一个与元素:${ARRAY[i]} 

        2.挑选某几个连续的元素:${ARRAY[@]:offset:number}  这里offset指从数组开始偏移的第几个数,number指的是从索引号开始取的个数。

        3.挑选某元素开始到最后的所有元素:${ARRAY[@]:offset} 这就表示从指定索引号开始之后的所有元素都取出。

        4.使用所有元素:${ARRAY[@]}

        5.从数组中追加一个元素: ARRAY[${#ARRAY[@]}]

        6.从数组中删除一个元素: unset ARRAY[i]    

    关联数组:关联数组与数组的不同有声明时为-A;而且可以自定义任意字符串当作索引比如:

declare -A week

week=([mon]="Monday" [tue]="Tuesday")此时索引不再是数字了。

    三.字符串操作:

    我们知道对于文本而言可以使用诸多相sed等工具找出自己想要的行或者一个单词。这种方式非常方便,于是对于字符串我们也有相似的处理方式。

    字符串的切片:

字符串切片格式:${var:offset:lenth} 这里格式很像数组的选取,只是此处的var不是数组而是代表一个变量比如:a="hello,world", ${a:5:2} 结果为:,w 。

    取字符串最后的几个字符:${var: -lenth} 例如:a="hello,world", ${a: -2} 结果就是ld。注意:冒号之后有空格;

    基于模式取字符串:

    1.${var#*word}:其中word可以是指定的任意自己想要查找的字符。含义:自左而右,删除到查找var变量所存储字符中,第一次出现的word的地方。例如:

# mypath=‘sysconfig/network-scripts/ifcfg-eth0‘
# echo ${mypath#*/}
network-scripts/ifcfg-eth0

    2.${var##*word}: 其中word可以是指定的任意字符;含义:自左而右,删除var变量所存储字符中,直到最后一次出现的word。

# mypath=‘/sysconfig/network-scripts/ifcfg-eth0‘
# echo ${mypath##*/}
ifcfg-eth0    
          

    3.${var%word*}: 自右而左,删除第一次word出现处的字符开始直到尾部的所有字符;

     

# mypath=‘/sysconfig/network-scripts/ifcfg-eth0‘
# echo ${mypath%/*}
/sysconfig/network-scripts
          

    4.${var%%word*}:自右而左,删除最后一次word出现处的字符开始直到尾部的所有字符;

理解记忆:这里可以这么记忆,#表示从左至右,所以*要放在word左边表示要删除的部分,1个#就表示第一次匹配到的地方,##就表示最后一次匹配到的地方。%表示从右向左,所以*要放在word右边表示要被删除的部分,同样的第一个%表示第一次被匹配到的地方,%%表示最后一次被匹配到的地方。

    查找并替换:

    ${var/pattern/substi}:查找var所表示的字串中,第一次被Pattern匹配到的字串,并以substi替换之;

    例如:

# mypath=‘/sysconfig/network-scripts/ifcfg-eth0‘
# echo ${mypath/\/sysconfig/etc}
 etc/network-scripts

    ${var//patten/substi}:查找var所表示的字串中,所有被Pattern匹配到的字串,并以substi替换之;

    

    ${var/#pattern/substi}:以行首锚定的方式将pattern匹配至var所表示的字串上,如果能匹配,则以substi替换之;

    

# mypath=‘/sysconfig/network-scripts/ifcfg-eth0‘
# echo ${mypath/#\/sysconfig/etc}
 etc/network-scripts

    ${var/%pattern/substi}:以行尾锚定的方式将pattern匹配至var所表示的字串上,如果能匹配,则以substi替换之;

    这里的模式可使用?, *元字符;

    查找并删除:

    ${var/pattern}:删除pattern匹配到的第一次出现;

    ${var//pattern}: 删除pattern匹配到的所有出现;

    ${var/#pattern}:删除pattern匹配到在行首的地方

    ${var/%pattern}:删除pattern匹配到在行尾的地方

    这里可以和上一个替换加在一起记忆,理解为替换为空,那么就是删除了。

    字符串大小写转换:

    ${var^^}:小写-->大写    

 mypath=‘/sysconfig/network-scripts/ifcfg-eth0‘
     echo ${mypath^^}
    /SYSCONFIG/NETWORK-SCRIPTS/IFCFG-ETH0

 

    ${var,,}:大写-->小写    

    变量赋值:

    ${var:-word}: 如果var为空或未设置,那么返回word;否则,则返回var中的值;

    ${var:=word}:如果var为空或未设置,那么返回word,并且将word赋值给var;否则,返回var中的值;

    ${var:?err_info}:如果var为空或未设置,那么返回错误信息;否则,则返回var自身的值;

    ${var:+word}:如果var自身有正常数据,则返回word;




























本文出自 “我和Linux的那些年” 博客,请务必保留此出处http://guanqianjian.blog.51cto.com/9652236/1587052

bash脚本编程进阶篇