shell脚本之如使用return和exit

2023-11-11

shell脚本之如使用return和exit

return和exit各有用途,合理使用可以使shell编程更规范可控。

一、exit和return基础

1、return是一个关键字; exit是一个函数。
2、return是编程语言级别,它表示调用堆栈的返回;exit是系统调用级别,它表示了一个进程的结束。
3、return是函数的退出(返回);exit是进程的退出。

  • exit 0
      正常运行程序并退出程序。使用echo $?返回0,也就是说调用环境认为你的程序执行正常。
  • exit 1
      非正常运行导致退出程序,也可以是其他数字,例如exit -1。系统程序对于程序运行错误是有约定含义的,不为 0 就表示程序运行出错。调用环境根据这个返回值,判断你的程序运行是否正常。

return 0用于函数中,表示函数执行成功并返回 0;而exit 0 则表示当前程序执行成功并且直接退出当前执行脚本或程序。

return -1 表示函数执行失败返回错误;exit 1 (或大于 1)表示程序执行失败并退出程序。

总结: exit用于退出整个shell脚本进程。

EXIT退出指令举例

"exit"命令是终止Bash shell脚本的最常见方法之一。它允许脚本在执行过程中的任何时候退出,并且可以使用可选的退出代码来表示脚本终止的原因。

# 检查一个文件是否存在
if [ -f "myfile.txt" ]; then
  echo "The file exists"
  exit 0 # 成功的退出
else
  echo "The file does not exist"
  exit 1 # 异常的退出并附带说明
fi

在这个例子中,脚本使用“-f”测试运算符检查一个名为“myfile.txt”的文件是否存在。如果文件存在,脚本会向控制台打印一条消息,并使用“exit”命令以成功代码0退出。如果文件不存在,脚本会打印不同的消息,并使用错误代码1退出。

“exit”命令还可以用于处理脚本执行过程中的错误或意外情况。例如,假设一个脚本需要访问可能不可用的资源,如网络服务或数据库。在这种情况下,脚本可以使用“exit”命令以错误消息和适当的退出代码优雅地终止。

在函数中使用return语句退出举例

#!/bin/bash

# 定义一个函数并返回数字之和
function add_numbers {
  local num1=$1
  local num2=$2
  local sum=$((num1 + num2))
  return $sum
}

# 调用函数并打印结果
read_file "myfile.txt"

# 调用函数并打印结果
add_numbers 3 71
result=$?
echo "3 + 71 = $result" 

在这个例子中,脚本定义了一个名为“add_numbers”的函数,它接受两个参数并返回它们的总和。在函数内部,使用“return”命令以总和作为返回值退出。

**“return”命令也可以用于处理函数内部的错误或意外情况。**例如,假设一个函数需要从一个文件中读取数据,但是该文件不存在。在这种情况下,函数可以使用“return”命令以错误代码和错误消息退出。

#!/bin/bash

# 定义一个函数读取文件
function read_file {
  local file=$1
  if [ ! -f "$file" ]; then
    echo "Error: File $file not found"
    return 1
  fi
  cat $file
}

如果文件不存在,函数将打印一个错误消息并返回错误代码1,该代码可以由调用脚本或进程用于相应地处理错误。

**在函数内使用“return”命令是一个很好的方式,可以正确退出函数并将其结果传达给脚本的其他部分或调用进程。**通过使用适当的返回值和错误代码,脚本可以处理意外情况,并提高其整体稳健性和可靠性。

二、最佳实践

  • 函数必须使用return退出,不能用exit。
  • 脚本主体逻辑使用return设置退出码,最后用exit退出脚本。
  • 如果需要精确控制退出码,脚本中的各处逻辑都建议用return。
  • 发生不可处理的错误时,可以直接用exit终止脚本。

三、子脚本返回非零状态码时导致主控脚本退出中断的问题

问题描述

主控脚本A,循环调用子脚本B、C,执行B子脚本exit,发现主控脚本A循环中断了,C子脚本没有调用

[重要]问题分析

开始以为:
exit 会导致整个脚本进程结束,主控制脚本循环也会被中断。测试发现子脚本 exit非零,会导致主控脚本也直接退出

其实最后发现,是我主控日志打印,根据子脚本的 $? 结果,非0时自己exit 的,并不是因为子脚本非0 exit,主控就一定退出!!! 是一场乌龙~

结论:子脚本的exit不会直接导致主脚本退出,主程序要自行处理$?并决定下一步操作。

不过可以这里总结出如下2个比较有用的shell实践技巧:

  • 善用exit $?
  • 使用 sh 调子脚本

善用exit $?

使用函数+return 方式返回状态码,最后 exit $? 方式退出脚本

完整示例demo:

start_mongodb(){
  $MONGODB_BIN_DIR/mongod -f $MONGO_CONF
  if [ $? -eq 0 ]; then
    echo "MongoDB started successfully"
    return 0
  else
    echo "Failed to start MongoDB"
    return 1
  fi
}

stop_mongodb(){
  $MONGODB_BIN_DIR/mongod -f $MONGO_CONF --shutdown
  if [ $? -eq 0 ]; then  
    echo "MongoDB stopped successfully"
    return 0
  else
    echo "Failed to stop MongoDB"
    return 1  
  fi
}

status(){
  if [ -f $MONGODB_PIDFILE ]; then
    echo "MongoDB is running, PID: $(cat $MONGODB_PIDFILE)"
    return 0
  else
    echo "MongoDB is stopped"
    return 1
  fi
}


function control_mongodb(){

  case $1 in
    start)
      start_mongodb  
      ;;
  
    stop)
      stop_mongodb
      ;;

    restart)
      stop_mongodb
      start_mongodb
      ;;

    status)
      status
      ;;

    *)
      echo "Usage: $0 {start|stop|restart|status}"
      exit 1
  esac

  return $? 
}


# 调用函数
control_mongodb $1

exit $?

在Linux shell脚本中,exit $? 表示使用上一个命令的退出状态码来退出当前shell脚本。
$? 是一个特殊变量,它保存了上一个执行的命令或者函数的退出状态码
退出状态码0表示成功执行,非0通常表示失败或错误。
exit $? 的具体作用是:

  • $? 获取上一个命令的退出码
  • exit 使脚本退出
  • 将上一个命令的退出码作为脚本的最终退出码

使用 sh 调子脚本,需要注意事项

使用 sh 调子脚本,通常有下面的几种使用场景:

  1. 强制子脚本在一个干净的环境中运行。sh 会启动一个新的shell实例,不会继承当前shell的任何自定义设置、变量等,可以提供一个干净隔离的运行环境。
  2. 为子脚本设置特定的shell。直接调用子脚本时,使用当前shell(通常是bash),但有时需要指定为sh、csh等其他shell。
  3. 在脚本中改变目录时,调用子脚本使用相对路径。sh 重置了工作目录,使相对路径生效。
  4. 当子脚本需要另一个版本的shell时。直接调用继承当前shell,但 sh 可以指定所需的shell。
  5. 在升级系统shell时,保证子脚本向后兼容。直接调用使用新shell可能出错,但 sh 调用可保持原有的shell。
  6. 出于安全考虑,不信任子脚本,使用 sh 加沙箱隔离
  7. 在守护进程中调用脚本,需要一个干净可预测的新shell环境。
  8. 一些老的脚本依赖 sh 调用,直接转换会破坏原有行为。

总之,使用 sh 调用主要是为了精细控制子脚本的执行环境,符合特定的兼容性要求等。但通常需要时,直接调用更简单。

其实一般情况下,不推荐使用 sh,因为有时通过sh 调子脚本会有问题,例如:sh 执行脚本时, kill 命令出现了非法选项的错误

dash 和 bash 在处理 kill 命令选项上有一些细微区别。
dash 不支持不带参数的 kill 命令,也不支持kill -s 信号的方式,需要直接写信号名。
所以在 bash 下可以正常执行:

kill -SIGTERM pid
kill -s TERM pid 

但在 dash 下,需要写成:

kill -TERM pid

你的脚本本身没有问题,但当通过 sh 调用时,使用了 dash 的限制导致了错误。
可以修改为直接调用脚本,不使用 sh:

./nginx_ctrl.sh stop

或者在脚本中用 kill -TERM 替代 kill -s TERM。
这种 shell 的差异导致的错误可以引起注意,如果兼容 sh/dash 也需要调整下参数格式。

你可以通过以下几种方法来判断系统默认的 sh 是什么 shell:

  1. 直接运行 sh,看看默认进入的是什么 shell:
$ sh
$ echo $SHELL
/bin/dash

如果打印出来是 /bin/dash,那么默认就是 dash。
2. 查看 /bin/sh 的符号链接所指向的文件:

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Mar 15  2020 /bin/sh -> dash

如果是指向 dash 就表示默认是 dash。

Ubuntu 和 Debian 默认就是 dash。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

shell脚本之如使用return和exit 的相关文章

  • 分叉/多线程进程|重击

    我想让我的代码的一部分更加高效 我正在考虑让它分叉成多个进程 并让它们一次执行 50 100 次 而不是只执行一次 例如 伪 for line in file do foo foo2 foo3 done 我希望这个 for 循环运行多次 我
  • jq中如何分组?

    这是 json 文档 name bucket1 clusterName cluster1 name bucket2 clusterName cluster1 name bucket3 clusterName cluster2 name bu
  • shell-out 值到 md5(加密)函数

    我正在寻找一种解决方案 我正在构建 JSON 记录 并需要在 JQ 中生成一些文本 但将此文本通过管道传输到 MD5 求和函数并将其用作键的值 echo first John last Big jq id first last md5 通过
  • Linux 上有关 getBounds() 和 setBounds() 的 bug_id=4806603 的解决方法?

    在 Linux 平台上 Frame getBounds 和 Frame setBounds 的工作方式不一致 这在 2003 年就已经有报道了 请参见此处 http bugs java com bugdatabase view bug do
  • Linux 上的用户空间能否实现本机代码的抢占式多任务处理?

    我想知道是否可以在 Linux 用户空间的单个进程中实现本机代码的抢占式多任务处理 也就是说 从外部暂停一些正在运行的本机代码 保存上下文 交换到不同的上下文 然后恢复执行 所有这些都由用户空间精心安排 但使用可能进入内核的调用 我认为这可
  • 我如何知道 C 程序的可执行文件是在前台还是后台运行?

    在我的 C 程序中 我想知道我的可执行文件是否像这样在前台运行 a out 或者像这样 a out 如果你是前台工作 getpgrp tcgetpgrp STDOUT FILENO or STDIN FILENO or STDERR FIL
  • 进程退出后 POSIX 名称信号量不会释放

    我正在尝试使用 POSIX 命名信号量进行跨进程同步 我注意到进程死亡或退出后 信号量仍然被系统打开 在进程 打开它 死亡或退出后是否有办法使其关闭 释放 早期的讨论在这里 当将信号量递减至零的进程崩溃时 如何恢复信号量 https sta
  • 子目录中的头文件(例如 gtk/gtk.h 与 gtk-2.0/gtk/gtk.h)

    我正在尝试使用 GTK 构建一个 hello world 其中包括以下行 include
  • 如何构建任务“gems:install”

    我正在将 Rails 应用程序部署到 Linux 服务器 并且缺少一些 rake 任务 包括 rake gems install 和 rake db 我正在运行来自 GEM 的 Rails 2 3 4 为什么是这样 我该如何解决 我可以以某
  • Urwid:使光标不可见

    我正在使用 urwid 它是一个用于在 ncurses 中设计终端用户界面的 Python 框架 但有一件事我在 urwid 中无法做到 而这在 Curses 中很容易做到 使光标不可见 现在 选择按钮时光标是可见的 而且看起来很丑 有办法
  • 高效的内存屏障

    我有一个多线程应用程序 其中每个线程都有一个整数类型的变量 这些变量在程序执行期间递增 在代码中的某些点 线程将其计数变量与其他线程的计数变量进行比较 现在 我们知道在多核上运行的线程可能会无序执行 一个线程可能无法读取其他线程的预期计数器
  • 如何查询X11显示分辨率?

    这似乎是一个简单的问题 但我找不到答案 如何查询 通过 X11 存在哪些监视器及其分辨率 查看显示宏 http tronche com gui x xlib display display macros html and 屏幕宏 http
  • 在 C++ linux 中将 STRINGS 写入串口

    我知道这个问题遍布互联网 但仍然没有任何东西能让我完全解决这个问题 我想用 C linux 将数据写入 Propeller 板的串行端口 从控制台获取输入时程序运行良好 但是当我向它写入字符串时总是返回 ERROR Invalid comm
  • Capistrano 3 部署无法连接到 GitHub - 权限被拒绝(公钥)

    我使用 Capistrano v3 和 capistrano symfony gem 设置了以下部署脚本 我正在使用 Ubuntu 14 4 部署到 AWS EC2 实例 我正在连接从 AWS 下载的 pem 文件 我的deploy rb中
  • git 错误:无法处理 https

    当我尝试使用 git clone 时https xxx https xxx我收到以下错误我不处理协议 https 有人可以帮我吗 完整消息 dementrock dementrock A8Se git 克隆https git innosta
  • 在 unix 中编译 dhrystone 时出错

    我是使用基准测试和 makefile 的新手 我已经从下面的链接下载了 Dhrystone 基准测试 我正在尝试编译它 但我遇到了奇怪的错误 我尝试解决它 但没有成功 有人可以帮助我运行 dhrystone 基准测试吗 以下是我尝试编译的两
  • 使用 plistBuddy 获取值数组

    var keychain access groups declare a val usr libexec PlistBuddy c Print var sample plist echo val echo val 0 Ouput Array
  • 如何使用 sed 将空格替换为 \(space)?

    当我使用 sed 将所有空格替换为 X 时 该命令有效 命令为 sed s X g filelist tmp 但是 当我尝试用 space 替换所有出现的空格时 代码是 sed s g filelist tmp 这不起作用 我究竟做错了什么
  • 让 TeXstudio 在 linux mint 中工作:找不到文件“url.sty”。

    刚刚切换到 Linux Mint 以前的顽固 Windows 用户 我在尝试安装 TeXstudio 时遇到一些问题 Sudo apt get install texstudio 给了我一个正确的安装 至少 我是这么认为的 但是当我尝试构建
  • 嵌入式linux编写AT命令

    我在向 GSM 模块写入 AT 命令时遇到问题 当我使用 minicom b 115200 D dev ttySP0 term vt100 时它工作完美 但我不知道如何在 C 代码中做同样的事情 我没有收到任何错误 但模块对命令没有反应 有

随机推荐

  • 数字图像处理 第五章图像复原与重建

    文章目录 数字图像处理 第五章 图像复原与重建 引言 5 1背景知识 5 2图像退化与复原 5 3噪声模型 高斯噪声 椒盐噪声 瑞丽噪声 伽马噪声 5 4只存在噪声的复原 空间滤波 均值滤波 统计排序滤波 自适应滤波 数字图像处理 第五章
  • java多线程总结:原理结合源码详细讲解 - 简单实用

    执行策略 线程执行的方式 串行执行 比如 医院给病人看病的时候 可以让所有的病人都拍成一个队形 让一个医生统一的看病 医生 线程 病人看病 任务 这种一个医生给一群站好队形的病人看病 映射到java就相当于 单线程串行执行任务 映射到我们j
  • 理解D3D—(2)最多混合几层texture

    理解D3D 2 最多混合几层texture 先提出问题 Q 要是模型有很多层贴图 再加上shadow map 还有ssao 岂不是会不够用了 A 参考资料 IDirect3DDevice9 SetTexture Assigns a text
  • Linux静态库与动态库

    文章目录 一 源代码的组织 二 静态库 三 动态库 四 静态库与动态库的优缺点 1 优点 2 缺点 五 动态库的优缺点 1 优点 2 缺点 六 编译的优先级 七 版权声明 一 源代码的组织 我们通常把公用的自定义函数和类从主程序中分离出来
  • mnt/hgfs 共享文件夹文件丢失不见

    Vmware centos mnt hgfs 共享文件夹文件丢失 我在强制虚拟机关机后 再次打开发现原先与windows共享的文件夹没有文件了 熟悉的同学都知道windows和Vmware如何共享文件夹 使用vmware tools 在li
  • 硅基生命之漫谈-1:天马行空

    1 身 生理 硬件 1 1 分解与组合 原子 分子 有机分子 基因 器官 组织 人体 1 2 五官 眼 摄像头 耳 拾音器 鼻 各种气体床传感器 口 发声器 舌 味道传感器 1 3 人体八大系统 运动系统 手 足 身体 运动 神经系统 眼
  • ChatGPT对教育发展方向的影响

    ChatGPT 对教育发展的影响主要体现在以下几个方面 智能化教育 通过 ChatGPT 这样的语言模型 可以提供智能的教育辅助 如自动纠错 智能问答等 提高教学效率和学习效果 在线教育 通过使用 ChatGPT 可以在线提供教育服务 消除
  • UVM环境(env)树形结构

    UVM验证环境的组成 sequencer 负责将数据转给 driver driver 负责数据的 发送 driver 有时钟 时序的概念 agent 其实只是简单的把 driver monitor 和 sequencer 封装在一起 age
  • 微信小程序配置不同页面title

    1 配置全局title 在app json中window配置navigationBarTitleText 2 配置不同页面title 在页面的json文件中单独配置navigationBarTitleText 注意 如果保存之后不刷新执行以
  • spring中的@Configuration配置类和@Component

    在Spring的开发工作中 基本都会使用配置注解 尤其以 Component及 Configuration为主 当然在Spring中还可以使用其他的注解来标注一个类为配置类 这是广义上的配置类概念 但是这里我们只讨论 Component和
  • Redis常用数据结构及应用场景

    1 概述 Redis 一个开源的基于键值对 Key Value NoSQL 数据库 使用 ANSIC 语言编写 支持网络 基于内存但支持持久化 性能优秀 并提供多种语言的 API 我们要首先理解一点 我们把 Redis 称为 KV 数据库
  • Python文件操作常用的API(open函数使用)

    打开文件 获取文件对象 fp open file mode encoding file 要操作的文件路径 使用的时候注意目录的拼接 mode 打开方式 encoding 编码方式 关于第一个参数file需要注意的是 文件的路径 使用的时候要
  • 30个简单又实用的Python代码

    作者 billy 版权声明 著作权归作者所有 商业转载请联系作者获得授权 非商业转载请注明出处 1 重复元素判定 def all unique lst return len lst len set lst x 1 1 2 2 3 2 3 4
  • 冬至,这一天,与汤圆饺子一样暖心的——还有我呢

    冬至 送你一碗万事如意的汤圆 一碗幸福安康的饺子 用真情煮水 以祝福调味 让你吃出一天好运气 一年好福气 视腾与你相伴 这一天 与汤圆饺子一样暖心的 还有我呢
  • 下载对应版本的torch-geometric

    本篇主要针对使用torch geometric读取数据时出现错误 The data object was created by an older version of PyG If this error occurred while loa
  • css fixed定位失效问题

    css fixed定位失效问题 fixed定位失效问题的原因多半在于fixed定位的元素其祖先的transform perspective 或 filter属性非none 导致fixed定位不再参照视口进行定位 而是参照祖先进行定位 MDN
  • c语言int型能储存的最大数,int类型在内存中的存储方式

    Q1 int类型在内存中是以何种方式存储的 要解决这个问题 我们需要首先比较深入地理解下int类型 本文中的int类型的相关数据都以32位操作系统下的VC 6 0编译器环境为准 在下表中可以看到 int类型表示带有符号的整型 而unsign
  • AMD第四代EPYC拼上最后一块拼图 智能边缘市场烽烟再起

    2023年5月初 有媒体爆料称 代号 Siena 锡耶纳 的AMD EPYC 霄龙 8004系列处理器已通过了SATA IO的验证 当时的EPYC 8004可谓犹抱琵琶半遮面 9月18日 AMD正式推出EPYC 8004系列处理器 千呼万唤
  • JVM-内存结构

    目录 1 什么是JVM 2 jvm的内存结构 2 1程序计数器 2 1 1定义 2 1 2Java程序的运行原理 2 2虚拟机栈 2 2 1定义 2 2 2栈内存溢出 2 3线程运行诊断 3 本地方法栈 4 堆 4 1定义 4 2堆内存溢出
  • shell脚本之如使用return和exit

    文章目录 shell脚本之如使用return和exit 一 exit和return基础 EXIT退出指令举例 在函数中使用return语句退出举例 二 最佳实践 三 子脚本返回非零状态码时导致主控脚本退出中断的问题 问题描述 重要 问题分析