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的迁移。
目录
相关文章
|
2天前
|
Linux 程序员 开发者
源社区的兴起:从“代码隐士”到Linux引领的“全球编程嘉年华”
在编程的古老森林中,曾有“代码隐士”默默耕耘,惧怕智慧外泄。直到“开源”春风拂过,源社区如全球编程嘉年华盛开!开源文化颠覆了“独门秘籍”的传统,像“武林秘籍共享”般在网络上公开,鼓励知识传播与智慧碰撞。程序员组队开发,分享代码,提升科技实力。Linux则从“首席大厨”变身为“总导演”,以强大内核调制出诱人应用,引领潮流并推动技术创新。加入这场没有血腥厮杀,只有知识盛宴的“编程版《饥饿游戏》”吧!与全球开发者共享编程的乐趣与成就感!别忘了带上你的“独门秘籍”,可能下一个改变世界的创意就在其中!
18 7
|
16天前
|
监控 Shell Linux
探索Linux操作系统下的Shell编程之魅力
【8月更文挑战第4天】本文旨在通过一系列精心设计的示例和分析,揭示在Linux环境下进行Shell编程的独特之处及其强大功能。我们将从基础语法入手,逐步深入到脚本的编写与执行,最终通过实际代码案例展现Shell编程在日常系统管理和自动化任务中的应用价值。文章不仅适合初学者构建扎实的基础,同时也为有一定经验的开发者提供进阶技巧。
29 11
|
16天前
|
Ubuntu Linux 开发工具
深入探索Linux内核模块编程
【8月更文挑战第4天】在这篇文章中,我们不仅将探讨Linux内核模块的基础知识,还将通过一个实际的例子来展示如何编写一个简单的内核模块。我们将从理论出发,逐步过渡到动手实践,最终实现一个可以在Linux系统上运行的模块。文章的目标是为读者提供足够的信息和知识,以便他们能够自己编写内核模块,从而对操作系统的内部工作原理有更深入的了解。
|
1月前
|
弹性计算 运维
阿里云操作系统智能助手OS Copilot实验测评报告
**OS Copilot 产品体验与功能反馈摘要** 运维人员发现OS Copilot易上手,文档清晰,助其高效排查故障(8/10分)。愿意推荐并参与开源开发。亮点在于知识问答,能快速筛选答案。相较于竞品,优点是新手友好、文档清晰,但功能扩展性待增强。期望增加系统错误排查与解决方案,并集成ECS等,以优化系统安装流程。
阿里云操作系统智能助手OS Copilot实验测评报告
|
23天前
|
Linux C语言 Windows
C语言文件编程-Linux环境下运行
本文介绍了在Linux环境下使用C语言进行文件编程时的两种主要接口:C标准库函数与Linux系统调用。C标准库提供了`fopen`, `fread`, `fwrite`, 和 `fclose`等函数,适用于普通文件操作;而Linux系统调用如`open`, `read`, `write`, 和 `close`则更适合处理设备文件,同时也可用于普通文件。这两种方法的主要区别在于前者使用文件指针,后者使用文件描述符。文章还给出了两个示例程序:一个使用C标准库函数实现文件复制,另一个则使用Linux系统调用完成相同任务。
20 2
|
3天前
|
Shell Linux 程序员
详解shell 运行原理及Linux权限
详解shell 运行原理及Linux权限
|
1月前
|
Shell Linux C语言
|
10月前
|
网络协议 Shell Linux
【Linux】shell编程基础(超详细,入门看这一篇就够了)(下)
【Linux】shell编程基础(超详细,入门看这一篇就够了)(下)
93 0
|
3月前
|
存储 Shell C语言
shell脚本 编程 变量 基本入门(详解)
shell脚本 编程 变量 基本入门(详解)
|
10月前
|
Java Shell Linux
【Linux】shell编程基础(超详细,入门看这一篇就够了)(上)
【Linux】shell编程基础(超详细,入门看这一篇就够了)
658 0

热门文章

最新文章