Linux3.3Shell编程之循环语句与函数

计算机系统

5G云计算

第一章 LINUXShell编程之循环语句与函数

一、循环语句
1.for循环语句

1)循环和遍历

循环(Loop)和遍历(Traversal)是两个在计算机编程中经常使用的概念。

循环是一种重复执行一段代码的结构。通过循环,可以在满足一定条件的情况下,多次执行相同的代码。
循环语句通常包括一个循环体(Loop Body)一个循环条件(Loop Condition),当循环条件满足时,循环体会被执行,否则循环结束。

遍历是指对一组数据结构中的每个元素进行一次访问的过程
在计算机编程中,常常需要对数组、列表、树等数据结构中的每个元素进行遍历,以便对它们进行操作或者输出。

循环是一种控制流程的结构,用于重复执行一段代码,而遍历是一种数据操作的过程,用于访问并处理数据结构中的每个元素
在某些情况下,循环和遍历可以结合使用,以便在循环中遍历数据结构并对其进行操作。

循环(Loop)是计算机编程中非常重要的一种结构,其主要目的是为了简化代码编写,提高代码的重复利用率,以及提高程序的效率。以下是使用循环的一些好处:

好处说明
重复执行代码通过使用循环,可以在程序中重复执行一段代码,从而避免代码的重复编写,减少程序代码量,提高程序的可读性和可维护性
简化程序逻辑使用循环可以使程序逻辑更加简洁明了,提高程序的可读性可理解性,降低代码出错的可能性
提高代码的重复利用率通过将重复的代码封装在循环结构中,可以提高代码的重复利用率,减少代码冗余
提高程序的效率循环可以让程序自动化地执行重复任务,从而减少人工干预的次数,提高程序的效率和性能

2)for语句结构

读取不同的变量值,用来逐个执行同一组命令

for 变量名称(注意是名称不是变量$等) [ in 名称范围 ] (可以不写)
do
   执行内容    ###若满足循环则做什么动作
done         ###for循环结束标志

①批量添加用户

Ⅰ.用户名存放在users.txt文件中,每行一个

Ⅱ.初始密码均设为123456

Ⅲ.验证脚本

for i in `cat /root/user.txt`
do
  useradd $i
  echo 123456 | passwd --stdin $i
done

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

②根据IP地址检查主机状态

Ⅰ.IP地址存放在ipadds.txt文件中,每行一个

Ⅱ.使用ping命令检测个主机的连通性

for i in `cat /root/ipadds.txt`
do
  ping -c 3 -W 2 $i &> dev/null
  if [ $? -eq 0 ]
   then
     echo $i >> /opt/hosts
   else
     echo "$i下线了"
  fi
done

在这里插入图片描述

3)常用转义字符

echo -n 表示不换行输出
echo -e输出转义字符,将转义后的内容输出到屏幕上
###只有加了-e才能执行转义符
常用转义字符说明
\b转义后相当于按退格键(backspace),但前提是“\b”后面存在字符:“\b”表示删除前一个字符,“\b\b”表示删除前两个字符
\c不换行输出,在“\c”后面不存在字符的情况下,作用相当于echo -n;但是当“\c”后面仍然存在字符是,“\c”后面的字符将不会被输出
\n换行,被输出的字符从“\n”处开始另起一行
\f换行,但是换行后的新行的开头位置连接着上一行的行尾
\v与\f相同
\t转义后表示插入tab,即横向制表符
\r光标移至行首,但不换行,相当于使用"\r"以后的字符覆盖"\r"之前同等长度的字符;但是当"\r"后面不存在任何字符时,"\r"前面的字符不会被覆盖
\\表示插入"\"本身

4)跳出循环

表示满足continue条件时跳出循环,但是不影响后续循环执行,它的作用是在循环内部跳过本次循环并继续下一次循环
continue 2 是一个 Shell 中的控制结构,它的作用是在循环内部跳过本次循环并继续下一次循环,而且会跳过两层循环,即在两层循环内部执行
在嵌套循环中,如果我们使用 continue 2 命令,它将跳过包含它的内层循环和外层循环的迭代,并继续执行下一次外层循环的迭代

#!/bin/bash
for i in [1..5]
do
if [ $i -eq 2 ]
then
continue
fi
echo "$i"
done
sh a.sh
输出结果
1
3
4
5

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5)打破循环
在某个循环中遇到break直接结束此次循环,但是不影响外边嵌套的循环
break [数字] 结束数字中的几次循环,若为2则结束自己所在循环和外边嵌套的一层循环,
若为3则结束自己所在循环和外边嵌套的2层循环
break 命令并不控制循环次数,它是用于控制循环流程的
当 break 命令执行时,它会立即跳出当前的循环并继续执行循环后面的代码

#!/bin/bash 
#第一层for循环,j=1,2,3各执行一次do的内容
for j in {1..3}          
 #第一层for循环的执行内容
do                      
     #第二层for循环,i=1,2,3,4,5各执行一次do的内容
    for i in {1..5}     
    #第二层for循环的执行内容  
    do                   
        #第二层for循环中添加的if判断语句,当$i=3是执行then内容
        if [ $i -eq 3 ]  
        #第二层for循环判断语句执行命令,break控制循环的流程
        then                
        break 2         
        #if语句结束
        fi               
    #第二层for循环语句执行每循环一次输出$i
    echo "$i"         
    #第二层for循环语句结束标志
    done                                 
#第一层for循环语句结束标志
done                     

6)死循环

for  ((;;))             ###直接写判断条件为真(for循环)
while  [ 1  -eq  1 ]       ###判断条件为一个永久为真的条件
while  true                ###直接写判断条件为真(while循环)
while  :                   ###判断条件为冒号也表示死循环
2.while循环语句

while循环满足条件执行,不满足不执行

用于不知道循环次数,需要主动结束循环或达到条件结束循环

while [ 判断条件 ]
do
执行动作
done

请添加图片描述
在这里插入图片描述

3.untli循环语句

until循环通常用于当判断条件不成立时才进行循环,一旦判断条件成立,就终止循环,

比如等待某个进程退出、等待某个文件被创建等等

一般不用

二、Shell函数

使用函数可以避免代码重复
使用函数可以将大的工程分割为若干小的功能模块,代码的可读性更强

1.Shell函数的定义

在脚本内定义函数的两种方式

function 函数名{
              命令序列
}
函数名() {
              命令序列
}
2.Shell函数返回值

在脚本内输出函数结果值的两种方式

function db1 {
       read -p "请输入:" value
       return $[$value * 2]
}
db1
echo $?
###return表示退出函数并返回一个退出值,脚本中可以用$?变量显示值
使用原则:
1.函数一结束就取返回值,因为$?变量只返回执行的最后一条命令的退出状态码
2.退出状态码必须0~255,超出时值将除以256取余
db1() {
        read -p "请输入:" value
        echo $[$value * 2]
}
result=`db1`
echo $result
###在函数内使用echo直接将函数返回值输出,在函数外再利用变量名将数值接收
好处:可以再次利用echo来对输出的值做二次操作
3.函数的传参

1)函数内部
函数体内部的$1 $2 代表的是调用函数时,函数后面跟的位置参数
在函数体内部的$# 代表调用函数时,函数后面跟的参数个数
在函数体内部的$* $@代表调用函数时,函数后面跟的所有参数

2)函数外部,脚本内部
函数体外的$1 $2 代表的是执行脚本时,脚本后面跟的位置参数
在函数体外时,$# 代表的是执行脚本时,脚本后面跟的参数个数
在函数体外时,$* $@ 代表的是执行脚本时,脚本后面跟的所有参数

3.函数变量的作用范围

1)函数在Shell脚本中仅在当前脚本Shell环境中有效(使用source/. 执行脚本,也会影响系统的当前Shell环境)

2)Shell脚本中变量默认全局有效

3)将变量限定在函数内部使用local命令

4)系统Shell环境>>脚本Shell环境>>函数Shell环境

4.函数调用自己本身的函数实现递归

1)阶乘

#!/bin/bash
###使用函数的递归实现阶乘

fact() {
  if [ $1 -eq 1 ];then
    echo 1
  elif [ $1 -gt 1 ];then
    local temp=$[$1 - 1]
    ###实现函数内部调用自己本身
    local result=$(fact $temp)
    ###echo $[$1 * Sresult]
    ###              5 * $result(fact 4)
    ###                   4 * $result(fact 3)
    ###                        3 * $result(fact 2)
    ###                             2 * $result(fact 1 = 1)
    ### 即最终输出结果就是=5*4*3*2*1             
    echo $[$1 * result]
  else
    echo "输入的值无效。请输入大于等于1的整数!"
  fi
}

#####main#####

read -p "请输入一个大于等于1的整数:" num
result=$(fact $num)
echo "$num的阶乘值为$result"

2)目录

#!/bin/bash
###使用函数递归目录/var/log,如果是文件直接输出文件名,如果是目录则输出目录名且输出此目录下的所有目录和文件名

listf () {
  for f in $(ls $1)
   do
    if [ -d "$1/$f" ];then
      echo "$2目录$f"
      listf "$1/$f" "  $2"
    else
      echo "$2文件$f"
    fi
  done
}

#####main#####

listf "/var/log" ""
#!/bin/bash
###通过脚本输出环境变量PATH所包含的所有目录以及其中的子目录和所有不可执行文件

###定义一个遍历PATH环境变量的函数
list_path() {
    IFSB=$IFS
    IFS=$IFS':'
    for F in $PATH
    do
        echo $F
    done
    IFS=$IFSB
}

###定义一个递归函数
listf() {
    for F in $(ls $1)
    do
        if [ -d "$1/$F" ];then
            echo "$2$F"
            listf "$1/$F" "  $2"
        else
            if [ ! -x "$1/$F" ];then
                echo "$2$F"
            fi
        fi
    done
}

######main######

folder=$(list_path)
for path in $folder
do
    echo $path
    listf "$path" ""
done
三、Shell数组
1.Shell数组的定义

在脚本内定义数组的四种方式

数组名=(值0 值1 值2 ...)
数组名=([0]=值 [1]=值 [2]=值 ...)
列表名="值0 值1 值2 ..."
数组名=($列表名)
数组名[0]="值"
数组名[1]="值"
数组名[2]="值"
2.Shell数组包括的数据类型

使用" "' ' 定义

1)数值类型

2)字符类型

3.Shell数组操作

1)获取数组长度

arr=(1 2 3 4 5)
length=${$arr[*]}
       ${$arr[@]}
echo $length

2)获取数组数据列表

echo ${arr[*]}
echo ${arr[@]}

3)获取数组下标列表

echo ${!arr[*]}
echo ${!arr[@]}

4)读取某下标赋值

arr1=${arr[2]}
echo $arr1

5)数组遍历

#!/bin/bash
arr=(1 2 3 4 5)
for v in ${arr[@]}
do
echo $v
done

6)数组切片

arr=(1 2 3 4 5)
###输出整个数组
echo ${arr[@]}
###获取${数组名[@或*]:起始位置的下标:取值的长度}的值
echo ${arr[@]:0:2}

7)数组替换

arr=(1 2 3 4 5)
###${数组名[@或*]/查找的旧字符/替换的新字符},并不会替换数组原有内容,仅输出时临时更改
echo ${arr[@]/3/66}
echo ${arr[@]}
###要实现改变原有数组,可通过重新赋值实现
arr=(${arr[@]/3/66})
echo ${arr[@]}

8)数组删除

arr=(1 2 3 4 5)
###删除数组
unset arr
echo ${arr[@]}

arr=(1 2 3 4 5)
###删除第三个元素
unset arr[2]
echo ${arr[*]}
4.Shell数组追加元素

1)方法一

arr[数组下标]=值

2)方法二

arr[${#arr[@]}]=值
###此方法数组内不能有空值,即数组不能是有缺的

3)方法三

arr=("${arr[@]}" 值1 ... 值N)
###双引号不能省略,否则,当数组arr中存在包含空格的元素时会按空格将元素拆分成多个
不能将“@”替换成为“*”,如果替换为“*”,不加双引号时与“@”的表现一致,加双引号时,会将数组arr中的所有元素作为一个元素添加到数组中
for i in "${arr[@]}"; do echo $i; done

4)方法四

arr+=(值1 ... 值N)
待添加元素必须用“()”包围起来,并且多个元素用空格分割
5.Shell向函数传数组参数
###如果将数组变量作为函数参数,函数只会取数组变量的第一个值
test1(){
      echo "接收到的参数列表:$@"
      newarr=($1)
      echo "新数组的值为:${newarr[*]}"
}
###########main############
arr=(1 2 3 4 5)
echo "原始数组的值为:${arr[*]}"
test $arr

###解决这个问题则需要将数组变量的值分解成单个的值,然后将这些值作为函数参数使用。在函数内部,再将所有的参数重新组合成一个新的数组变量
test2(){
	newarr=($(echo $@))
	echo "新数组的值为:${newarr[*]}"
}
array=(1 2 3 4 5)
echo"原始数组的值为:${array[*]}"
test1 ${array[*]}
6.Shell从函数返回数组
test1() {
	newarr=(`ehco $@`)
	
	sum=0
	for value in ${newarr[*]}
	do
		sum=$[$sum + $value]
	done
	echo $sum
}

test2() {
	newarr=(`ehco $@`)
	for ((i=0;i<=$[$# - 1];i++))
	{
		newarr[$i]=$[${newarr[$i]} * 2]
	}
	echo $ {newarr[*]}
}

###########main############

arr=(1 2 3 4 5)
echo "原始数组的值为:${arr[*]}"
resultl=`test2 ${arr[*]}`
echo "新数组的和为:$result1"

result2=(`test3 ${array[*]}`)
echo "新数组的值为:${result2[*]}"
7.Shell数组排序算法

1)冒泡排序
类似气泡上涌的动作,会将数据在数组中从小到大或者从大到小不断地向前移动
基本思想
冒泡排序的基本思想是对比相邻的两个元素值,如果满足条件就交换元素值,把较小的元素移动到数组前面,把大的元素移动到数组后面(也就是交换两个元素的位置),这样较小的元素就像气泡一样从底部上升到顶部
算法思路
冒泡算法由双层循环实现,其中外部循环用于控制排序轮数,一般为要排序的数组长度减1次,因为最后一次循环只剩下一个数组元素,不需要对比,同时数组已经完成排序,而内部循环主要用于对比数组中每个相邻元素的大小,以确定是否交换位置,对比和交换次数排序轮数而减少

#!/bin/bash
array=(63 4 24 1 3 15)
echo "old_array: ${array[*]}"
length=${#array[*]}
#定义比较轮数,比较轮数为数组长度减1,从1开始
for ((i=l; i<$length; i++))
do
	#确定比较元素的位置,比较相邻两个元素,较大的数往后放,比较次数随比较轮数而减少
	for ((j=0; j<$length-i; j++))
	do
		#定义第一个元素的值
		first=${array[$j]}
		#定义第二个元素的值
		k=$[$j+1]
		second=${array[$k]}
		#比较两个相邻元素的值,如果第一个元素比第二个元素大就互换
		if [ $first -gt $second]
		then
			#把第一个元素值保存到临时变量中
			temp=$first
			#把第二个元素值赋给第一个元素
			array[$j]=$second
			#把临时变量(也就是第一个元素值)赋给第二个元素
			array[$k]=$temp
		fi
	done
done
#输出排序后后的数组
echo "new_arrays: $(array[*])"

2)直接选择排序
与冒泡排序相比,直接选择排序的交换次数更少,所以速度会快些

基本思想
将指定排序位置与其它数组元素分别对比,如果满足条件就交换元素值,注意这里区别冒泡排序,不是交换相邻元素,而是把满足条件的元素与指定的排序位置交换(如从最后一个元素开始排序),这样排序好的位置逐渐扩大,最后整个数组都成为已排序好的格式

#!/ bin/bash
#定义一组数组
array=(63 4 24 1 3 15)
#获取数组长度
length=${#array[*]}
#定义排序轮数,为数组长度减1,且从1开始
for((i=l; i<$length; i++))
do
	#定义一个每轮循环比较的初始最大元素的下标,从0开始,即第一个元素
	index=0
	#确定用于第一个元素与当前最大元素作比较的索引范围,从1开始,且每轮比较的最后一个元素下标会随着轮数增加而减少
	for ((j=l; j<=$length-i; j++))
	do
		#通过比较获取最大值元素的索引位置
		if [ ${ array[$j]} -gt $ {array [$index]} ] ;then
		index=$j
		fi
	done
	#获取每轮最后一个元素的索引
	last=$[$length - $i]
	#把当前轮次的最后一个元素的值保存在临时变量中
	temp=${array[$last]}
	#把最大的元素的值赋给当前轮次的最后一个元素
	array[$last]=${array[$index]}
	#将临时变量的值,即原最后一个元素的值交换
	array[$index]=$temp
done
echo "排序后的数组的值为:${array[@]}"

3)反转排序
以相反的顺序把原有数组的内容重新排序

基本思想
把数组最后一个元素与第一个元素替换,倒数第二个元素与第二个元素替换,以此类推,直到把所有数组元素反转替换

#!/bin/bash
array=(10 20 30 40 50 60)
length=${#array[*]}
for ((i=0; i<$length/2; i++))
do
	temp=${array[$i]}
	array[$i]=${array[$length-$i-1]}
	array[$length-$i-1]=$temp
done
echo ${array[*]}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值