文章目录
计算机系统
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[*]}