linux12shell编程 -->函数

文章目录

函数

一、 函数介绍

1、什么是函数???
函数就是用来盛放一组代码的容器,函数内的一组代码完成一个特定的功能,称之为一组代码块,调用函数便可触发函数内代码块的运行,这可以实现代码的复用,所以函数又可以称之为一个工具
2、为何要用函数
#1、减少代码冗余
#2、提升代码的组织结构性、可读性
#3、增强扩展性

二、 函数的基本使用

具备某一功能的工具=>函数
事先准备好哦工具=>函数的定义
遇到应用场景,拿来就用=>函数的调用

所以函数的使用原则:先定义,后调用
1、定义函数
#语法:
[ function ] funname [()]
{
    命令1;
    命令2;
    命令3;
    ...
    [return int;]
}

# 示例1:完整写法
function 函数名() {
	函数要实现的功能代码
}

# 示例2:省略关键字(),注意此时不能省略关键字function
function 函数名 {
	函数要实现的功能代码
}

# 示例3:省略关键字function
函数名() {
	函数要实现的功能代码
}
2、调用函数
# 语法:
函数名  # 无参调用
函数名 参数1 参数2  # 有参调用

# 示例
function test1(){
    echo "执行第一个函数"
}


function test2 {
    echo "执行第二个函数"
}


test3(){
    echo "执行第三个函数"
}


# 调用函数:直接引用函数名字即调用函数,会触发函数内代码的运行
test1
test2
test3

# ps:关于有参调用见下一小节

三、 函数参数

如果把函数当成一座工厂,函数的参数就是为工厂运送的原材料

1、调用函数时可以向其传递参数
# 调用函数test1,在其后以空格为分隔符依次罗列参数 
test1 111 222 333 444 555
2、在函数体内部调用函数
、在函数体内部,通过 `$n` 的形式来获取参数的值,例如,`$1`表示第一个参数,`$2`表示第二个参数...当n>=10时,需要使用`${n}`来获取参数
[root@mm ~]# cat b.sh 
function test1(){
    echo "...start..."
    echo $1
    echo $2
    echo $3
    echo "...end..."
}

test1 111 222 333 444 555  # 为函数体传参

[root@mm ~]# ./b.sh 
...start...
111
222
333
...end...
3、脚本内获取命令行定义函数
1.脚本内定义函数
[root@m01 function]# cat a.sh	# 只针对函数内定义的值生效
#!/bin/bash
# 直接运行函数会自动依次调用以下值,即111 222 333
function test1(){
    echo "...start..."
    echo "这是函数内:$1"
    echo "这是函数内:$2"
    echo "这是函数内:$3"
    echo "...end..."
}
test1 111 222 333 444 555

# 测试效果:脚本级参数不会调用到
[root@m01 function]# sh a.sh 
···开始···
这是函数内:111
这是函数内:222
这是函数内:333
···结束···
这是脚本级的参数
这是脚本级的参数
这是脚本级的参数

=======================================================================

2.在命令行定义函数	# 只针对脚本级参数生效
#!/bin/bash
#一个是脚本内调用,一个是脚本外调用(命令行传入参数)
test1(){
	echo ···开始···
	echo 这是函数内:$1
	echo 这是函数内:$2
	echo 这是函数内:$3
	echo ···结束···
}
test1 111 222 333
echo 这是脚本级的参数$1
echo 这是脚本级的参数$2
echo 这是脚本级的参数$3

# 测试效果:函数内不会调用到,只会调用脚本里定义的函数
[root@m01 function]# sh b.sh 4 5 6
···开始···
这是函数内:111
这是函数内:222
这是函数内:333
···结束···
这是脚本级的参数4
这是脚本级的参数5
这是脚本级的参数6

方式二
[root@openvpn day6]# cat function2.sh 
#! /bin/bash

function f1 {
    echo $1
    echo $2
    echo $3
}

function add() {
   expr $1 + $2
}

f1  111 33 22
add 10 22
add 22 11

# 判断数字是否为偶数
[root@openvpn day6]# cat function7.sh 
#! /bin/bash

function test(){
  num=`echo "$1%2"|bc`  
  return $num
}
test $1

if [ $? -eq 0 ];then
   echo "$1 是偶数"
else
   echo "就是为奇数"
fi
PS:在脚本内,获取脚本调用者在“命令行里”为脚本传入的参数,同样使用的是$n,勿搞混

4、参数处理说明

参数处理说明
$#传递到脚本或函数的参数个数
$*所有参数
$@所有参数,与$*类似
$$当前脚本进程的ID号
$?获取上一条命令执行完毕后的退出状态。0表示正确,非0代表错误,如果执行的是函数那么$?取的是函数体内return后的值

ps:

#1、当$*和$@没有被引号引用起来的时候,它们确实没有什么区别,都会把位置参数当成一个个体。

#2、"$*" 会把所有位置参数当成一个整体(或者说当成一个单词),如果没有位置参数,则"$*"为空,如果有两个位置参数并且分隔符为空格时,"$*"相当于"$1 $2"

#3、"$@"  会把所有位置参数当成一个单独的字段,如果没有位置参数,则"$@"展开为空(不是空字符串,而是空列表),如果存在一个位置参数,则"$@"相当于"$1",如果有两个参数,则"$@"相当于"$1"  "$2"等等
示例
$0	脚本本身文件名称
$1	命令行第一个参数,$2 为第二个,以此类推
$*	所有参数列表
$@	所有参数列表
$#	参数个数
$$	脚本运行时的 PID
$?	脚本退出码
 
1. $∗ 与 $@ 的区别:
[root@m01 ~]#  test.sh 1 2 3
没加双引号:为"1 2 3" 
加上双引号:
$* 表示 "1 2 3”
$@ 表示 "1” “2” “3”

2. $单双括号
$() 等同于 ``	# 取其命令的结果

$(())可进行数字运算 
[root@m01 ~]# a=3;b=2;c=5
[root@m01 ~]# echo $((a+b*c))
13

5、@ 与 @与@与*区别示例
[root@mm ~]# cat b.sh 
echo "=======函数test1==========="
function test1(){
    echo "$*"  # 111 222 333 444 555
    echo "$@"  # 111 222 333 444 555
    echo $#    # 5
    echo $$    # 87380
    echo $?    # 0
}

test1 111 222 333 444 555

echo "=======函数test2==========="
function test2(){
    for i in "$*"  # 注意:$*不加引号结果与$@一模一样
    do
        echo $i
    done
}

test2 111 222 333 "444 555"  # 注意"444 555"被引号引成了一个参数
# 运行结果为:111 222 333 444 555

echo "=======函数test3==========="
function test3(){
    for i in "$@"  # 注意:$*不加引号结果与$@一模一样
    do
        echo $i
    done
}

test3 111 222 333 "444 555"  # 注意"444 555"被引号引成了一个参数
# 运行结果为:
# 111
# 222
# 333
# 444 555

四 、函数的返回值

如果把函数当成一座工厂,函数的返回值就是工厂的产品,在函数内使用return关键字返回值,函数内可以有多个return,但只要执行一个,整个函数就会立刻结束

[root@mm ~]# cat b.sh 
function test1(){
    echo 111
    return
    echo 222
    return 
    echo 333
}

test1

[root@mm ~]# chmod +x bbbb.sh 
[root@mm ~]# ./b.sh 
111


#使用函数实现跳板机连接
方式一:
[root@openvpn day6]# cat function_jump.sh 
#! /bin/bash

function web01 (){
    ssh web01
}

function web02(){
    ssh web02
}

function lb4 {
    ssh lb4
}
#web01 
#web02
lb4

# 方式二:
[root@openvpn day6]# cat function_all.sh 
#! /bin/bash

function connect (){
    ssh root@$i
}

PS3="请连接"
select i in web01 web02 web03 lb4
do
   ssh $i
done

需要注意的是shell语言的函数中,通常用return返回函数运行是否成功的状态,0代表成功,非零代表失败,需要用$?获取函数的返回值

1、如果函数内没有return,那么将以最后一条命令运行结果(命令运行成功结果为0,否则为非0)作为返回值
# return 最大数是256

[root@mm ~]# cat b.sh 
function test1(){
    echo 111
    echo 222
    echo 333
    xxx  # 运行该命令出错
}

test1
echo $?
[root@mm ~]# ./b.sh 
111
222
333
./b.sh:行5: xxx: 未找到命令
127
2、如果函数内有return,那么return后跟的只能是整型值并且范围为0-255,用于标识函数的运行结果是否正确, 与C 语言不同,shell 语言中 0 代表 true,0 以外的值代表 false
[root@openvpn day6]# cat function3.sh 
#! /bin/bash

function f1 (){
    echo 111
    echo 123
    return
    echo 2222
}

f1
echo $? # 上一条命令的返回值
[root@openvpn day6]# ./function3.sh 
111
123

五 、变量的作用域

Shell 变量的作用域(Scope),就是 Shell 变量的有效范围(可以使用的范围)。

1、局部变量:只能在函数内访问

使用local关键字定义在函数内的变量属于局部范围,只能在函数内使用,如下所示

[root@openvpn shell]# cat hello.sh 
#!/bin/bash

# 定义函数
function test(){
    local x=111
    echo "函数内访问x:$x"
}

# 调用函数
test

echo "在函数外即全局访问x:$x"  # 无法访问函数内的局部变量

执行结果

[root@openvpn shell]# ./hello.sh 
函数内访问x:111
在函数外即全局访问x:
2、全局变量:可以在当前shell进程中使用

所谓全局变量,就是指变量在当前的整个 Shell 进程中都有效。每个 Shell 进程都有自己的作用域,彼此之间互不影响。在 Shell 中定义的变量,默认就都是全局变量。

[root@openvpn shell]# cat hello.sh 
#!/bin/bash
x=2222

function test(){
    echo "函数内访问x:$x"
}
test
echo "在函数外即全局访问x:$x" 

执行结果

[root@openvpn shell]# ./hello.sh 
函数内访问x:2222
在函数外即全局访问x:2222

请注意

1、在函数内定义的变量,如果没有用local声明,那么默认也是全局变量,shell变量语法的该特性与js的变量是类似的(在js函数内部定义的变量,默认也是全局变量,除非加上关键字var)
[root@openvpn shell]# cat hello.sh 
#!/bin/bash
function test(){
    x=2222  # 全局变量
}
test
echo "在函数外即全局访问x:$x"  

[root@openvpn shell]# ./hello.sh 
在函数外即全局访问x:2222

方式二:
[root@openvpn day6]# cat function4.sh 
#! /bin/bash
x=11111
function f1 (){
    echo 123
    local x=22
    echo $x
}

f1
echo "====$x"
[root@openvpn day6]# ./function4.sh 
123
22
====11111
2、每执行一个解释器,都会开启一个解释的shell进程,每个shell进程都有自己的作用域彼此互不干扰
[root@openvpn shell]# x=111  # 该变量仅仅只在当前shell进程中有效,对新的shell进程无影响
[root@openvpn shell]# echo $x
111
[root@openvpn shell]# bash  # 执行bash解释器,则开启一个新的进程,或者干脆直接打开一个新的终端
[root@openvpn shell]# echo $x

[root@openvpn shell]# 
3、需要强调的是:
全局变量的作用范围是当前的 Shell 进程,而不是当前的 Shell 脚本文件,它们是不同的概念。打开一个 Shell 窗口就创建了一个 Shell 进程,打开多个 Shell 窗口就创建了多个 Shell 进程,每个 Shell 进程都是独立的,拥有不同的进程 ID。在一个 Shell 进程中可以使用 source 命令执行多个 Shell 脚本文件,此时全局变量在这些脚本文件中都有效。
[root@openvpn shell]# echo $x

[root@openvpn shell]# cat hello.sh 
#!/bin/bash
function test(){
    x=2222  # 全局变量
}
test

[root@openvpn shell]# source hello.sh  # 在当前shell进程中执行,产生一个全局变量x
[root@openvpn shell]# echo $x  # 在当前shell进程中访问全局变量x,可以看到
2222
[root@openvpn shell]# 
[root@openvpn shell]# 
[root@openvpn shell]# cat aaa.sh 
#!/bin/bash
echo $x

[root@openvpn shell]# source aaa.sh # 在当前shell进程中访问全局变量x,同样可以看到
2222

结论:函数test内的全局变量x早已超越了文件,即全局变量是超越文件的,作用范围是整个当前bash进程

3、环境变量:在当前进程的子进程中都可以使用
全局变量只在当前 Shell 进程中有效,对其它 Shell 进程和子进程都无效。如果使用`export`命令将全局变量导出,那么它就在所有的子进程中也有效了,这称为“环境变量”。

环境变量被创建时所处的 Shell 进程称为父进程,如果在父进程中再创建一个新的进程来执行 Shell 命令,那么这个新的进程被称作 Shell 子进程。当 Shell 子进程产生时,它会继承父进程的环境变量为自己所用,所以说环境变量可从父进程传给子进程。不难理解,环境变量还可以传递给孙进程。
[root@openvpn shell]# export y=333  # 爷爷
[root@openvpn shell]# bash  # 爸爸
[root@openvpn shell]# echo $y
333
[root@openvpn shell]# bash  # 孙子
[root@openvpn shell]# echo $y
333

ps:通过exit命令可以一层一层地退出 Shell。

ps:命令set 和 env

set:显示所有变量
env:环境变量

注意

1、环境变量只能向下传递而不能向上传递,即“传子不传父”。
2、两个没有父子关系的 Shell 进程是不能传递环境变量的
我们一直强调的是环境变量在 Shell 子进程中有效,并没有说它在所有的 Shell 进程中都有效;如果你通过终端创建了一个新的 Shell 窗口,那它就不是当前 Shell 的子进程,环境变量对这个新的 Shell 进程仍然是无效的。
3、环境变量也是临时的
[root@openvpn ~]# ps aux |grep bash$ |grep -v grep
root     123436  0.0  0.1 116356  2960 pts/0    Ss   21:52   0:00 -bash
root     123492  0.0  0.1 116472  2932 pts/0    S    21:54   0:00 bash
root     123520  0.0  0.1 116440  2988 pts/0    S    21:54   0:00 bash

注意:
# -开头的bash代表是在登录终端登录的顶级shell进程
# 非-开头的bash代表的是子shell进程
一旦退出了在终端登录的顶级shell,那么该终端下开启的所有子shell都会被回收,export设置的环境变量随即消失

所以说环境变量也是临时的,如果想设置成永久的,需要将变量写入shell配置文件中才可以,Shell 进程每次启动时都会执行配置文件中的代码做一些初始化工作,如果将变量放在配置文件中,那么每次启动进程都会定义这个变量。请看下一小节

六、 登录shell与非登录shell

承接上一小节末尾留下的疑问,我们首先需要了解一下BASH的两种类型

1、登录shell:就是通过输入用户名 密码后 或 su - 获得的shell

2、非登录shell:则是通过bash命令和脚本开启的shell环境

那么他们有什么区别呢?和我们永久设定环境变量又有什么关系呢?

我们知道在linux里一切皆为文件,同样,shell的属性加载也是写到文件里的
在登陆时就会加载对应文件的内容来初始化shell环境,
非登录与登录区别就在于加载的文件不同 从而导致获得的shell环境不同
我们看看登录shell都加载了那些文件
--> /etc/profile
--> /etc/profile.d/*.sh
--> $HOME/.bash_profile
--> $HOME/.bashrc
--> /etc/bashrc
再看非登录shell加载的文件,非登录shell加载的文件要少很多
--> $HOME/.bashrc
--> /etc/bashrc
--> /etc/profile.d/*.sh

通常,我们会将环境变量设置在 $HOME/.bash_profile

但如果不管哪种登录shell都想使用的变量 可以考虑设置在 $HOME/.bashrc或者/etc/bashrc中,因为它们都属于无论如何都会执行的文件,但是,如果我们真的在这类文件中添加了变量,那么意味着每次执行shell都会重新定义一遍该变量,而定义变量是要耗费内存资源的,这非常不可取,所以我们通常会结合export在/etc/profile文件中声明一个全局变量,这样在每次登录用户时产生的顶级shell里会有一个全局变量,所有的子shell都可以看到了,无需重复定义

[root@openvpn ~]# vim /etc/profile
[root@openvpn ~]# head -2 /etc/profile
# /etc/profile
name="mm"
[root@openvpn ~]# echo $name

[root@openvpn ~]# source /etc/profile  # 可以在当前shell中立即生效,也可以退出后重新登录终端
[root@openvpn ~]# echo $name
mm

七 、作业

开发一个计算器程序如下,引入函数减少代码冗余

#方式一:
[root@openvpn day6]# cat function_cale.sh 
#!/bin/bash

read -p "第一个数:" num1
read -p "第二个数:" num2
read -p "第三个数:" num3

function cale(){
   echo "$1 $2 $3 " =c `echo "$1 $2 $3" |bc`
}
cale $num1 "$num3" $num2

#方式二:
[root@mm ~]# cat b.sh 
#!/bin/bash

echo " ----------------------------------"
echo "|这是一个简单的整数计算器,biubiu  |"
echo " ----------------------------------"
echo

# 接收第一个整数,并校验
while :
do
    read -p  "请输入第一个整数: " num1
    expr $num1 + 0 &> /dev/null
    if [ $? -eq 0 ];then
        break
    else
        echo "必须输入整数"
    fi
done

# 接收第二个整数,并校验
while :
do
    read -p  "请输入第二个整数: " num2
    expr $num2 + 0 &> /dev/null
    if [ $? -eq 0 ];then
        break
    else
        echo "必须输入整数"
    fi
done

# 接收输入的操作
echo "-------------------"
echo "|  1.加法         |"
echo "|  2.减法         |"
echo "|  3.乘法         |"
echo "|  4.除法         |"
echo "-------------------"
read -p "请输入您想执行的操作:" choice
case $choice in
    "1")
        res=`expr $num1 + $num2`
        echo "$num1+$num2=$res"
        ;;
    "2")
        res=`expr $num1 - $num2`
        echo "$num1+$num2=$res"
        ;;
    "3")
        res=`expr $num1 \* $num2`
        echo "$num1*$num2=$res"
        ;;
    "4")
        res=`expr $num1 / $num2`
        echo "$num1/$num2=$res"
        ;;
    *)
        echo "未知的操作"
esac
nginx安装、启动、停止脚本
[root@lb4 ~]# cat nginx.sh 
#!/bin/bash 
. /etc/init.d/functions
#######################
##############检测是否安装nginx##################
function print_action (){
	if (( $2 ==0 ));then
	  action "echo $1" /bin/true
	else
	  action "echo $1" /bin/false
	fi
}

function testing (){
	rpm -q nginx &>/dev/null
	if (( $? == 0 ));then
	  echo "`rpm -q nginx`"
	else
	  echo "未检测到nginx"
	fi
}
##################安装nginx######################
function install (){
	cat >/etc/yum.repos.d/nginx.repo<<EOF
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/\$releasever/\$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/\$releasever/\$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
EOF
	if (( $? == 0 ));then
	  yum install nginx -y &>/dev/null
	  if (( $? == 0 ));then
	    echo "nginx安装完成"
	  else
	    echo "nginx安装失败"
	  fi
	else
	  echo "yum源配置错误请检查"
	fi
}
################检测nginx状态####################
function testing1 (){
	nginx -t &>/dev/null
	if (( $? == 0 ));then
	  echo "nginx配置文件正确,准备检查nginx状态"
	  netstat -lntup|grep  ":80\b" &>/dev/null
	  if (( $? == 0 ));then
	    echo "nginx已经启动,请重新选择或停止或重启"
	  else
	    echo "检测nginx未启动,可以启动nginx"
	    echo "nginx未启动,可以启动nginx"
	  fi
	else
	  echo "配置文件错误,请检查"
	fi
}
#################启动nginx######################
function start_nginx (){
	systemctl start nginx
	if (( $? == 0 ));then
	  echo "Nginx启动成功"
	  print_action "Nginx启动成功" 0
	  exit
	else
	  echo "Nginx启动失败"
	  print_action "Nginx 启动失败" 1
	fi
}
#################重启nginx######################
function restart_nginx (){
        systemctl restart nginx
        if (( $? == 0 ));then
          echo "Nginx 重启成功"
	  print_action "Nginx 重启成功" 0
	  exit
        else
          echo "Nginx 重启失败"
	  print_action "Nginx 重启失败" 1
        fi
}
#################停止nginx######################
function stop_nginx (){
        systemctl stop nginx
        if (( $? == 0 ));then
          echo "Nginx 停止成功"
	  print_action "Nginx 停止成功" 0
        else
          echo "Nginx 停止失败"
	  print_action "Nginx 停止失败" 1
        fi
}
function uninstall (){
	testing1
	stop_nginx
	rpm -q nginx
	if (( $? == 0 ));then
	  echo "开始卸载nginx"
	  rpm -e nginx
	  echo "删除nginx源"
	  rm -rf /etc/yum.repos.d/nginx.repo
	  exit
	else
	  echo "未发现nginx"
	fi
}

function mian (){
	PS3="请选择:"
	select i in install start_nginx restart_nginx stop_nginx uninstall
	do
		case $i in
		install)
			testing
			install
		;;
		start_nginx)
			testing1
			start_nginx
		;;
		restart_nginx)
			testing1
			restart_nginx
		;;
		stop_nginx)
			testing1
			stop_nginx
		;;
		uninstall)
			uninstall
		;;		
		esac
	done
}
mian
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FikL-09-19

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值