linux之基础shell脚本编程4 字符串操作,变量赋值,配置用户环境
本章主要写shell有关函数,数组使用
函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程。
它与shell程序形式上是相似的,不同的是它不是一个单独的 进程,不能独立运行,而是shell程序的一部分。
函数和shell程序比较相似,区别在于: Shell程序在子Shell中运行,而Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改
函数由两部分组成:函数名和函数体。
- #语法一:
- function f_name {
- ...函数体...
- }
- #语法二:
- function f_name () {
- ...函数体...
- }
- #语法三:
- f_name (){
- ...函数体...
- }
可在交互式环境下定义函数
可将函数放在脚本文件中作为它的一部分
可放在只包含函数的单独文件中
调用:函数只有被调用才会执行;
调用:给定函数名,函数名出现的地方,会被自动替换为函数代码
函数的生命周期:被调用时创建,返回时终止
函数一直保留到用户从系统退出,或执行了 unset name 命令
函数的执行结果返回值:
(1) 使用echo或printf命令进行输出
(2) 函数体中调用命令的输出结果
函数的退出状态码:
(1) 默认取决于函数中执行的最后一条命令的退出状态码
(2) 自定义退出状态码,其格式为:
return 从函数中返回,用最后状态命令决定返回值
return 0 无错误返回。
return 1-255 有错误返回
函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它后才能使用
调用函数仅使用其函数名即可
比如:
- #!/bin/bash
- hello(){
- echo "hello to linux"
- }
- hello #直接调用函数
可以将经常使用的函数存入函数文件,然后将函数文件载入shell。
文件名可任意选取,但最好与相关任务有某种联系。例如:functions.main
一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用set命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数。
若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件。
- [root@centos7 test]# vim functions.main
- #!/bin/bash
- #functions.main
- #author nineven
- findit(){
- if [ $# -lt 1 ] ; then
- echo "Usage:findit file"
- return 1
- fi
- find / -name $1 -print
- }
函数文件已创建好后,要将它载入shell
定位函数文件并载入shell的格式: . filename 或 source filename
注意:此即<点> <空格> <文件名> 这里的文件名要带正确路径
比如:上例中的函数,可使用如下命令:
[root@centos7 test]# . ./functions.main
或
[root@centos7 test]# source ./functions.main
使用set命令检查函数是否已载入
set命令将在shell中显示所有的载入函数
比如:
要执行函数,简单地键入函数名即可
- [root@centos7 test]# findit te.sh
- /testdir/shell/test/te.sh
- /root/Desktop/tmp/testdir/shell/test/te.sh
- /root/te.sh
- /tmp/testdir/shell/test/te.sh
- [root@centos7 test]#
删除函数,使其对shell不可用 。使用unset命令完成此功能.
命令格式为: unset function_name
比如: $unset findit 再键入set命令,函数将不再显示
- [root@centos7 test]# findit te.sh
- /testdir/shell/test/te.sh
- /root/Desktop/tmp/testdir/shell/test/te.sh
- /root/te.sh
- /tmp/testdir/shell/test/te.sh
- [root@centos7 test]#
- [root@centos7 test]# unset findit
- [root@centos7 test]# findit te.sh
- bash: findit: command not found...
- [root@centos7 test]#
函数可以接受参数,传递参数给函数,调用函数时,在函数名后面以空白分隔给定参数列表即可;例如“func 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
1、写一个服务脚本/root/bin/testsrv.sh,完成如下要求
(1) 脚本可接受参数:start, stop, restart, status
(2) 如果参数非此四者之一,提示使用格式后报错退出
(3) 如是start:则创建/var/lock/subsys/SCRIPT_NAME, 并显示“启动成功” 考虑:如果事先已经启动过一次,该如何处理?
(4) 如是stop:则删除/var/lock/subsys/SCRIPT_NAME, 并显示“停止完成” 考虑:如果事先已然停止过了,该如何处理?
(5) 如是restart,则先stop, 再start 考虑:如果本来没有start,如何处理?
(6) 如是status, 则如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示 “SCRIPT_NAME is running...” 如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“SCRIPT_NAME is stopped...” 其中:SCRIPT_NAME为当前脚本名
(7)在所有模式下禁止启动该服务,可用chkconfig 和 service命令管理
- #!/bin/bash
- syon(){
- if [ -e /var/lock/subsys/$0 ];then
- echo 1
- else
- echo 0
- fi
- }
- starts(){
- if [ $(syon) -eq 1 ];then
- echo "服务已经启动过了"
- else
- if touch /var/lock/subsys/$0 &> /dev/null ;then
- echo "$1启动成功"
- else
- echo "$1启动失败"
- exit 1
- fi
- fi
- }
- stops(){
- if [ $(syon) -eq 0 ] ;then
- echo "服务没有启动"
- else
- if rm -f /var/lock/subsys/$0 &>/dev/null ;then
- echo "停止成功"
- else
- echo "停止失败$1"
- exit 1
- fi
- fi
- }
- restarts(){
- stops ",服务无法重新启动"
- starts "服务正在重新重启中,"
- }
- statuss(){
- if [ $(syon) -eq 0 ] ;then
- echo "服务没有启动"
- else
- echo "服务已经启动过了"
- fi
- }
- case $1 in
- start)
- starts
- ;;
- stop)
- stops
- ;;
- restart)
- restarts
- ;;
- status)
- statuss
- ;;
- *)
- echo "error ,please Usage: $0 {start|stop|restart|status}"
- esac
2、编写一个脚本/root/bin/copycmd.sh
(1) 提示用户输入一个可执行命令名称;
(2) 获取此命令所依赖到的所有库文件列表
(3) 复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下;
如:/bin/bash ==> /mnt/sysroot/bin/bash /usr/bin/passwd ==> /mnt/sysroot/usr/bin/passwd
(4) 复制此命令依赖到的所有库文件至目标目录下的对应路径下:
如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ldlinux-x86-64.so.2
(5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit退出
- #!/bin/bash
- flag=1
- read -p "please input a path that you want to copy : " mubiaodir
- panduanlujing(){
- path=$(dirname $mubiaodir$ki)
- if [ ! -e $path ];then
- mkdir -p $path
- fi
- cp -f $ki $mubiaodir$ki &> /dev/null || flag=0
- }
- kuwenjian(){
- for ki in $(ldd $(which --skip-alias $cmdname) | sed -rn "s@.*[[:space:]]([/].*)[[:space:]].*@\1@p") $(which --skip-alias $cmdname);do
- panduanlujing
- done
- }
- while true ;do
- read -p "please input a command name , quit is exit:" cmdname
- if [ "$cmdname" == "quit" -o "$cmdname" == "exit" ];then
- break
- else
- if (ldd $(which --skip-alias $cmdname)) &> /dev/null ;then
- kuwenjian
- [ $flag -eq 1 ] && echo "复制成功" || echo "权限不足,复制失败"
- else
- echo "错误的命令名称"
- fi
- fi
- done
3、写一个函数实现两个数字做为参数,返回最大值
- #!/bin/bash
- maxnum(){
- expr $1 + $2 &> /dev/null && echo $(( $1 > $2 ? $1 : $2 )) || echo "参数错误"
- }
- maxnum $1 $2
4、写一个函数实现数字的加减乘除运算,例如输入 1 + 2,,将得出正 确结果
- #!/bin/bash
- yunsuan(){
-
- case $key in
- *)
- echo "$a$key$b=$[a $key b]"
- ;;
- -)
- echo "$a$key$b=$[a $key b]"
- ;;
- +)
- echo "$a$key$b=$[a $key b]"
- ;;
- /)
- echo "$a$key$b=$[a $key b]"
- ;;
- esac
- }
-
- read -p "please input agr1 [+|-|*|/] agr2 :" a key b
- if [ "$key" == "*" ];then
- if expr $a \* $b &> /dev/null ;then
- yunsuan
- else
- echo "输入错误"
- fi
- else
-
- if expr $a $key $b &> /dev/null ;then
- yunsuan
- else
- echo "输入错误"
- fi
- fi
5、斐波那契数列又称黄金分割数列,因数学家列昂纳多·斐波那契以兔子 繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1 、1、2、3、5、8、13、21、34、……,斐波纳契数列以如下被以递归的 方法定义:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2) 写一个函数,求n阶斐波那契数列
- #!/bin/bash
- fei(){
- if [ $1 -eq 0 ] ;then
- echo 0
- elif [ $1 -eq 1 ];then
- echo 1
- else
- echo $[$(fei $[$1-1]) + $(fei $[$1-2])]
- fi
- }
- if [ $# -eq 0 ] ;then
- echo "请传一个个整数作为参数"
- else
- fei $[$1-1]
- fi
- #!/bin/bash
- a=0
- b=1
- fei(){
- for((i=3;i<=$1;i++)){
- let c=a+b,a=b,b=c
- echo -n "$c "
- }
- }
- if expr $1 + 1 &> /dev/null ;then
- if [ $1 -eq 1 ];then
- echo $a
- elif [ $1 -eq 2 ];then
- echo $a $b
- elif [ $1 -ge 3 ];then
- echo $a $b `fei $1`
- else
- echo error
- fi
- else
- echo error
- fi
6、汉诺塔(又称河内塔)问题是源于印度一个古老传说。大梵天创造世 界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着 64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放 在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间 一次只能移动一个圆盘。 利用函数,实现N片盘的汉诺塔的移动步骤
- #!/bin/bash
- move(){
- echo "将第$1个盘子从 $2 --> $3 "
- }
- han(){
- if [ $1 -eq 1 ];then
- move 1 $2 $4
- else
- han $[$1-1] $2 $4 $3
- move $1 $2 $4
- han $[$1-1] $3 $2 $4
- fi
- }
- read -p "请输入盘数:" n
- han $n a b c
变量:存储在单个元素的内存空间
属组:存储多个元素的连续的内存空间,相当于多个变量的集合
数组名和索引
索引:编号从0开始,属于数值索引
注意:索引可支持使用自定义格式,而不仅是数值格式,即为关联索引,bash4.0版本之后开始支持
bash的属组支持稀疏格式(索引不连续)
1,声明数组:
- declare -a ARRAY_NAME
-
- declare -A ARRAY_NAME:关联数组
2,数组元素的赋值:
(1)一次只赋值一个元素
ARRAY_NAME[INDEX]=VALUE
- week[0]="oneday"
- week[2]="twoday"
(2)一次性赋值全部元素
ARRAY_NAME=("one" "two" "three")
(3)只赋值特定元素
ARRAY_NAME=([0]="val1" [2]="val2"...)
(4)交互式数组赋值
read -a ARRAY
3,引用数组
引用数组元素: ${ARRAY_NAME[INDEX]} 省略[INDEX]表示引用下标为0的元素
- ${ARRAY_NAME[*]} #输出数组所有内容
-
- ${ARRAY_NAME[@]} #输出数组所有内容
数组的长度(数组中元素的个数)
- ${#ARRAY_NAME[*]}
-
- ${#ARRAY_NAME[@]}
比如:生成10个随机数保存于数组中,并找出其最大值和最小值
- #!/bin/bash
- declare -a num
- let num[0]=min=max=$RANDOM
- for i in {1..9};do
- num[$i]=$RANDOM
- [ ${num[$i]} -gt $max ] && max=${num[$i]}
- [ ${num[$i]} -lt $min ] && min=${num[$i]}
- done
- echo ${num[*]}
- echo "max=$max min=$min"
练习: 编写脚本,定义一个数组,数组中的元素是/var/log目录下所有以.log结尾的文件;要统计其下标为偶数的文件中的行数之和
- #!/bin/bash
- declare -i lines=0
- declare -a filename
- filename=(/var/log/*.log)
- for i in $(seq 0 $[${#filename[*]}-1]);do
- if [ $[$i%2] -eq 0 ];then
- let lines+=$(cat ${filename[$i]} | wc -l)
- fi
- done
- echo "lines is $lines"
4,数组数据处理
a,引用数组中的元素
所有元素: ${ARRAY_NAME[*]} ${ARRAY_NAME[@]}
数组切片: ${ARRAY_NAME[@]:offset:number}
offset: 要跳过的元素个数
number: 要取出的元素个数
取出偏移量之后的所有元素: ${ARRAY_NAME[@]:offset}
b,向数组中追加元素: ARRAY_NAME[${#ARRAYNEM[*]}]
c,删除数组中的某元素:导致稀疏格式:unset ARRAY_NAME[INDEX]
d,关联数组:
- declare -A ARRAY_NAME
-
- ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2'...)
- #!/bin/bash
- declare -i n=10
- maopao(){
- for i in $(seq 1 $[$n-1]);do
- for j in $(seq 0 $[$n-$i-1]);do
- if [ ${k[$j+1]} -gt ${k[$j]} ];then
- tmp=${k[$j+1]}
- k[$j+1]=${k[$j]}
- k[$j]=$tmp
- fi
- done
- done
- }
- declare -a k
- for i in $(seq 0 $[$n-1]);do
- k[$i]=$RANDOM
- done
- echo "原来随机数:${k[*]}"
- maopao
- echo "排序后的数:"${k[*]}
2,手动输入数字排序
- #!/bin/bash
- echo "please input number and split with space"
- read -a k
- n=${#k[*]}
- for((i=0;i
- for((j=0;j
- [ ${k[$j]} -gt ${k[$j+1]} ] && { tmp=${k[$j]};k[$j]=${k[$j+1]};k[$j+1]=$tmp; }
- }
- }
- echo ${k[@]}
-
相关阅读:
一张图进阶 RocketMQ - 消息存储
开源协作开发者内容平台Vrite
iOS xcframework项目提示“ld: framework not found”
excel提取单元格中的数字
在vue3中使用el-tree-select做一个树形下拉选择器
(181)Verilog HDL:设计一个计数器count_clock
Postman接收列表、数组参数@RequestParam List<String> ids
第四章 网络层
redis zset score 求和
嵌入式C语言自我修养《内存堆栈管理》学习笔记
-
原文地址:https://blog.csdn.net/ly1358152944/article/details/126290052