exit 是Linux/Unix系统中的一个内置Shell命令,用于终止当前Shell会话、退出登录或从脚本中返回退出状态码。它是Shell编程和系统管理中非常重要的工具,用于控制程序流程和传递执行结果。
exit [n]
其中n是一个可选的整数参数,表示退出状态码(Exit Status)。如果不指定n,则使用最后执行的命令的退出状态作为退出状态码。
| 选项/参数 | 说明 |
|---|---|
| 无参数 | 使用最后执行的命令的退出状态码 |
n(整数) |
指定退出状态码(0-255) |
--help(某些Shell) |
显示帮助信息 |
退出状态码是0-255之间的整数,其中0表示成功,非0表示失败。不同的值可以表示不同类型的错误:
| 退出码 | 含义 | 说明 |
|---|---|---|
0 |
成功(Success) | 命令或脚本执行成功 |
1 |
通用错误(General Error) | 未指定的错误 |
2 |
Shell内置命令误用 | Shell命令使用不当 |
126 |
命令不可执行 | 命令权限不足或不可执行 |
127 |
命令未找到 | 命令不存在 |
128 |
无效的退出参数 | 退出状态码超出0-255范围 |
130 |
被Ctrl+C终止 | 脚本被用户中断(SIGINT) |
137 |
被KILL信号终止 | 进程被强制杀死(SIGKILL) |
143 |
被TERM信号终止 | 进程被终止(SIGTERM) |
255 |
退出状态超出范围 | 状态码不在0-255范围内 |
# 打开一个终端,输入exit退出
exit
# 或使用快捷键
# Ctrl+D 也可以退出当前Shell会话
#!/bin/bash
# exit_example.sh - 演示exit命令
echo "脚本开始执行"
# 检查参数数量
if [ $# -lt 1 ]; then
echo "错误: 需要至少一个参数"
exit 1 # 返回错误状态码
fi
echo "参数1: $1"
# 模拟成功执行
echo "任务完成"
exit 0 # 返回成功状态码
# 检查命令是否成功执行
ls /etc/passwd
if [ $? -eq 0 ]; then
echo "文件存在"
else
echo "文件不存在"
fi
# 更简洁的写法
if ls /etc/passwd; then
echo "文件存在"
else
echo "文件不存在"
fi
#!/bin/bash
# get_exit_status.sh - 获取退出状态
# 运行一个脚本
./some_script.sh
exit_status=$?
echo "脚本退出状态: $exit_status"
if [ $exit_status -eq 0 ]; then
echo "脚本执行成功"
elif [ $exit_status -eq 1 ]; then
echo "通用错误"
elif [ $exit_status -eq 2 ]; then
echo "Shell内置命令误用"
elif [ $exit_status -eq 127 ]; then
echo "命令未找到"
else
echo "其他错误 (代码: $exit_status)"
fi
# 通过SSH登录后退出
ssh user@remotehost
# 登录后执行命令...
exit
# 返回本地Shell
# 退出当前用户登录
logout
# 注意:logout只在登录Shell中有效
# 创建子Shell
bash
echo "这是在子Shell中"
exit
echo "这行不会执行,因为已经退出了"
# 使用圆括号创建子Shell
(echo "子Shell开始"; exit 5; echo "这行不会执行")
echo "子Shell退出状态: $?" # 显示5
# 在终端中直接退出
exit
# 也可以使用Ctrl+D快捷键
# 注意:如果设置了IGNOREEOF变量,可能需要多次Ctrl+D
#!/bin/bash
# function_exit.sh - 函数中使用exit
# 函数中的exit会退出整个脚本
error_exit() {
echo "错误: $1"
exit 1
}
# 检查必需的文件
check_required_files() {
local required_files=("/etc/passwd" "/etc/shadow" "/etc/group")
for file in "${required_files[@]}"; do
if [ ! -f "$file" ]; then
error_exit "必需文件 $file 不存在"
fi
done
echo "所有必需文件都存在"
}
# 主程序
echo "开始检查系统文件..."
check_required_files
echo "检查完成" # 如果error_exit被调用,这行不会执行
#!/bin/bash
# conditional_exit.sh - 条件退出
# 检查是否以root运行
check_root() {
if [ "$EUID" -ne 0 ]; then
echo "错误: 此脚本必须以root权限运行"
exit 1
fi
echo "✓ 以root权限运行"
}
# 检查必需的软件包
check_packages() {
local packages=("curl" "wget" "git")
for pkg in "${packages[@]}"; do
if ! command -v "$pkg" &> /dev/null; then
echo "错误: 必需软件包 $pkg 未安装"
exit 2
fi
done
echo "✓ 所有必需软件包已安装"
}
# 主程序
echo "系统检查开始..."
check_root
check_packages
echo "系统检查完成"
exit 0
#!/bin/bash
# graceful_exit.sh - 优雅退出
# 设置trap捕获信号
trap cleanup EXIT
# 清理函数
cleanup() {
local exit_code=$?
echo ""
echo "正在清理..."
# 清理临时文件
if [ -f "/tmp/myapp.tmp" ]; then
rm -f "/tmp/myapp.tmp"
echo "已删除临时文件"
fi
# 根据退出码显示消息
if [ $exit_code -eq 0 ]; then
echo "✓ 程序成功退出"
else
echo "✗ 程序异常退出 (代码: $exit_code)"
fi
exit $exit_code
}
# 模拟工作
echo "开始处理..."
sleep 2
# 模拟错误(随机)
if [ $((RANDOM % 3)) -eq 0 ]; then
echo "发生错误,退出..."
exit 1
fi
echo "处理完成"
exit 0
#!/bin/bash
# advanced_exit_codes.sh - 高级退出码使用
check_service() {
local service_name="$1"
if ! systemctl is-active --quiet "$service_name"; then
echo "服务 $service_name 未运行"
return 1
fi
if ! systemctl is-enabled --quiet "$service_name"; then
echo "服务 $service_name 未启用开机启动"
return 2
fi
echo "服务 $service_name 状态正常"
return 0
}
check_disk_space() {
local usage=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
if [ $usage -gt 90 ]; then
echo "磁盘空间不足: ${usage}% 已使用"
return 3
elif [ $usage -gt 80 ]; then
echo "磁盘空间警告: ${usage}% 已使用"
return 4
fi
echo "磁盘空间正常: ${usage}% 已使用"
return 0
}
# 主程序
main() {
local exit_code=0
echo "系统健康检查..."
echo "=================="
# 检查服务
check_service "sshd"
service_code=$?
[ $service_code -ne 0 ] && exit_code=$service_code
# 检查磁盘空间
check_disk_space
disk_code=$?
[ $disk_code -ne 0 ] && exit_code=$disk_code
# 根据最严重的错误代码退出
exit $exit_code
}
main
#!/bin/bash
# signal_exit.sh - 信号与退出码
# 捕获信号
trap 'echo "收到SIGINT信号"; exit 130' INT
trap 'echo "收到SIGTERM信号"; exit 143' TERM
trap 'echo "收到SIGHUP信号"; exit 129' HUP
echo "脚本PID: $$"
echo "按Ctrl+C发送SIGINT"
echo "在另一个终端执行: kill -TERM $$ 发送SIGTERM"
echo "等待信号..."
# 无限循环等待信号
while true; do
sleep 1
done
# 这行永远不会执行
exit 0
# bash的exit命令
help exit
# 显示: exit: exit [n]
# 退出shell,返回状态码n
# 在bash脚本中,如果没有指定exit,脚本会以最后命令的退出码退出
# 检查当前Shell
echo $SHELL
echo $BASH_VERSION
# zsh的exit命令
echo $ZSH_VERSION
# zsh的行为与bash类似,但有些细微差别
# 在zsh中,如果没有明确设置,函数默认返回最后命令的退出码
# dash是Debian/Ubuntu的默认/bin/sh
# 它的exit行为与bash类似,但更简洁
ls -l /bin/sh
| 命令 | 说明 |
|---|---|
logout |
退出登录Shell(仅对登录Shell有效) |
Ctrl+D |
发送EOF(End of File),通常退出当前Shell |
trap |
捕获信号并执行命令,用于优雅退出 |
return |
从函数返回,退出当前函数 |
break |
退出当前循环 |
continue |
跳过当前循环迭代,继续下一次 |
kill |
发送信号给进程,可用于终止进程 |
pkill |
根据进程名杀死进程 |
exec |
执行命令并替换当前Shell |
source 或 . |
在当前Shell中执行脚本,不创建子Shell |
#!/bin/bash
# deploy_script.sh - 自动化部署脚本
set -e # 任何命令失败时立即退出
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
# 错误处理函数
error_exit() {
echo -e "${RED}错误: $1${NC}" >&2
exit 1
}
# 检查环境
check_environment() {
echo "检查部署环境..."
# 检查是否在git仓库中
if [ ! -d ".git" ]; then
error_exit "当前目录不是git仓库"
fi
# 检查必要的工具
for cmd in git docker docker-compose; do
if ! command -v "$cmd" >/dev/null 2>&1; then
error_exit "必需工具 $cmd 未安装"
fi
done
echo -e "${GREEN}环境检查通过${NC}"
}
# 拉取最新代码
pull_latest_code() {
echo "拉取最新代码..."
if ! git pull; then
error_exit "代码拉取失败"
fi
echo -e "${GREEN}代码更新完成${NC}"
}
# 构建Docker镜像
build_docker_image() {
echo "构建Docker镜像..."
if ! docker-compose build; then
error_exit "Docker镜像构建失败"
fi
echo -e "${GREEN}Docker镜像构建完成${NC}"
}
# 重启服务
restart_services() {
echo "重启服务..."
if ! docker-compose up -d; then
error_exit "服务重启失败"
fi
echo -e "${GREEN}服务重启完成${NC}"
}
# 主程序
main() {
echo "开始自动化部署..."
echo "=================="
check_environment
pull_latest_code
build_docker_image
restart_services
echo -e "${GREEN}部署成功完成${NC}"
exit 0
}
# 运行主程序
main
#!/bin/bash
# system_monitor.sh - 系统监控脚本
# 阈值定义
CPU_THRESHOLD=80
MEM_THRESHOLD=85
DISK_THRESHOLD=90
ERROR_COUNT=0
# 检查CPU使用率
check_cpu() {
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
if (( $(echo "$cpu_usage > $CPU_THRESHOLD" | bc -l) )); then
echo "警告: CPU使用率过高: ${cpu_usage}%"
ERROR_COUNT=$((ERROR_COUNT + 1))
else
echo "CPU使用率正常: ${cpu_usage}%"
fi
}
# 检查内存使用率
check_memory() {
local mem_total=$(free -m | awk '/^Mem:/{print $2}')
local mem_used=$(free -m | awk '/^Mem:/{print $3}')
local mem_usage=$((mem_used * 100 / mem_total))
if [ $mem_usage -gt $MEM_THRESHOLD ]; then
echo "警告: 内存使用率过高: ${mem_usage}%"
ERROR_COUNT=$((ERROR_COUNT + 1))
else
echo "内存使用率正常: ${mem_usage}%"
fi
}
# 检查磁盘使用率
check_disk() {
local disk_usage=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
if [ $disk_usage -gt $DISK_THRESHOLD ]; then
echo "警告: 磁盘使用率过高: ${disk_usage}%"
ERROR_COUNT=$((ERROR_COUNT + 1))
else
echo "磁盘使用率正常: ${disk_usage}%"
fi
}
# 检查关键服务
check_services() {
local services=("sshd" "nginx" "mysql" "docker")
for service in "${services[@]}"; do
if systemctl is-active --quiet "$service"; then
echo "服务 $service 运行正常"
else
echo "错误: 服务 $service 未运行"
ERROR_COUNT=$((ERROR_COUNT + 2)) # 服务错误权重更高
fi
done
}
# 主程序
main() {
echo "系统监控检查 - $(date)"
echo "========================"
check_cpu
check_memory
check_disk
check_services
echo ""
echo "检查完成"
echo "错误计数: $ERROR_COUNT"
# 根据错误计数返回退出码
if [ $ERROR_COUNT -eq 0 ]; then
echo "系统状态: 健康"
exit 0
elif [ $ERROR_COUNT -le 3 ]; then
echo "系统状态: 警告"
exit 1
else
echo "系统状态: 危险"
exit 2
fi
}
main
#!/bin/bash
# task_scheduler.sh - 任务调度脚本
# 日志文件
LOG_FILE="/var/log/task_scheduler.log"
LOCK_FILE="/tmp/task_scheduler.lock"
# 检查是否已在运行
check_running() {
if [ -f "$LOCK_FILE" ]; then
local pid=$(cat "$LOCK_FILE")
if kill -0 "$pid" 2>/dev/null; then
echo "错误: 脚本已在运行 (PID: $pid)"
exit 1
else
# 移除过期的锁文件
rm -f "$LOCK_FILE"
fi
fi
}
# 创建锁文件
create_lock() {
echo $$ > "$LOCK_FILE"
trap 'rm -f "$LOCK_FILE"; exit' EXIT
}
# 日志记录
log_message() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local message="$1"
echo "[$timestamp] $message" >> "$LOG_FILE"
echo "$message"
}
# 任务1: 备份数据库
task_backup_database() {
log_message "开始数据库备份..."
if mysqldump -u root --all-databases > /backup/db_backup_$(date +%Y%m%d).sql; then
log_message "数据库备份成功"
return 0
else
log_message "数据库备份失败"
return 1
fi
}
# 任务2: 清理临时文件
task_cleanup_temp() {
log_message "开始清理临时文件..."
if find /tmp -type f -name "*.tmp" -mtime +7 -delete; then
log_message "临时文件清理成功"
return 0
else
log_message "临时文件清理失败"
return 1
fi
}
# 任务3: 同步日志
task_sync_logs() {
log_message "开始同步日志..."
if rsync -avz /var/log/ backup-server:/backup/logs/; then
log_message "日志同步成功"
return 0
else
log_message "日志同步失败"
return 1
fi
}
# 主程序
main() {
check_running
create_lock
log_message "任务调度开始"
local exit_code=0
local failed_tasks=0
# 执行任务
tasks=("task_backup_database" "task_cleanup_temp" "task_sync_logs")
for task in "${tasks[@]}"; do
if $task; then
log_message "✓ $task 成功"
else
log_message "✗ $task 失败"
failed_tasks=$((failed_tasks + 1))
fi
done
# 汇总结果
if [ $failed_tasks -eq 0 ]; then
log_message "所有任务执行成功"
exit_code=0
else
log_message "警告: $failed_tasks 个任务失败"
exit_code=$failed_tasks
fi
log_message "任务调度结束"
exit $exit_code
}
# 运行主程序
main
# 在某些情况下,exit可能不会退出整个脚本
# 比如在管道中或子Shell中
# 错误的例子
# 在管道中,exit只退出子Shell
echo "test" | while read line; do
echo $line
exit 1
done
echo "这行仍然会执行"
# 解决方案:使用进程替换或重定向
while read line; do
echo $line
exit 1
done < <(echo "test")
echo "这行不会执行"
# 函数中的exit会退出整个脚本,而不是只退出函数
my_function() {
echo "函数开始"
exit 1
echo "这行不会执行"
}
my_function
echo "这行不会执行,因为脚本已退出"
# 解决方案:在函数中使用return
my_safe_function() {
echo "函数开始"
return 1
echo "这行不会执行"
}
my_safe_function
echo "这行会执行,退出码: $?"
# 最后的命令会覆盖之前的退出码
false
echo "上一个命令的退出码: $?" # 显示1
true
echo "现在的退出码: $?" # 显示0,被覆盖了
# 解决方案:保存退出码
false
exit_code=$?
echo "退出码: $exit_code" # 显示1
true
echo "新的退出码: $?" # 显示0
echo "保存的退出码: $exit_code" # 仍然显示1
# 多个trap可能会冲突
trap 'echo "捕获信号"; exit 130' INT
trap 'echo "另一个处理程序"; exit 131' INT # 这会覆盖前一个
# 解决方案:合并处理程序
trap 'handle_signal' INT
handle_signal() {
echo "捕获信号"
# 执行清理操作
exit 130
}
set -e使脚本在命令失败时立即退出trap在退出前清理临时文件等资源return而不是exit| 用途 | 命令示例 |
|---|---|
| 退出Shell会话 | exit |
| 退出并返回成功状态码 | exit 0 |
| 退出并返回通用错误 | exit 1 |
| 退出并返回自定义状态码 | exit 5 |
| 检查上一个命令的退出码 | echo $? |
| 在脚本开头启用错误时立即退出 | set -e |
| 捕获信号并优雅退出 | trap 'exit 130' INT |
| 退出前执行清理 | trap cleanup EXIT |
| 退出登录Shell | logout |
| 快捷键退出当前Shell | Ctrl+D |
| 命令 | 作用范围 | 使用场景 | 返回值 |
|---|---|---|---|
exit |
退出当前Shell/脚本 | 脚本结束、Shell会话结束 | 指定退出码 |
return |
退出当前函数 | 函数返回 | 指定返回码 |
break |
退出当前循环 | 循环控制 | 无 |
logout |
退出登录Shell | 用户登出 | 无 |
Ctrl+D |
退出当前Shell | 终端会话 | EOF信号 |
kill |
终止指定进程 | 进程管理 | 信号退出码 |
exit命令起源于早期的Unix系统,是Shell编程的基础命令之一:
虽然exit是一个简单的命令,但它在Shell脚本编程、进程管理和系统监控中起着至关重要的作用。正确使用exit命令可以使脚本更加健壮、可靠和易于调试。