nohup 是 Linux 中用于使进程忽略挂起(HUP)信号并在后台继续运行的命令。它通常用于在用户注销后仍需要继续运行的长时间任务。
📦 基本语法
bash
nohup 命令 [参数]...
nohup 命令 [参数]... > 输出文件
🎯 主要特性
- 忽略挂起信号:当终端关闭时,进程不会收到 SIGHUP 信号而终止
- 输出重定向 :默认将输出重定向到
nohup.out文件 - 后台运行 :常与
&结合使用,让进程在后台运行 - 脱离终端:进程与当前终端分离,不受终端关闭影响
💡 核心用法示例
1. 基本用法
bash
# 启动进程并忽略挂起信号
nohup 命令
# 启动进程并在后台运行
nohup 命令 &
# 指定输出文件
nohup 命令 > output.log
# 后台运行并指定输出文件
nohup 命令 > output.log 2>&1 &
# 将标准输出和错误输出都重定向
nohup 命令 > output.log 2>&1
2. 常见使用场景
bash
# 1. 运行长时间脚本
nohup ./long_running_script.sh &
# 2. 启动服务器应用
nohup java -jar myapp.jar > app.log 2>&1 &
# 3. 数据库备份
nohup mysqldump -u root -p database > backup.sql &
# 4. 大文件处理
nohup gzip -c largefile.dat > largefile.dat.gz &
# 5. 编译项目
nohup make -j4 > compile.log 2>&1 &
# 6. 下载大文件
nohup wget -c http://example.com/large.iso &
# 7. 科学计算
nohup python compute.py > result.log 2>&1 &
3. 输出重定向控制
bash
# 只重定向标准输出
nohup command > output.log
# 重定向标准输出和错误输出到不同文件
nohup command > output.log 2> error.log
# 重定向标准输出和错误输出到同一文件
nohup command > output.log 2>&1
# 丢弃所有输出
nohup command > /dev/null 2>&1
# 只保留错误输出
nohup command > /dev/null 2> error.log
🔧 实用技巧
1. 查看和管理 nohup 进程
bash
# 查找所有 nohup 启动的进程
ps aux | grep nohup
ps aux | grep -v grep | grep nohup
# 查找特定用户的 nohup 进程
ps -u username -o pid,cmd | grep nohup
# 查看进程树
pstree -p $(pgrep -f "nohup.*command")
# 查看 nohup 输出文件
tail -f nohup.out
tail -f output.log
2. 停止 nohup 进程
bash
# 找到进程ID
ps aux | grep "command_name"
# 发送终止信号
kill PID
kill -9 PID # 强制终止
# 停止所有相关进程
pkill -f "command_name"
kill $(pgrep -f "command_name")
3. 结合其他命令使用
bash
# 使用 time 计时
nohup time ./script.sh > output.log 2>&1 &
# 结合 nice 设置优先级
nohup nice -n 10 ./script.sh > output.log 2>&1 &
# 结合 setsid 完全脱离终端
nohup setsid ./daemon.sh > /dev/null 2>&1 &
# 在 screen/tmux 中使用
# 先在 screen/tmux 中启动
nohup ./script.sh > output.log 2>&1
# 然后 detach,可以随时 reattach
📊 输出文件管理
1. 自动管理输出文件
bash
#!/bin/bash
# 自动管理 nohup 输出文件
LOG_DIR="/var/log/nohup_logs"
LOG_FILE="${LOG_DIR}/task_$(date +%Y%m%d_%H%M%S).log"
# 创建日志目录
mkdir -p "$LOG_DIR"
# 启动任务
nohup ./long_task.sh > "$LOG_FILE" 2>&1 &
echo "任务已启动,日志文件: $LOG_FILE"
echo "PID: $!"
2. 日志轮转脚本
bash
#!/bin/bash
# nohup 日志轮转脚本
LOG_DIR="/var/log/nohup_logs"
RETENTION_DAYS=7
MAX_LOG_SIZE=100M # 100MB
# 清理旧日志
find "$LOG_DIR" -name "*.log" -mtime +$RETENTION_DAYS -delete
# 检查并分割大日志文件
for logfile in "$LOG_DIR"/*.log; do
if [[ -f "$logfile" ]]; then
size=$(stat -c%s "$logfile" 2>/dev/null || stat -f%z "$logfile" 2>/dev/null)
if [[ $size -gt 104857600 ]]; then # 100MB in bytes
mv "$logfile" "${logfile}.$(date +%Y%m%d_%H%M%S)"
touch "$logfile"
fi
fi
done
⚡ 高级用法
1. 监控和重启脚本
bash
#!/bin/bash
# 监控和自动重启脚本
APP_NAME="myapp"
APP_CMD="java -jar myapp.jar"
LOG_FILE="/var/log/${APP_NAME}.log"
PID_FILE="/var/run/${APP_NAME}.pid"
CHECK_INTERVAL=60
# 启动应用
start_app() {
echo "启动 $APP_NAME..."
nohup $APP_CMD > "$LOG_FILE" 2>&1 &
echo $! > "$PID_FILE"
echo "PID: $(cat $PID_FILE)"
}
# 停止应用
stop_app() {
if [[ -f "$PID_FILE" ]]; then
PID=$(cat "$PID_FILE")
if kill -0 $PID 2>/dev/null; then
echo "停止 $APP_NAME (PID: $PID)..."
kill $PID
sleep 5
if kill -0 $PID 2>/dev/null; then
echo "强制停止..."
kill -9 $PID
fi
fi
rm -f "$PID_FILE"
fi
}
# 检查应用状态
check_app() {
if [[ -f "$PID_FILE" ]]; then
PID=$(cat "$PID_FILE")
if ! kill -0 $PID 2>/dev/null; then
echo "应用已停止,重新启动..."
start_app
fi
else
echo "PID文件不存在,启动应用..."
start_app
fi
}
# 主循环
trap 'stop_app; exit 0' INT TERM
echo "开始监控 $APP_NAME..."
while true; do
check_app
sleep "$CHECK_INTERVAL"
done
2. 批量启动任务
bash
#!/bin/bash
# 批量启动 nohup 任务
declare -A TASKS=(
["task1"]="./task1.sh"
["task2"]="./task2.sh"
["task3"]="python3 task3.py"
["task4"]="node task4.js"
)
LOG_DIR="logs"
mkdir -p "$LOG_DIR"
echo "开始批量启动任务..."
echo "======================"
for task_name in "${!TASKS[@]}"; do
cmd="${TASKS[$task_name]}"
log_file="$LOG_DIR/${task_name}_$(date +%Y%m%d_%H%M%S).log"
echo "启动: $task_name"
echo "命令: $cmd"
echo "日志: $log_file"
nohup $cmd > "$log_file" 2>&1 &
pid=$!
echo "PID: $pid"
echo "$pid" > "$LOG_DIR/${task_name}.pid"
echo "---"
done
echo "所有任务已启动"
echo "查看进程: ps aux | grep -E '$(echo ${TASKS[@]} | tr ' ' '|')'"
🔍 常见问题排查
1. 检查 nohup 是否正常工作
bash
# 测试脚本
cat > test_nohup.sh << 'EOF'
#!/bin/bash
for i in {1..10}; do
echo "Count: $i at $(date)"
sleep 1
done
EOF
chmod +x test_nohup.sh
# 启动测试
nohup ./test_nohup.sh > test.log 2>&1 &
echo "PID: $!"
# 立即退出终端,重新登录后检查
cat test.log
2. 查看进程状态
bash
# 查看进程是否仍在运行
ps -p $PID -o pid,cmd,state
# 查看进程打开的文件
lsof -p $PID
# 查看进程资源使用
top -p $PID
⚠️ 重要注意事项
- 输出重定向:如果不重定向输出,可能会占用磁盘空间
- 权限问题:某些操作可能需要特定权限
- 信号处理:nohup 只忽略 SIGHUP,其他信号仍需处理
- 资源限制:长期运行的进程可能消耗系统资源
- 日志管理:定期清理 nohup.out 文件
- 环境变量:nohup 会继承当前 shell 的环境变量
🔄 替代方案
| 工具 | 说明 | 适用场景 |
|---|---|---|
screen |
终端复用器,会话可分离 | 需要交互的长时间任务 |
tmux |
现代终端复用器 | 复杂的多任务管理 |
disown |
从 shell 作业表中移除作业 | 已启动的作业需要后台运行 |
setsid |
在新会话中运行程序 | 完全脱离终端的守护进程 |
systemd |
系统和服务管理器 | 系统服务管理 |
🎯 最佳实践
- 始终重定向输出:避免 nohup.out 文件无限增长
- 使用日志轮转:定期清理和分割日志文件
- 记录进程ID:方便后续管理
- 设置资源限制:使用 ulimit 避免资源耗尽
- 监控进程状态:定期检查进程是否正常运行
- 优雅停止:为进程实现信号处理,支持优雅关闭
nohup 是 Linux 系统管理中非常实用的工具,特别适合运行不需要用户交互的长时间任务。结合适当的重定向和监控,可以确保任务稳定可靠地运行。