Linux操作系统实验五 shell编程基础(四)

简介: Linux操作系统实验五 shell编程基础(四)

任务描述

本关任务:学习掌握什么是 shell 函数的返回值。

相关知识

bash shell 会把函数当作一个小型脚本,运行结束时,会返回一个退出状态码。有 3 种不同的方法来为函数生成退出状态码。

默认退出状态码

默认情况下,函数的退出状态码是函数中最后一条命令返回的退出状态码。在函数执行结束后,可以用标准变量$?来确定函数的退出状态码。 【实例1】已知 test1.sh 脚本内容如下:

  1. #!/bin/bash
  2. func1() {
  3. echo "trying to display a non-existent file"
  4. ls -l badfile
  5. }
  6. echo "testing the function: "
  7. func1
  8. echo "The exit status is: $?"

运行脚本 bash test1.sh 执行结果如下:

  1. testing the function:
  2. trying to display a non-existent file
  3. ls: cannot access badfile: No such file or directory
  4. The exit status is: 2

函数的退出状态码是 2,这是因为函数中的最后一条命令没有成功运行。但你无法知道函数中其他命令中是否成功运行。带着这个疑问再看下面的例子:

【实例2】已知 test2.sh 脚本内容如下:

  1. #!/bin/bash
  2. func1() {
  3. ls -l badfile
  4. echo "trying to display a non-existent file"

  5. }
  6. echo "testing the function: "
  7. func1
  8. echo "The exit status is: $?"

运行脚本 bash test1.sh 执行结果如下:

  1. testing the function:
  2. ls: cannot access badfile: No such file or directory
  3. trying to display a non-existent file
  4. The exit status is: 0

这次,由于函数最后一条语句 echo 运行成功,该函数的退出状态码就是 0,尽管其中有一条命令并没有正常运行。由此可见,使用函数的默认退出状态码,来判断函数是否正常运行是很危险的。幸运的是,有几种办法可以解决这个问题。

return 命令

bash shell 使用 return 命令退出函数,并返回特定的退出状态码。return 命令允许指定一个整数值来定义函数的退出状态码,从而提供了一种简单的途径来设定函数退出状态码。 【实例3】已知 test3.sh 脚本的内容如下:

  1. #!/bin/bash
  2. func1() {
  3. ls -l badfile
  4. echo "trying to display a non-existent file"
  5. return 20
  6. }
  7. echo "testing the function: "
  8. func1
  9. echo "The exit status is: $?"

执行bash test.sh显示如下:

  1. testing the function:
  2. ls: cannot access badfile: No such file or directory
  3. trying to display a non-existent file
  4. The exit status is: 20

这一次变量$?返回的结果是 20,而按照默认的返回 $? 应该是 0,因为函数的最后一条命令正确执行。

任务描述

本关任务:掌握函数传递参数的方法以及如何处理变量。

相关知识

向函数传递参数

我们在 shell 变量那个实训,介绍过了一些特殊变量 $0$1$# 等。在 shell 中,函数可以使用标准的参数环境变量来表示命令行上传给函数的参数。例如,函数名会在$0变量中定义,函数命令行上的任何参数都会通过$1$2等定义。也可以用特殊变量$#,来判断传给函数的参数数目。请参考实例 1:

【实例1】已知脚本 test.sh 的内容如下:

  1. function addem {
  2. if [ $# -eq 0 ] || [ $# -gt 2 ]   # 判断传递给函数的参数个数
  3.     then
  4.     echo -1
  5. elif [ $# -eq 1 ]
  6.     then
  7.     echo $[ $1 + $1 ]
  8. else
  9.     echo $[ $1 + $2 ]
  10. fi
  11. }

  12. echo -n "Adding 10 and 15: "
  13. value=$(addem 10 15)
  14. echo $value

  15. echo -n "Let's try adding just one number: "
  16. value=$(addem 10)
  17. echo $value

  18. echo -n "Now trying adding no numbers: "
  19. value=$(addem)
  20. echo $value

  21. echo -n "Finally, try adding three numbers: "
  22. value=$(addem 10 15 20)
  23. echo $value

输出结果如下:

  1. Adding 10 and 15: 25
  2. Let's try adding just one number: 20
  3. Now trying adding no numbers: -1
  4. Finally, try adding three numbers: -1

这段脚本的意思是:脚本中的 addem 函数首先会检查脚本传给它的参数数目。如果没有任何参数,或者参数多于两个,addem 会返回值 -1;如果只有一个参数,addem 会将参数与自身相加;如果有两个参数,addem 会将它们进行相加。

注意:由于函数使用特殊参数环境变量作为自己的参数值,因此它无法直接获取脚本在命令行中的参数值。下面的【实例2】将会运行失败。

【实例2】:已知 test2.sh 的内容如下:

  1. function badfunc1 {
  2.     echo $[ $1 * $2 ]
  3. }

  4. badfunc1

我在命令行执行 bash test2.sh 10 3,发现脚本报错:

  1. [root@pre-host-work02 opt]# bash a.sh 1 10
  2. a.sh: line 3: *  : syntax error: operand expected (error token is "*  ")

尽管函数 badfunc1 也使用了 $1 和 $2 变量,但它们和脚本主体中的 $1 和 $2 变量并不相同。要在函数中使用这些值,必须在调用函数时手动将它们传过去。我们把脚本改动如下:

  1. function badfunc1 {
  2.     echo $[ $1 * $2 ]
  3. }

  4. badfunc1 $1 $2    

通过将$1$2变量传给函数,它们就能跟其他变量一样供函数使用了,我在命令行执行 bash test2.sh 10 3,结果符合预期:

  1. [root@pre-host-work02 opt]# bash a.sh 10 3
  2. 30
函数中处理变量

函数使用两种类型的变量:

  • 全局变量
  • 局部变量
全局变量

全局变量是在 shell 脚本中任何地方都有效的变量。如果你在脚本的主体部分定义了一个全局变量,那么可以在函数内读取它的值。类似地,如果你在函数内定义了一个全局变量,可以在脚本的主体部分读取它的值。 默认情况下,你在脚本中定义的任何变量都是全局变量。在函数外定义的变量可在函数内正常访问,请看【实例3】。

【实列3】查看 test3.sh 内容,执行cat test3.sh 得到如下内容:

  1. #!/bin/bash

  2. function dbl {

  3. value=$[ $value * 2 ]
  4. }

  5. value=450

  6. dbl
  7. echo "The new value is: $value"

执行脚本输出结果如下:

  1. The new value is: 900

$value变量在函数外定义并被赋值。当 dbl 函数被调用时,该变量及其值在函数中都依然有效。如果变量在函数内被赋予了新值,那么在脚本中引用该变量时,新值也依然有效。但这其实很危险,尤其是如果你想在不同的 shell 脚本中使用函数的话。它要求你清清楚楚地知道函数中具体使用了哪些变量,包括那些用来计算非返回值的变量。请看【实例4】。

【实例4】:

  1. #!/bin/bash

  2. function func1 {
  3.     temp=$[ $value + 5 ]
  4.     result=$[ $temp * 2 ]
  5. }

  6. temp=4
  7. value=6
  8. func1               # 这个时候全局变量temp=6+5及为11,result=22

  9. echo "The result is $result"
  10. if [ $temp -gt $value ]
  11. then
  12. echo "temp is larger"
  13. else
  14. echo "temp is smaller"
  15. fi

执行脚本输出结果如下:

  1. The result is 22
  2. temp is larger
局部变量

无需在函数中使用全局变量,函数内部使用的任何变量都可以被声明成局部变量。要实现这一点,只要在变量声明的前面加上 local 关键字就可以了,比如local temp。或者也可以在变量赋值语句中使用 local 关键字:local temp=$[ $value + 5 ]。 local 关键字保证了变量只局限在该函数中。如果脚本中在该函数之外有同样名字的变量,那么 shell 将会保持这两个变量的值是分离的。现在你就能很轻松地将函数变量和脚本变量隔离开了,只共享需要共享的变量。

【实例5】:

  1. #!/bin/bash
  2. # demonstrating the local keyword
  3. function func1 {
  4.     local temp=$[ $value + 5 ]
  5.     result=$[ $temp * 2 ]
  6. }

  7. temp=4                    
  8. value=6    
  9. func1         #这个时候全局部变量temp=6+5,但是全局变量temp=4.

  10. echo "The result is $result"
  11. if [ $temp -gt $value ]
  12. then
  13. echo "temp is larger"
  14. else
  15. echo "temp is smaller"
  16. fi

执行脚本输出结果如下:

  1. The result is 22
  2. temp is smaller

现在,在 func1 函数中使用 $temp 变量时,并不会影响在脚本主体中赋给$temp变量的值。请重点分析【实例4】和【实例5】中的不同。

编程要求

本关不进行考核,希望大家能够真正的理解:

  • 向函数传递变量的方法;
  • 在函数中使用局部变量和全局变量的区别。 这两点非常重要,希望大家能够在命令行中自行练习本关【实例】中代码,能够充分的理解掌握这两点内容。

任务描述

本关任务:掌握文件包含方法的使用。

相关知识

文件包含方法

在 C,C++,PHP 中都是用 include 来包含文件,Go 和 Java 使用 import 来包含(导入)包,而在 shell 中,很简单,只需要一个点.,然后跟着文件路径及文件名,或者使用source关键字也可以,注意文件路径可以使用绝对路径和相对路径。请看实例 1。

【实例1】已知在同一目录下,有 test1.sh 和 test2.sh 两个脚本,test1.sh 内容如下:

  1. #!/bin/bash
  2. url="www.educoder.net"

同一目录下的 test2.sh 内容如下:

  1. #!/bin/bash
  2. . ./test1.sh   # 第一个.是包含符号,第二个. 表示当前目录下。
  3.               # 这是采用相对路径包含的学法,记得中间要空格。

  4. echo "编程平台地址是:$url"

在命令行执行bash test2.sh,大家想想看这个时候输出的是什么? 输出的结果如下:

  1. 编程平台地址是:www.educoder.net

注意:

  • 变量的定义也要考虑局部变量和全局变量;
  • 如实例1 所示 test1.sh 并不需要赋予执行权限;
  • 注意包含文件的路径。

【实例2】同一目录下 test1.sh、test2.sh、test3.sh 内容如下:

  1. [root@pre-host-work02 opt]# cat test1.sh

  2. #!/bin/bash

  3. test(){

  4.    url='www.educoder.net'

  5. }

  6. [root@pre-host-work02 opt]# cat test2.sh
  7. #!/bin/bash

  8. hello(){

  9.    echo "i am from test3.sh"

  10. }
  11. [root@pre-host-work02 opt]# cat test3.sh
  12. #!/bin/bash
  13.               ##包含多个文件时,一行智能写一个文件,分开写。
  14. . ./test1.sh   ##包含test1.sh  ./test1.sh表示当前目录下
  15. . ./test2.sh   ##包含test2.sh  ./test2.sh表示当前目录下

  16. echo "编程平台:$url"
  17. hello

在命令行执行bash test3.sh ,输出结果如下:

  1. [root@pre-host-work02 opt]# bash test3.sh
  2. 编程平台:
  3. i am from test3.sh
  • 因为在 test1.sh 中 url 变量定义在函数内,即为局部变量,所以包含过来之后不能被调用;
  • test2.sh 中定义了函数 hello,在 test3.sh 通过. 的方式包含进来,通过函数名 hello 的调用,调用了 test2.sh 中的 hello()函数,输出“i am from test3.sh” 。

编程要求

本关任务包含了三个脚本文件,它们分别是 step4/t1.shstep4/t2.shstep4/main.sh,点击右侧“代码文件”即可查看,如下图所示:

  • 根据要求补全 step4/t1.sh 代码。
  • 根据要求补全 step4/main.sh 代码。

相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
目录
相关文章
|
14天前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
39 1
|
2天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
35 13
|
15天前
|
安全 Linux 数据安全/隐私保护
深入Linux操作系统:文件系统和权限管理
在数字世界的海洋中,操作系统是连接用户与硬件的桥梁,而Linux作为其中的佼佼者,其文件系统和权限管理则是这座桥梁上不可或缺的结构。本文将带你探索Linux的文件系统结构,理解文件权限的重要性,并通过实际案例揭示如何有效地管理和控制这些权限。我们将一起航行在Linux的命令行海洋中,解锁文件系统的奥秘,并学习如何保护你的数据免受不必要的访问。
|
16天前
|
搜索推荐 Linux
深入理解Linux操作系统的启动过程
本文旨在揭示Linux操作系统从开机到完全启动的神秘面纱,通过逐步解析BIOS、引导加载程序、内核初始化等关键步骤,帮助读者建立对Linux启动流程的清晰认识。我们将探讨如何自定义和优化这一过程,以实现更高效、更稳定的系统运行。
|
14天前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
15天前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
17天前
|
运维 监控 Linux
Linux操作系统的守护进程与服务管理深度剖析####
本文作为一篇技术性文章,旨在深入探讨Linux操作系统中守护进程与服务管理的机制、工具及实践策略。不同于传统的摘要概述,本文将以“守护进程的生命周期”为核心线索,串联起Linux服务管理的各个方面,从守护进程的定义与特性出发,逐步深入到Systemd的工作原理、服务单元文件编写、服务状态管理以及故障排查技巧,为读者呈现一幅Linux服务管理的全景图。 ####
|
19天前
|
消息中间件 安全 Linux
深入探索Linux操作系统的内核机制
本文旨在为读者提供一个关于Linux操作系统内核机制的全面解析。通过探讨Linux内核的设计哲学、核心组件、以及其如何高效地管理硬件资源和系统操作,本文揭示了Linux之所以成为众多开发者和组织首选操作系统的原因。不同于常规摘要,此处我们不涉及具体代码或技术细节,而是从宏观的角度审视Linux内核的架构和功能,为对Linux感兴趣的读者提供一个高层次的理解框架。
|
1月前
|
安全 Linux 数据安全/隐私保护
Vanilla OS:下一代安全 Linux 发行版
【10月更文挑战第30天】
57 0
Vanilla OS:下一代安全 Linux 发行版
|
29天前
|
NoSQL Linux PHP
如何在不同操作系统上安装 Redis 服务器,包括 Linux 和 Windows 的具体步骤
本文介绍了如何在不同操作系统上安装 Redis 服务器,包括 Linux 和 Windows 的具体步骤。接着,对比了两种常用的 PHP Redis 客户端扩展:PhpRedis 和 Predis,详细说明了它们的安装方法及优缺点。最后,提供了使用 PhpRedis 和 Predis 在 PHP 中连接 Redis 服务器及进行字符串、列表、集合和哈希等数据类型的基本操作示例。
56 4