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 的相关文章

  • 使用 \r 并打印一些文本后如何清除控制台中的一行?

    对于我当前的项目 有一些代码很慢并且我无法使其更快 为了获得一些关于已完成 必须完成多少的反馈 我创建了一个进度片段 您可以在下面看到 当你看到最后一行时 sys stdout write r100 80 n I use 80覆盖最终剩余的
  • tcpdump 是否受 iptables 过滤影响?

    如果我的开发机器有iptables规则到FORWARD一些数据包 这些数据包是否被 tcpdump 捕获 我有这个问题 因为我知道存在其他链称为INPUT如果数据包路由到 它会过滤发往应用程序的数据包FORWARD链 它会到达吗tcpdum
  • 就分页分段内存而言的程序寿命

    我对 x86 Linux 机器中的分段和分页过程有一个令人困惑的概念 如果有人能澄清从开始到结束所涉及的所有步骤 我们将很高兴 x86 使用分页分段内存技术进行内存管理 任何人都可以解释一下从可执行的 elf 格式文件从硬盘加载到主内存到它
  • linux-x64 二进制文件无法在 linuxmusl-x64 平台上使用错误

    我正在安装Sharp用于使用 package json 的 Nodejs 项目的 docker 映像上的映像压缩包 当我创建容器时 我收到有关 Sharp 包的以下错误 app node modules sharp lib libvips
  • 执行“minikube start”命令时出现问题

    malik malik minikube start minikube v1 12 0 on Ubuntu 18 04 Using the docker driver based on existing profile Starting c
  • 并行运行 shell 脚本

    我有一个 shell 脚本 打乱大型文本文件 600 万行和 6 列 根据第一列对文件进行排序 输出 1000 个文件 所以伪代码看起来像这样 file1 sh bin bash for i in seq 1 1000 do Generat
  • 使用 MAX_ORDER / 包含 mmzone.h

    根据https www kernel org doc Documentation networking packet mmap txt https www kernel org doc Documentation networking pa
  • 如何设置 tmux 在启动时打开指定的窗口?

    如何设置 tmux 使其在启动时打开指定的窗口 您可以编写一个小 shell 脚本来启动 tmux 以及所需的程序 我在一个名为 dev tmux 的 shell 脚本中包含以下内容 开发环境 bin sh tmux new session
  • 检查 Git 中是否需要 pull

    如何检查远程存储库是否已更改并且需要拉取 现在我使用这个简单的脚本 git pull dry run grep q v Already up to date changed 1 但它比较重 有没有更好的办法 理想的解决方案是检查所有远程分支
  • SVN 提交后挂钩在提交后不会运行

    我的服务器上设置了 SVN 存储库 并且遇到提交后问题 我在 iMac 上使用 SmartSVN 作为客户端 我通过 SmartSVN 的 ssh svn 连接 我能够成功连接到 SVN 并对其进行更改 但从 SVN 客户端提交后 我的提交
  • 如何在 Linux 中使用 C 语言使用共享内存

    我的一个项目有点问题 我一直在试图找到一个有据可查的使用共享内存的例子fork 但没有成功 基本上情况是 当用户启动程序时 我需要在共享内存中存储两个值 当前路径这是一个char and a 文件名这也是char 根据命令参数 启动一个新进
  • Mac OS X 上的 /proc/self/cmdline / GetCommandLine 等效项是什么?

    如何在不使用 argc argv 的情况下访问 Mac OS X 上的命令行 在 Linux 上 我会简单地阅读 proc self cmdline or use GetCommandLine在 Windows 上 但我找不到 Mac OS
  • 为什么opencv videowriter这么慢?

    你好 stackoverflow 社区 我有一个棘手的问题 我需要你的帮助来了解这里发生了什么 我的程序从视频采集卡 Blackmagic 捕获帧 到目前为止 它工作得很好 同时我用 opencv cv imshow 显示捕获的图像 它也工
  • 每次重新运行终端时,我都必须输入 export PATH=~/anaconda/bin:"$PATH"

    我已经安装了 Anaconda for Mac 但出现了一些问题 当我输入命令时which conda or which ipython I get conda not found and ipython not find 然后我找到这个命
  • 在生产服务器上使用 Subversion 使文件生效的最佳方法是什么?

    目前我已经设置了 subversion 这样当我在 Eclipse PDT 中进行更改时 我可以提交更改 它们将保存在 home administrator 中项目文件 该文件具有 subversion 推荐的 branches tags
  • Apache 访问 Linux 中的 NTFS 链接文件夹

    在 Debian jessie 中使用 Apache2 PHP 当我想在 Apache 的文档文件夹 var www 中创建一个新的小节时 我只需创建一个指向我的 php 文件所在的外部文件夹的链接 然后只需更改该文件夹的所有者和权限文件夹
  • 我什么时候应该编写 Linux 内核模块?

    有些人出于某种原因想要将 Linux 中的代码从用户空间移动到内核空间 很多时候 原因似乎是代码应该具有特别高的优先级 或者只是 内核空间更快 这对我来说似乎很奇怪 我什么时候应该考虑编写内核模块 有一套标准吗 我怎样才能激励将代码保存在
  • 如何检查主机是否在您的known_host ssh中

    我的脚本中使用以下命令 将主机添加到 ssh 中的已知主机 VAR2 expect c spawn ssh o StrictHostKeyChecking no REMOTE HOST USER REMOTE HOST IP expect
  • 为使用 SSH.NET SshClient.CreateShellStream 执行的命令 (sudo/su) 提供子命令

    我正在尝试使用 Renci SSH NET 从 C Web 应用程序连接到远程 Linux 服务器并执行 shell 脚本 我想一个接一个地运行脚本 但不知道如何运行脚本并读取输出并将其存储在标签中 我已经尝试了下面的代码 但无法一行接一行
  • 测试 bash shell 脚本[重复]

    这个问题在这里已经有答案了 有人可以解释一下如何测试 bash shell 脚本吗 例如 我有一个 sh 文件 其中包含此代码 bin sh for file in txt do mv file basename file txt doc

随机推荐

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

    文章目录 数字图像处理 第五章 图像复原与重建 引言 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语句退出举例 二 最佳实践 三 子脚本返回非零状态码时导致主控脚本退出中断的问题 问题描述 重要 问题分析