分享

shell脚本-函数、数组、括号

 夜猫速读 2022-05-17 发布于湖北

一、函数

1.概述Shell函数类似于Shell脚本,里面存放了一系列的指令,不过Shell的函数存在于内存,而不是硬盘文件,所以速度很快,另外,Shell还能对函数进行预处理,所以函数的启动比脚本更快。shell允许将一组命令集或语句形成一个可用块,这些块称为shell函数。

2.语法:

 function 函数名() {

    语句

    [return]

}

解析:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。函数中的关键字“return”可以放到函数体的任意位置,通常用于返回某些值,Shell在执行到return之后,就停止往下执行,返回到主程序的调用行,return的返回值只能是0~256之间的一个整数,返回值将保存到变量“$?”中。

3.shell函数的退出及删除:

函数结束之后会返回调用函数的部分继续执行,

退出函数体:exit退出整个脚本、break语句来中断函数的执行。

shell中查询函数及删除:

source或. 脚本          ##函数载入到内存

declare -f                         ##可以显示定义的函数内容

declare -F                         ##可以只显示定义的函数名

unset -f                            ##可以从Shell内存中删除函数

4.变量
全局变量:默认情况下,脚本中定义的任何变量都是全局变量,在函数外定义的变量可在函数内正常访问。
局部变量:函数内部使用的任何变量都可以用“local 变量名=值”声明成局部变量,局部变量只能在函数体内生效。

5.案例:

案例一:函数的基本使用

[root@localhost ~]# vi func_linuxfan.sh

#!/bin/bash

read -p "输入第一个数:" ANum

function linuxfan(){

  echo "这个函数的功能是两个数进行相加:"

  read -p "输入第二个数:" BNum

  echo "您输入的数是$ANum和$BNum! "

  SUM=$(expr $ANum + $BNum)

  echo "两个数相加的结果是:$SUM"

  local A="哈哈!"

  echo "\$A是使用了local声明的局部变量,在函数体内情况是:$A"     

  echo "\$ANum是在函数体外的变量,函数体内调用它的情况是:$ANum"

  return 0    ##设置返回值

}

linuxfan   ##调用函数

echo "\$A 在函数体外,结果是这样的:$A"

echo "\$BNum 在函数体外,结果是这样的:$BNum"

:wq

[root@localhost ~]# chmod +x func_linuxfan.sh

[root@localhost ~]# ./func_linuxfan.sh

输入第一个数:123

这个函数的功能是两个数进行相加:

输入第二个数:456

您输入的数是123和456!

两个数相加的结果是:579

$A是使用了local声明的局部变量,在函数体内情况是:哈哈

$ANum是在函数体外的变量,函数体内调用它的情况是:123

$A 在函数体外,结果是这样的:

$B 在函数体外,结果是这样的:

[root@localhost ~]# source func_linuxfan.sh    ##从脚本文件中载入函数

[root@localhost bin]# declare -f                       ##显示当前shell中函数内容

linuxfan ()

{

   省略函数体内的内容。

}

[root@localhost bin]# declare -F                       ##查看当前shell中函数名称

declare -f linuxfan

[root@localhost bin]# unset -f linuxfan                 ##删除当前shell中的函数

[root@localhost bin]# declare -F                        ##查看验证

案例二:函数参数的传递

函数可以通过位置变量传递参数。

函数名 参数1 参数2 参数3 参数4 ...

[root@localhost ~]# vi fun-paramters.sh

#!/bin/bash

funparam(){

  echo "\$1可以给函数传递第一个参数,函数的第一个参数:$1"

  echo "\$2可以给函数传递第二个参数,函数的第二个参数:$2"

  echo "\$7可以给函数传递第七个参数,函数的第七个参数:$7"

  echo "\${10}可以给函数传递第十个参数,函数的第十个参数:${10}"

  echo "\${11}可以给函数传递第十一个参数,函数的第十一个参数:${11}"

  echo "函数有$#个参数,函数参数具体内容是$*"

}

funparam 1 2 3 4 5 6 11 8 9 66 99

echo "脚本后的参数\$1是$1;"

echo "脚本后的参数\$2是$2;"

echo "脚本后的参数\$3是$3;"

echo "脚本后的参数\$4是$4;"

echo "脚本后的参数\$5是$5;"

:wq

[root@localhost ~]# chmod +x fun-paramters.sh 

[root@localhost ~]# ./fun-paramters.sh a b c d f e

$1可以给函数传递第一个参数,函数的第一个参数:1

$2可以给函数传递第一个参数,函数的第一个参数:2

$7可以给函数传递第一个参数,函数的第一个参数:11

${10}可以给函数传递第一个参数,函数的第一个参数:66

${11}可以给函数传递第一个参数,函数的第一个参数:99

函数有11个参数,函数参数具体内容是1 2 3 4 5 6 11 8 9 66 99

脚本后的参数\$1是a;

脚本后的参数\$1是b;

脚本后的参数\$1是c;

脚本后的参数\$1是d;

脚本后的参数\$1是f;

[root@localhost ~]# cat test.sh                        ##将shell命令行中的参数传递给函数

#!/bin/bash

function test(){

echo "$1 $2 $3"

}

test $*

[root@localhost ~]# ./ test.sh a b c

a b c

案例三:

扩展学习(更多练习):

向系统学习函数的使用:

http://www.cnblogs.com/image-eye/archive/2011/10/26/2220405.html  ##请大家阅读/etc/init.d/funcations详解,至少搞懂daemon和killproc两个函数的作用。然后静下心来阅读这个脚本,并给它添加注释你能学会很多东西:

[root@localhost ~]# cat /etc/init.d/vsftpd                       ##这是vsftpd的启动脚本,非常经典

#!/bin/bash

#

### BEGIN INIT INFO

# Provides: vsftpd

# Required-Start: $local_fs $network $named $remote_fs $syslog

# Required-Stop: $local_fs $network $named $remote_fs $syslog

# Short-Description: Very Secure Ftp Daemon

# Description: vsftpd is a Very Secure FTP daemon. It was written completely from

#              scratch

### END INIT INFO

# vsftpd      This shell script takes care of starting and stopping

#             standalone vsftpd.

#

# chkconfig: - 60 50

# description: Vsftpd is a ftp daemon, which is the program \

#              that answers incoming ftp service requests.

# processname: vsftpd

# config: /etc/vsftpd/vsftpd.conf

# Source function library.

. /etc/rc.d/init.d/functions

# Source networking configuration.

. /etc/sysconfig/network

RETVAL=0

prog="vsftpd"

start() {

        # Start daemons.

       # Check that networking is up.

       [ ${NETWORKING} = "no" ] && exit 1

       [ -x /usr/sbin/vsftpd ] || exit 1

        if [ -d /etc/vsftpd ] ; then

                CONFS=`ls /etc/vsftpd/*.conf 2>/dev/null`

                [ -z "$CONFS" ] && exit 6

                PROC_FAILED=0

                for i in $CONFS; do

                        site=`basename $i .conf`

                        echo -n $"Starting $prog for $site: "

                        daemon /usr/sbin/vsftpd $i

                        RETVAL=$?

                        echo

                        if [ $RETVAL -eq 0 ] && [ ! -f /var/lock/subsys/$prog ]; then

                                touch /var/lock/subsys/$prog

                        elif [ $RETVAL -ne 0 ]; then

                                ps -FC vsftpd | grep "$i" > /dev/null

                                RETVAL=$?

                                if [ $PROC_FAILED -eq 0 ] && [ $RETVAL -ne 0 ]; then

                                        PROC_FAILED=1

                                fi

                        fi

                done

                if [ $RETVAL -eq 0 ] && [ $PROC_FAILED -ne 0 ]; then

                        RETVAL=1

                fi

        else

                RETVAL=1

        fi

        return $RETVAL

}

stop() {

        # Stop daemons.

        echo -n $"Shutting down $prog: "

        killproc $prog

        RETVAL=$?

        echo

        [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog

        return $RETVAL

}

# See how we were called.

case "$1" in

  start)

        start

        ;;

  stop)

        stop

        ;;

  restart|reload)

        stop

        start

        RETVAL=$?

        ;;

  condrestart|try-restart|force-reload)

        if [ -f /var/lock/subsys/$prog ]; then

            stop

            start

            RETVAL=$?

        fi

        ;;

  status)

        status $prog

        RETVAL=$?

        ;;

  *)

        echo $"Usage: $0 {start|stop|restart|try-restart|force-reload|status}"

        exit 1

esac

exit $RETVAL

案例四:测试主机访问url路径

二、shell数组:

1.概述:数组就是一组数据类型相同集合

2.数组的定义及使用:

[root@localhost bin]# arr1=()                              ##定义空元素数组

[root@localhost bin]# arr2=(1 2 3 4 5 6)                 ##定义数字元素的数组

[root@localhost bin]# arr3=("有梦想" "向往远方和诗" "高薪就业" "静心沉迷于学习!")                              ##定义字符串元素的数组,注意用引号(单、双引号皆可)

[root@localhost bin]# echo ${arr2[0]}

1

[root@localhost bin]# echo ${arr2[3]}

4

[root@localhost bin]# echo ${arr3[3]}

静心沉迷于学习!

[root@localhost bin]# echo ${arr3[*]}

有梦想 向往远方和诗 高薪就业 静心沉迷于学习!

[root@localhost bin]# for i in ${arr3[*]};do echo $i;done

有梦想

向往远方和诗

高薪就业

静心沉迷于学习!

[root@localhost bin]# echo ${#arr3[*]}                ##获取数组的长度

4

[root@localhost bin]# echo ${#arr3[@]}

4

数组赋值格式:数组名[下标]=值,如果下标不存在,则新增数组元素; 下标已有,则覆盖值。

[root@localhost bin]# echo ${arr2[*]}                         ##获取数组所有元素

1 2 3 4 5 6

[root@localhost bin]# arr2[6]=8                              ##下标为6(第七个)的内容为8,添加元素

[root@localhost bin]# echo ${arr2[*]}                 ##验证

1 2 3 4 5 6 8

[root@localhost bin]# arr2[2]=8                                ##修改下标为2的元素为8,覆盖原有值

[root@localhost bin]# echo ${arr2[*]}                       ##验证

1 2 8 4 5 6 8

数组分片的格式:${数组名[*或@]:起始位:长度},截取部分数组,返回字符串,中间用空格分隔;将结果使用“()”,则得到新的切片数组。

[root@localhost bin]# echo ${#arr3[*]}

4

[root@localhost bin]# echo ${arr3[*]:0:1}

有梦想

[root@localhost bin]# echo ${arr3[*]:2:2}

高薪就业 静心沉迷于学习!

[root@localhost bin]# arr4=(${arr3[*]:2:2})

[root@localhost bin]# echo ${arr4[*]}

高薪就业 静心沉迷于学习!

数组替换元素的格式:${数组名[*或@]/查找字符/替换字符}, 不会修改原数组;如需修改的数组,将结果使用“()”赋给新数组。

[root@localhost bin]# echo ${arr2[*]}

1 2 8 4 5 6 8

[root@localhost bin]# echo ${arr2[*]/4/9}

1 2 8 9 5 6 8

[root@localhost bin]# arr5=${arr2[*]/4/9}

[root@localhost bin]# echo ${arr5[*]}

1 2 8 9 5 6 8

删除数组的格式:unset 数组,清除整个数组; unset 数组[下标],清除单个元素。

[root@localhost bin]# echo ${arr3[*]}

有梦想 向往远方和诗 高薪就业 静心沉迷于学习!

[root@localhost bin]# unset arr3[0]

[root@localhost bin]# echo ${arr3[*]}

向往远方和诗 高薪就业 静心沉迷于学习!

[root@localhost bin]# unset arr3

[root@localhost bin]# echo ${arr3[*]}

三、shell中括号的使用:

1.单小括号():

命令组:组合多条命令一起执行,并按照顺序执行。

[root@www ~]# (umask 0077;mkdir -p test;ls -ld test)

替换命令:效果等于反撇,在命令中执行命令,并将执行结果交给命令处理。

[root@www ~]# rpm -qf $(which convert)   ##查询命令的安装包

ImageMagick-6.5.4.7-6.el6_2.x86_64  

用于初始化数组:如array=(a b c d)

[root@www ~]# array=(a b c d) 

2.双小括号(())

计算其他进制(二、八、十六)的数到十进制:

[root@www ~]# echo $((2#11))                              ##二转十

3

[root@www ~]# echo $((8#11))                             ##八转十

9

[root@www ~]# echo $((16#11))                             ##十六转十

17

[root@www ~]# echo $((16#1f))

31

[root@localhost bin]# echo $((3+2))                       ##加减乘除取摸运算

5

[root@localhost bin]# echo $((3-2))

1

[root@localhost bin]# echo $((3*2))

6

[root@localhost bin]# echo $((3/2))

1

[root@localhost bin]# echo $((3%2))

1

重新定义变量:

[root@www ~]# a=5;((a++));echo $a

6

算术运算比较,双括号内的变量可以不使用$,表达式用分号分开:

[root@www ~]# for i in {0..4};do echo $i;done

[root@www ~]# for i in $(seq 0 4);do echo $i;done

[root@www ~]# for ((i=0;i<5;i++));do echo $i;done  ##上述三种都是一样的效果

[root@www ~]# i=10

[root@www ~]# if ((i>5));then echo $i;fi

[root@www ~]# if [ $i -gt 5 ];then echo $i;fi  ##两个if的效果相同

3.括号[  ]

条件表达式

[root@www ~]# [ -f /etc/hosts ]&&echo ok

[root@www ~]# test -f /etc/hosts &&echo ok

字符范围。用作正则表达式的一部分,描述一个匹配的字符范围。

[root@www ~]# i=1                      ##case中使用的[0-9]|[a-z]|[A-Z]表示正则

[root@www ~]# case $i in [0-9]) echo "number";  ;; [a-z]|[A-Z]) echo "alph";  ;; esac

4.双中括号[[  ]]

①[[是 bash 程序语言的关键字。并不是一个命令,[[ ]] 结构比[ ]结构更加通用。在[[和]]之间所有的字符都不会发生文件名扩展或者单词分割,但是会发生参数扩展和命令替换。

②支持字符串的模式匹配,使用=~操作符时甚至支持shell的正则表达式。字符串比较时可以把右边的作为一个模式,而不仅仅是一个字符串,比如[[ hello == hell? ]],结果为真。[[ ]] 中匹配字符串或通配符,不需要引号。 

③使用[[ ... ]]条件判断结构,而不是[ ... ],能够防止脚本中的许多逻辑错误。比如,&&、||、<和> 操作符能够正常存在于[[ ]]条件判断结构中,但是如果出现在[ ]结构中的话,会报错。比如可以直接使用if [[ $a != 1 && $a != 2 ]], 如果不使用双括号, 则为if [ $a -ne 1] && [ $a != 2 ]或者if [ $a -ne 1 -a $a != 2 ]。

[root@www ~]# a=3

[root@www ~]# if [ $a != 1 && $a != 2 ];then echo $a;fi    ##报错

-bash: [: missing `]'

[root@www ~]# if [[ $a != 1 && $a != 2 ]];then echo $a;fi   ##成功执行

3

[root@www ~]# if [ $a != 1 ] && [ $a != 2 ];then echo $a;fi    ##执行成功

3

5.花括号{  }

常规用法:

[root@www ~]# touch {a..z}.mp{3..5}                             ##..表示分割顺序文件列表

[root@www ~]# ls {{a..e},h,y,z}.mp4 

a.mp4  b.mp4  c.mp4  d.mp4  e.mp4  h.mp4  y.mp4  z.mp4

定义函数:代码块,又被称为内部组,这个结构事实上创建了一个匿名函数 。与小括号中的命令不同,括号内的命令不会新开一个子shell运行,即脚本余下部分仍可使用括号内变量。

字符串提取和替换

${var:num},${var:num1:num2},${var/pattern/pattern},${var//pattern/pattern}

第一种模式:${var:num},这种模式时,shell在var中提取第num个字符到末尾的所有字符。若num为正数,从左边0处开始;若num为负数,从右边开始提取字串,但必须使用在冒号后面加空格或一个数字或整个num加上括号,如${var: -2}、${var:1-3}或${+var:(-2)}。       

[root@www ~]# var=www.linuxfan.cn

[root@www ~]# echo ${var:4}

linuxfan.cn

[root@www ~]# echo ${var:(-2)}

cn

第二种模式:${var:num1:num2},num1是位置,num2是长度。表示从$var字符串的第$num1个位置开始提取长度为$num2的子串。不能为负数。

[root@www ~]# var=www.linuxfan.cn

[root@www ~]# echo ${var:4:5}

linux

[root@www ~]# echo ${var:1:3}

ww.

[root@www ~]# echo ${var:0:3}

www   

第三种模式:${var/pattern/pattern}表示将var字符串的第一个匹配的pattern替换为另一个pattern。。       

[root@www ~]# var=www.linuxfan.cn

[root@www ~]# echo ${var/www/dns}

dns.linuxfan.cn

第四种模式:${var//pattern/pattern}表示将var字符串中的所有能匹配的pattern替换为另一个pattern。

[root@www ~]# var=www.linuxfan.cn

[root@www ~]# echo ${var/n/N}

www.liNuxfan.cn

[root@www ~]# echo ${var//n/N}

www.liNuxfaN.cN

6.多条命令执行

单小括号:(cmd1;cmd2;cmd3)新开一个子shell顺序执行命令cmd1,cmd2,cmd3, 各命令之间用分号隔开, 最后一个命令后可以没有分号。

[root@www tmp]# (touch index.html;rm -rf index.html;ls -l;)

单大括号:{ cmd1;cmd2;cmd3;} 在当前shell顺序执行命令cmd1,cmd2,cmd3, 各命令之间用分号隔开, 最后一个命令后必须有分号, 括号两侧必须有空格

[root@www tmp]# {touch index.html;rm -rf index.html;ls -l;}  ##开始的{后无空格报错

-bash: syntax error near unexpected token `}'

[root@www tmp]# { touch index.html;rm -rf index.html;ls -l  }

[root@www tmp]# { touch index.html;rm -rf index.html;ls -l;  }  ##最后一条命令必须;

总用量 0

注:对{}和()而言, 括号中的重定向符只影响该条命令,而括号外的重定向符影响到括号中的所有命令。

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多