文章目录
- shell脚本之如使用return和exit
- 一、exit和return基础
- EXIT退出指令举例
- 在函数中使用return语句退出举例
- 二、最佳实践
- 三、子脚本返回非零状态码时导致主控脚本退出中断的问题
- 问题描述
- [重要]问题分析
- 善用exit $?
- 使用 sh 调子脚本
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" ]; thenecho "The file exists"exit 0 # 成功的退出
elseecho "The file does not exist"exit 1 # 异常的退出并附带说明
fi
在这个例子中,脚本使用“-f”测试运算符检查一个名为“myfile.txt”的文件是否存在。如果文件存在,脚本会向控制台打印一条消息,并使用“exit”命令以成功代码0退出。如果文件不存在,脚本会打印不同的消息,并使用错误代码1退出。
“exit”命令还可以用于处理脚本执行过程中的错误或意外情况。例如,假设一个脚本需要访问可能不可用的资源,如网络服务或数据库。在这种情况下,脚本可以使用“exit”命令以错误消息和适当的退出代码优雅地终止。
在函数中使用return语句退出举例
#!/bin/bash# 定义一个函数并返回数字之和
function add_numbers {local num1=$1local num2=$2local 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=$1if [ ! -f "$file" ]; thenecho "Error: File $file not found"return 1ficat $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_CONFif [ $? -eq 0 ]; thenecho "MongoDB started successfully"return 0elseecho "Failed to start MongoDB"return 1fi
}stop_mongodb(){$MONGODB_BIN_DIR/mongod -f $MONGO_CONF --shutdownif [ $? -eq 0 ]; then echo "MongoDB stopped successfully"return 0elseecho "Failed to stop MongoDB"return 1 fi
}status(){if [ -f $MONGODB_PIDFILE ]; thenecho "MongoDB is running, PID: $(cat $MONGODB_PIDFILE)"return 0elseecho "MongoDB is stopped"return 1fi
}function control_mongodb(){case $1 instart)start_mongodb ;;stop)stop_mongodb;;restart)stop_mongodbstart_mongodb;;status)status;;*)echo "Usage: $0 {start|stop|restart|status}"exit 1esacreturn $?
}# 调用函数
control_mongodb $1exit $?
在Linux shell脚本中,exit $? 表示使用上一个命令的退出状态码来退出当前shell脚本。
$? 是一个特殊变量,它保存了上一个执行的命令或者函数的退出状态码。
退出状态码0表示成功执行,非0通常表示失败或错误。
exit $? 的具体作用是:
- $? 获取上一个命令的退出码
- exit 使脚本退出
- 将上一个命令的退出码作为脚本的最终退出码
使用 sh 调子脚本
使用 sh 调子脚本,通常有下面的几种使用场景:
- 强制子脚本在一个干净的环境中运行。
sh
会启动一个新的shell实例,不会继承当前shell的任何自定义设置、变量等,可以提供一个干净隔离的运行环境。 - 为子脚本设置特定的shell。直接调用子脚本时,使用当前shell(通常是bash),但有时需要指定为sh、csh等其他shell。
- 在脚本中改变目录时,调用子脚本使用相对路径。
sh
重置了工作目录,使相对路径生效。 - 当子脚本需要另一个版本的shell时。直接调用继承当前shell,但 sh 可以指定所需的shell。
- 在升级系统shell时,保证子脚本向后兼容。直接调用使用新shell可能出错,但 sh 调用可保持原有的shell。
- 出于安全考虑,不信任子脚本,使用 sh 加沙箱隔离。
- 在守护进程中调用脚本,需要一个干净可预测的新shell环境。
- 一些老的脚本依赖 sh 调用,直接转换会破坏原有行为。
总之,使用 sh 调用主要是为了精细控制子脚本的执行环境,符合特定的兼容性要求等。但通常需要时,直接调用更简单。