第七章 函数

1 函数介绍

1.1 什么是函数

  1. 函数就是用来盛放一组代码的容器,函数内的一组代码完成一个特定的功能,称之为一组代码块,调用函数便可触发函数内代码块的运行,这可以实现代码的复用,所以函数又可以称之为一个工具

1.2 为何要用函数

  1. 1、减少代码冗余
  2. 2、提升代码的组织结构性和可读性
  3. 3、增强扩展性

2 函数的基本使用

  1. 1、具备某一功能的工具=>函数
  2. 2、事先准备好工具=>函数的定义
  3. 3、遇到应用场景,拿来就用=>函数的调用
  4. 4、所以函数的使用原则:先定义,后调用

2.1 定义函数

  1. #语法:
  2. [ function ] funname [()]
  3. {
  4. 命令1;
  5. 命令2;
  6. 命令3;
  7. ...
  8. [return int;]
  9. }
  10. # 示例1:完整写法
  11. function 函数名() {
  12. 函数要实现的功能代码
  13. }
  14. # 示例2:省略关键字(),注意此时不能省略关键字function
  15. function 函数名 {
  16. 函数要实现的功能代码
  17. }
  18. # 示例3:省略关键字function
  19. 函数名() {
  20. 函数要实现的功能代码
  21. }

2.2 调用函数

  1. # 语法:
  2. 函数名 # 无参调用
  3. 函数名 参数1 参数2 # 有参调用
  4. # 示例
  5. function test1(){
  6. echo "执行第一个函数"
  7. }
  8. function test2 {
  9. echo "执行第二个函数"
  10. }
  11. test3(){
  12. echo "执行第三个函数"
  13. }
  14. # 调用函数:直接引用函数名字即调用函数,会触发函数内代码的运行
  15. test1
  16. test2
  17. test3

3 函数参数

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

  1. #函数体内和调用函数对应的参数一定要一致
  2. test(){
  3. echo "我要传递参数咯$1 $2"
  4. }
  5. test $1 $2

3.1 温故而知新之前的参数

  1. #1、当$*和$@没有被引号引用起来的时候,它们确实没有什么区别,都会把位置参数当成一个个体。
  2. #2、"$*" 会把所有位置参数当成一个整体(或者说当成一个单词),如果没有位置参数,则"$*"为空,如果有两个位置参数并且分隔符为空格时,"$*"相当于"$1 $2"
  3. #3、"$@" 会把所有位置参数当成一个单独的字段,如果没有位置参数,则"$@"展开为空(不是空字符串,而是空列表),如果存在一个位置参数,则"$@"相当于"$1",如果有两个参数,则"$@"相当于"$1" "$2"等等
  1. echo "=======函数test1==========="
  2. function test1(){
  3. echo "$*" # 111 222 333 444 555
  4. echo "$@" # 111 222 333 444 555
  5. echo $# # 5
  6. echo $$ # 87380
  7. echo $? # 0
  8. }
  9. test1 111 222 333 444 555
  10. echo "=======函数test2==========="
  11. function test2(){
  12. for i in "$*" # 注意:$*不加引号结果与$@一模一样
  13. do
  14. echo $i
  15. done
  16. }
  17. test2 111 222 333 "444 555" # 注意"444 555"被引号引成了一个参数
  18. # 运行结果为:111 222 333 444 555
  19. echo "=======函数test3==========="
  20. function test3(){
  21. for i in "$@" # 注意:$*不加引号结果与$@一模一样
  22. do
  23. echo $i
  24. done
  25. }
  26. test3 111 222 333 "444 555" # 注意"444 555"被引号引成了一个参数
  27. # 运行结果为:
  28. # 111
  29. # 222
  30. # 333
  31. # 444 555

4 函数的返回值

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

  1. test1(){
  2. echo 111
  3. return
  4. echo 222
  5. return
  6. echo 333
  7. }
  8. test1
  9. #执行结果:111

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

5 变量的作用域

Shell变量有全局变量、局部变量、环境变量。

5.1 局部变量

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

  1. [root@localhost shell]# cat hello.sh
  2. #!/bin/bash
  3. # 定义函数
  4. test(){
  5. local x=111
  6. echo "函数内访问x:$x"
  7. }
  8. # 调用函数
  9. test
  10. echo "在函数外即全局访问x:$x" # 无法访问函数内的局部变量
  11. #执行结果:
  12. 函数内访问x111
  13. 在函数外即全局访问x

5.2 全局变量

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

  1. [root@localhost shell]# cat hello.sh
  2. #!/bin/bash
  3. x=2222
  4. function test(){
  5. echo "函数内访问x:$x"
  6. }
  7. test
  8. echo "在函数外即全局访问x:$x"
  9. #执行结果
  10. 函数内访问x2222
  11. 在函数外即全局访问x2222
  1. #注意:
  2. 1、在函数内定义的变量,如果没有用local声明,那么默认也是全局变量。
  3. 2、每执行一个解释器,都会开启一个解释的shell进程,每个shell进程都有自己的作用域彼此互不干扰
  4. 3、全局变量的作用范围是当前的 Shell 进程,而不是当前的 Shell 脚本文件,它们是不同的概念。打开一个 Shell 窗口就创建了一个 Shell 进程,打开多个 Shell 窗口就创建了多个 Shell 进程,每个 Shell 进程都是独立的,拥有不同的进程 ID。在一个 Shell 进程中可以使用 source 命令执行多个 Shell 脚本文件,此时全局变量在这些脚本文件中都有效。

5.3 环境变量

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

环境变量被创建时所处的 Shell 进程称为父进程,如果在父进程中再创建一个新的进程来执行 Shell 命令,那么这个新的进程被称作 Shell 子进程。当 Shell 子进程产生时,它会继承父进程的环境变量为自己所用,所以说环境变量可从父进程传给子进程。不难理解,环境变量还可以传递给孙进程。

  1. [root@localhost shell]# export y=333 # 爷爷
  2. [root@localhost shell]# bash # 爸爸
  3. [root@localhost shell]# echo $y
  4. 333
  5. [root@localhost shell]# bash # 孙子
  6. [root@localhost shell]# echo $y
  7. 333
  8. ps:通过exit命令可以一层一层地退出 Shell
  1. #注意:
  2. 1、环境变量只能向下传递,不能向上传递,即"传子不传父"
  3. 2、两个没有父子关系的shell进程是不能传递环境变量的
  4. 我们一直强调的是环境变量在shell子进程中有效,并没有说它在所有的shell进程中都有效。如果你通过终端创建一个新的shell窗口,那它就不是当前shell的子进程,环境变量对这个新的shell进程仍然是无效的。
  5. 3、环境变量也是临时的,如果想永久有效,可以放到/etc/profile或者/etc/bashrc文件中

6 登录shell与非登录shell

首先我们要知道BASH的两种类型:

  • 登录shell:就是通过输入用户名和密码后,或su -获得的shell
  • 非登录shell:则是通过bash命令和脚本开启的shell环境

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

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

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

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

  1. [root@localhost ~]# vim /etc/profile
  2. [root@localhost ~]# head -2 /etc/profile
  3. # /etc/profile
  4. name="xujun"
  5. [root@localhost ~]# echo $name
  6. [root@localhost ~]# source /etc/profile # 可以在当前shell中立即生效,也可以退出后重新登录终端
  7. [root@localhost ~]# echo $name
  8. xujun