目录
示例1:写一个脚本,判定192.168.0.200-192.168.0.254之间的主机哪些在线。
示例6:从“标准输入”读入n次字符串,每次输入的字符串保存在数组array里。
示例7:将字符串里的字母逐个放入数组,并输出到“标准输出”。
示例8:把1-3这3个数字存到数组里,分别乘以8然后依次输出。
示例9:打印下面这句话中字母数不大于6的单词: cat is favorite to eat fish
实验1:编写函数,实现打印绿色OK和红色FAILED;判断是否有参数,存在为Ok,不存在为FAILED。
实验2:编写函数,实现判断是否无位置参数,如无参数,提示错误。
1.函数
函数几乎是学习所有的程序设计语言时都必须过的一关。对于学习过其他的程序语言的用户来说,
函数可能并不陌生。但是Shell中的函数与其他的程序设计语言的函数有许多不同之处。
1.1 什么是函数
通俗地讲,所谓函数就是将一组功能相对独立的代码集中起来,形成一个代码块,这个代码可以完
成某个具体的功能。从上面的定义可以看出,Shell
中的函数的概念与其他语言的函数的概念并没
有太大的区别。从本质上讲,函数是一个函数名到某个代码块的映射。也就是说,用户在定义了函
数之后,就可以通过函数名来调用其所对应的一组代码。
使用shell
函数优势:
1.
把相同的程序段定义为函数,可以减少整个程序段代码量,提升开发效率。
2.增加程序段可读性、易读性,提升管理效率。
3
.可以实现程序功能模块化,使得程序具备通用性(可移植性)。
1.2 函数的语法
function 函数名 () {指令return}简化写法 1 :function 函数名 {指令return}简化写法 2 :函数名 () {指令return}
1.3 函数的调用
在
Shell
中,函数调用的基本语法如下:
function_name param1 param2 …
在上面的语法中,
function_name
表示函数名称,其后面跟的
param1
、
param2…
表示函数的参
数。Shell的函数参数的语法比较特殊。实际上,
Shell
将脚本参数和函数参数做了统一地处理。也
就是说, Shell采用了相同的方法来处理脚本的参数和函数参数。
(
1
)调用函数:直接执行函数名即可。
函数名
(
2
)带参数的函数执行方法:
函数名 参数
与
Shell
脚本一样,用户可以在
Shell
函数中使用位置变量来获取参数值。例如,
$0
表示脚本名称,
$#
来获取函数的参数个数,$1
表示第
1
个参数,
$2
表示第
2
个参数等,以此类推。另外,用户还可
以通过系统变量$@
和
$*
获取所有参数的值。
[root@localhost test6]# vim func.sh
#!/bin/bash
func()
{
echo "the function has $# parameters"
echo "all parameters are $*"
echo "all parameters are $@"
echo "the script's name is $0"
echo "the first parameter is $1"
}
func hello world
[root@localhost test6]# bash func.sh
the function has 2 parameters
all parameters are hello world
all parameters are hello world
the script's name is func.sh
the first parameter is hello
1.4 函数的返回值
首先,用户可以使用
return
语句来返回某个数值,这与绝大部分的程序设计语言是相同的。但是,
在Shell中,
return
语句只能返回某个
0~255
之间的整数值。
在函数中,用户将需要返回的数据写入到标准输(stdout
),通常这个操作是使用
echo
语句来完成
的。然后在调用程序中将函数的执行结果赋给一个变量。这种做法实际上就是命令替换的一个变
种。例:
[root@localhost test6]# vim length.sh
#!/bin/bash
length(){
str=$1
result=0
if [ "$1" != "" ];then
result=${#str}
fi
echo "$result"
}
len=$(length "abcd12")
echo "the string's length is $len"
[root@localhost test6]# bash length.sh
the string's length is 6
1.5 函数示例
示例1:写一个脚本,判定192.168.0.200-192.168.0.254之间的主机哪些在线。
要求:
1
、使用函数来实现一台主机的判定过程;
2
、在主程序中来调用此函数判定指定范围内的所有主机的在线情况。直接使用函数实现
方法一:直接使用函数实现(无参数,无返回值)
[root@localhost test6]# vim online_host.sh
#!/bin/bash
online() {
for i in {200..254};do
if ping -c 1 192.168.0.$i &>/dev/null
then
echo "192.168.0.$i is up"
else
echo "192.168.0.$i is down"
fi
done
}
online
方法二:使用函数传参(有参数,无返回值)
[root@localhost test6]# vim online_host1.sh
#!/bin/bash
online() {
if ping -c 1 $1 &>/dev/null
then
echo "$1 is up"
else
echo "$1 is down"
fi
}
for i in {200..254}
do
online 192.168.0.$i
done
方法三:使用函数返回值判断(有参数,有返回值)
[root@localhost test6]# vim online_host2.sh
#!/bin/bash
online() {
if ping -c 1 $1 >/dev/null
then
return 0
else
return 1
fi
}
for i in {200..254}
do
online 192.168.0.$i
if [ $? -eq 0 ];then
echo "192.168.0.$i is up"
else
echo "192.168.0.$i is down"
fi
done
示例2:写一个脚本,使用函数完成
1
、函数能够接受一个参数,参数为用户名;
判断一个用户是否存在
如果存在,就返回此用户的
shell
和
UID
;并返回正常状态值;
如果不存在,就说此用户不存在;并返回错误状态值;
2
、在主程序中调用函数;
[root@localhost test6]# vim user_mess.sh
#!/bin/bash
user() {
if id $1 &>/dev/null
then
echo "`grep ^$1 /etc/passwd | cut -d: -f3,7`"
return 0
else
echo "$1 does not exist"
return 1
fi
}
read -p "please input username:" username
until [ "$username" = "quit" -o "$username" = "exit" -o "$username" = "q" ]
do
user $username
if [ $? == 0 ];then
read -p "please input again:" username
else
read -p "no $username,please input again:" username
fi
done
[root@localhost test6]# bash user_mess.sh
please input username:redhat
1000:/bin/bash
please input again:jkl
jkl does not exist
no jkl,please input again:exit
1.6 函数库文件
为了方便地重用这些功能,可以创建一些可重用的函数。这些函数可以单独地放在函数库文件中。
函数库文件定义:
创建一个函数库文件的过程非常类似于编写一个
Shell
脚本。脚本与库文件之间的唯一区别在于函
数库文件通常只包括函数,而脚本中则可以既包括函数和变量的定义,又包括可执行的代码。此处
所说的可执行代码,是指位于函数外部的代码,当脚本被载入后,这些代码会立即被执行,毋需另
外调用。
函数库文件的调用:
当库文件定义好之后,用户就可以在程序中载入库文件,并且调用其中的函数。在
Shell
中,载入
库 文件的命令为.
,即一个圆点,其语法如下:
. filename
其中,参数
filename
表示库文件的名称,必须是一个合法的文件名。库文件可以使用相对路径,也
可以使用绝对路径。另外,圆点命令和库文件名之间有一个空格。
1.7 递归函数
Linux
的
Shell
也支持函数的递归调用。也就是说,函数可以直接或者间接地调用自身。在函数的递
归调用中,函数既是调用者,又是被调用者。
递归函数的调用过程就是反复地调用其自身,每调用一次就进入新的一层。
示例3: 根据用户输入的数值计算该数的阶乘
[root@localhost test6]# vim fact.sh
#!/bin/bash
fact(){
local n="$1"
if [ "$n" -eq 0 ]
then
result=1
else
let "m=n-1"
fact "$m"
let "result=$n * $?"
fi
return $result
}
fact "$1"
echo "Factorial of $1 is $?"
[root@localhost test6]# bash fact.sh 0
Factorial of 0 is 1
[root@localhost test6]# bash fact.sh 5
Factorial of 5 is 120
注意:
默认情况下,除了与函数参数关联的特殊变量之外,其他所有的变量都有全局的有效范围。
另外,在函数内部,如果没有使用local
关键字进行修饰,那么函数中的变量也是全局变量。
示例4:函数的变量是全局变量
【root@localhost test6]# cat global.sh
#!/bin/bash
var="hello world"
func(){
var="orange"
echo $var
var2="hello"
}
echo "$var"
func
echo "$var"
echo "$var2"
[root@localhost test6]# ./global.sh
hello world
orange
orange
hello
示例5:函数的变量使用local指定为局部变量
[root@localhost test6]# cat 7.sh
#!/bin/bash
var="hello world"
func(){
local var="orange"
echo "$var"
local var2="hello"
}
echo "$var"
func
echo "$var"
echo "$var2"
[root@localhost test6]# ./7.sh
hello world
orange
hello world
2.数组
所谓数组,是指将具有相同类型的若干变量按照一定的顺序组织起来的一种数据类型。
Shell
语言
对于数组的支持非常强大。在Shell
中,用户可以通过多种方式来创建一个数组。
2.1 定义数组
方法一:用小括号将变量值括起来赋值给数组变量,每个变量之间要用空格进行分隔。
array = (value1 value2 value3 ... )
方法二:用小括号将变量值括起来,同时采用键值对的形式赋值。当通过键值对定义数组时,用户
所提供的键值对中的元素索引不一定是连续的,可以任意指定要赋值的元素的索引。之所以可以这
样操作,是因为用户已经显式指定了索引,Shell
就可以知道值和索引的对应关系。
array = ([0] = one [1] = two [2] = three ... [n] = valuen)
方法三:分别定义数组变量的值
array[0]=a;array[1]=b;array[2]=c
方法四:动态的定义变量,并使用命令的输出结果作为数组的内容。
array=( 命令 )
方法五:通过declare语句定义数组
declare -a array
定义关联数组:关联数组使用字符串作为下标,而不是整数,这样可以做到见名知意。不同于普通
数组,关联数组必须使用带有
-
A
选项的
declare
命令创建。
方法一:一次赋一个值
[root@localhost ~]# declare -A array
[root@localhost ~]# array[shuju1]=apple
[root@localhost ~]# array[shuju2]=banana
[root@localhost ~]# echo ${array[*]}
banana apple
方法二:一次赋多个值
[root@localhost ~]# declare -A a
[root@localhost ~]# a=([index1]=tom [index2]=jack [index3]=alice)
[root@localhost ~]# echo ${a[@]}
tom jack alice
2.2 数组操作
获取所有元素:
# echo ${array[*]} # *和@ 都是代表所有元素
获取元素下标:
# echo ${!array[@]}
# echo ${!array[*]}
获取数组长度:
# echo ${#array[*]}
# echo ${#array[@]}
获取第一个元素:
# echo ${array[0]}
添加元素:
# array[3]=d
添加多个元素:
# array+=(e f g)
以切片方式获取部分数组元素:(用户得到的是一个空格隔开的多个元素值组成的字符串)
${array[@|*]:start:length}
删除第一个元素:
# unset array[0] # 删除会保留元素下标
删除数组:
# unset array
遍历数组:
方法 1:
#!/bin/bash
IP=(192.168.1.1 192.168.1.2 192.168.1.3)
for ((i=0;i<${#IP[*]};i++))
do
echo ${IP[$i]}
done
# bash test.sh
192.168.1.1
192.168.1.2
192.168.1.3
方法 2:
#!/bin/bash
IP=(192.168.1.1 192.168.1.2 192.168.1.3)
for IP in ${IP[*]}
do
echo $IP
done
2.3 数组案例
示例6:从“标准输入”读入n次字符串,每次输入的字符串保存在数组array里。
[root@localhost test6]# vim str.sh
i=0
n=5
while [ "$i" -lt $n ];do
echo "please input strings...`expr $i + 1`"
read array[$i]
b=${array[$i]}
echo "$b"
i=`expr $i + 1`
done
[root@localhost test6]# bash str.sh
please input strings...1
a
a
please input strings...2
fa
fa
please input strings...3
fa
fa
please input strings...4
few
few
please input strings...5
wwwwwwwef
wwwwwwwef
示例7:将字符串里的字母逐个放入数组,并输出到“标准输出”。
[root@localhost test6]# vim letter.sh
chars='ajfkalfjoiweqqef'
for ((i=0;i<${#chars};i++));do
array[$i]=${chars:$i:1}
echo ${array[$i]}
done
说明:${chars:$i:1},表示从chars字符串的 $i 位置开始,获取 1 个字符
示例8:把1-3这3个数字存到数组里,分别乘以8然后依次输出。
[root@localhost test6]# vim number.sh
#!/bin/bash
array=(`seq 3`)
for ((i=0;i<${#array[@]};i++))
do
echo $[${array[$i]}*8]
done
[root@localhost test6]# bash number.sh
8
16
24
示例9:打印下面这句话中字母数不大于6的单词: cat is favorite to eat fish
[root@localhost test6]# vim word.sh
#!/bin/bash
array=(cat is favorite to eat fish)
for i in ${array[*]}
do
if [ ${#i} -lt 6 ]
then
echo $i
fi
done
[root@localhost test6]# bash word.sh
cat
is
to
eat
fish
函数和数组实验练习:
实验1:编写函数,实现打印绿色OK和红色FAILED;判断是否有参数,存在为Ok,不存在为FAILED。
[root@manager ~]# vim test301.sh
#!/bin/bash
#
#********************************************************************
#Author: Shi
#Date: 2022-12-30
#FileName: test301.sh
#********************************************************************
test301()
{
if [ -z $1 ]
then
echo -e "\033[31m FAILED \033[0m"
else
echo -e "\033[32m OK \033[0m"
fi
}
test301
[root@manager ~]# bash test301.sh
FAILED
实验2:编写函数,实现判断是否无位置参数,如无参数,提示错误。
[root@manager ~]# vim test302.sh
#!/bin/bash
#
#********************************************************************
#Author: Shi
#Date: 2022-12-30
#FileName: test302.sh
#********************************************************************
test302()
{
if [ $# -ne 0 ]
then
echo" parameters exists "
else
echo" error "
fi
}
read -p " please input parameter: " i
test302 $i
[root@manager ~]# bash test302.sh
please input parameter: 1 2
parameters exists 1 2
[root@manager ~]# bash test302.sh
please input parameter:
error
实验3:编写函数实现两个数字做为参数,返回最大值。
[root@manager ~]# vim test303.sh
#!/bin/bash
#
#********************************************************************
#Author: Shi
#Date: 2022-12-30
#FileName: test303.sh
#********************************************************************
read -p " please input two number: " num1 num2
test303(){
if [ $num1 -gt $num2 ]
then
echo " max is $num1 "
elif [ $num1 -lt $num2 ]
then
echo " max is $num2 "
else
echo " The number are equal "
fi
}
test303 $num1 $num2
[root@manager ~]# bash test303.sh
please input two number: 1 2
max is 2
[root@manager ~]# bash test303.sh
please input two number: 1 5
max is 5
[root@manager ~]# bash test303.sh
please input two number: 1 1
The number are equal
实验4:编写函数,实现两个整数位参数,计算加减乘除。
[root@manager ~]# vim test304.sh
#!/bin/bash
#
#********************************************************************
#Author: Shi
#Date: 2022-12-30
#FileName: test304.sh
#********************************************************************
read -p "please input two number:" num1 num2
test304 (){
expr $num1+$mun2 >&/dev/null
if [ $# -ne 0 ]
then
echo "$num1+$num2=$[$num1+$num2]"
echo "$num1-$num2=$[$num1-$num2]"
echo "$num1*$num2=$[$num1*$num2]"
echo "$num1/$num2=$[$num1/$num2]"
else
echo "is not an integer, please re-enter"
fi
}
test304 $num1 $num2
[root@manager ~]# bash test304.sh
please input two number:2 3
2+3=5
2-3=-1
2*3=6
2/3=0
实验5:将/etc/shadow文件的每一行作为元数赋值给数组。
[root@manager ~]# vim test305.sh
#!/bin/bash
#
#********************************************************************
#Author: Shi
#Date: 2022-12-30
#FileName: test305.sh
#********************************************************************
num=`wc -l </etc/shadow`
for i in `seq $num`
do
array[$i]=`head $i /etc/shadow | tail -l`
done
for i in ${array[*]}
do
echo $i
done
实验6:使用关联数组统计文件/etc/passwd中用户使用的不同类型shell的数量。
[root@manager ~]# vim test306.sh
#!/bin/bash
#
#********************************************************************
#Author: Shi
#Date: 2022-12-30
#FileName: test306.sh
#********************************************************************
declare -A array
for i in `cut -d: -f 7 /etc/passwd`
do
let array[$i]++
done
for i in ${!array[*]}
do
echo " $i: ${array[$i]} "
done
[root@manager ~]# bash test306.sh
/sbin/nologin: 42
/bin/bash: 4
/sbin/halt: 1
/bin/sync: 1
/bin/false: 1
/sbin/shutdown: 1
实验7:使用关联数组按扩展名统计指定目录中文件的数量。
[root@manager ~]# vim test307.sh
#!/bin/bash
#
#********************************************************************
#Author: Shi
#Date: 2022-12-30
#FileName: test307.sh
#*******************************************************************
read -p "pleace input filename:" filename
declare -A array
for i in `ls $filename | fgrep '.' | cut -d . -f 2`
do
let array[$i]++
done
for i in ${!array[*]}
do
echo "$i: ${array[$i]}"
done
[root@manager ~]# bash test307.sh
pleace input filename:
repo: 1
cfg: 2
sh: 33