&(后台运行)和2>&1(重定向错误输出)一起使用。
nohup命令(No Hang Up的缩写)用于运行不受挂起信号影响的命令。当用户注销(logout)或者网络连接断开时,终端会收到SIGHUP信号,所有从该终端启动的进程都会被终止。使用nohup可以防止这种情况发生,使命令继续在后台运行。
主要应用场景:
nohup 命令 [参数]
nohup 命令 [参数] > 输出文件 2>&1 &
nohup命令的工作原理:
nohup.out文件(如果未指定输出文件)# 最简单的nohup用法
nohup python script.py &
# 查看nohup输出
cat nohup.out
# 查看nohup进程
ps aux | grep script.py
# 将输出重定向到指定文件
nohup java -jar app.jar > app.log &
# 追加输出到现有文件
nohup ./start_server.sh >> server.log 2>&1 &
# 将标准输出和错误输出分别重定向
nohup ./program > output.log 2> error.log &
# 标准输出和错误输出都重定向到同一个文件
nohup ./myprogram > /dev/null 2>&1 &
# 丢弃所有输出(静默运行)
nohup ./background_task.sh > /dev/null 2>&1 &
# 只重定向错误输出,标准输出到终端
nohup ./script.sh 2> error.log &
# 使用nohup运行多个命令
nohup bash -c "command1 && command2" > output.log 2>&1 &
# 在nohup中使用管道
nohup cat access.log | grep "ERROR" > errors.log 2>&1 &
# 后台运行复杂的shell脚本
nohup sh -c '
for i in {1..10}
do
echo "Processing item $i"
./process_item.sh $i
sleep 1
done
' > process.log 2>&1 &
# 后台启动Web服务器
nohup python -m http.server 8080 > webserver.log 2>&1 &
# 数据库备份任务
nohup mysqldump -u root -p database > backup.sql 2> backup_error.log &
# 长时间运行的压缩任务
nohup tar -czf archive.tar.gz /path/to/large/directory > compression.log 2>&1 &
# 监控日志文件
nohup tail -f /var/log/syslog | grep "ERROR" > errors_monitor.log 2>&1 &
# 查找所有nohup进程
ps aux | grep nohup
ps -ef | grep nohup
# 查找特定nohup进程
pgrep -f "python script.py"
ps aux | grep "java.*jar"
# 查看进程树
pstree -p | grep -A 5 -B 5 nohup
# 查看进程资源使用情况
top -p $(pgrep -f "python script.py")
# 查看进程打开的文件
lsof -p $(pgrep -f "java.*jar")
# 找到进程ID并杀死
PID=$(pgrep -f "python script.py")
kill $PID
# 强制杀死进程
kill -9 $PID
# 杀死所有匹配的进程
pkill -f "java.*jar"
# 优雅停止(发送SIGTERM信号)
killall -TERM process_name
| 输出文件 | 说明 | 默认位置 |
|---|---|---|
nohup.out |
默认输出文件(当未指定重定向时) | 当前目录 |
~/.nohup.out |
如果当前目录不可写时的备用位置 | 用户家目录 |
自定义文件.log |
用户指定的输出文件 | 指定路径 |
#!/bin/bash
# run_backup.sh - 使用nohup运行备份脚本
# 定义日志文件
LOG_FILE="/var/log/backup_$(date +%Y%m%d_%H%M%S).log"
echo "Starting backup at $(date)" | tee -a "$LOG_FILE"
# 使用nohup运行备份
nohup /usr/local/bin/backup.sh >> "$LOG_FILE" 2>&1 &
# 获取进程ID
BACKUP_PID=$!
echo "Backup started with PID: $BACKUP_PID" | tee -a "$LOG_FILE"
echo "Log file: $LOG_FILE" | tee -a "$LOG_FILE"
# 保存PID到文件以便后续管理
echo "$BACKUP_PID" > /var/run/backup.pid
#!/bin/bash
# monitor_nohup.sh - 监控nohup进程状态
PID_FILE="/var/run/myapp.pid"
if [ ! -f "$PID_FILE" ]; then
echo "PID file not found: $PID_FILE"
exit 1
fi
PID=$(cat "$PID_FILE")
# 检查进程是否存在
if ps -p "$PID" > /dev/null 2>&1; then
echo "Process $PID is running"
# 查看进程状态
ps -p "$PID" -o pid,ppid,user,%cpu,%mem,cmd
# 查看进程打开的文件
echo "=== Open files ==="
lsof -p "$PID" | head -10
# 查看进程资源使用
echo "=== Resource usage ==="
top -b -n 1 -p "$PID" | tail -2
else
echo "Process $PID is not running"
# 检查是否有僵尸进程
if ps -p "$PID" -o state | grep -q Z; then
echo "Warning: Process $PID is a zombie!"
fi
fi
#!/bin/bash
# start_workers.sh - 批量启动worker进程
WORKER_COUNT=5
LOG_DIR="/var/log/workers"
mkdir -p "$LOG_DIR"
echo "Starting $WORKER_COUNT workers..."
for i in $(seq 1 $WORKER_COUNT); do
LOG_FILE="$LOG_DIR/worker_$i.log"
echo "Starting worker $i (log: $LOG_FILE)"
nohup /usr/local/bin/worker.py --id "$i" > "$LOG_FILE" 2>&1 &
WORKER_PID=$!
echo "Worker $i started with PID: $WORKER_PID"
# 保存PID到数组
PIDS[$i]=$WORKER_PID
done
echo "All workers started. PIDs: ${PIDS[*]}"
#!/bin/bash
# auto_restart.sh - 自动重启失败的nohup进程
APP_CMD="/usr/local/bin/myapp.py"
LOG_FILE="/var/log/myapp.log"
PID_FILE="/var/run/myapp.pid"
MAX_RETRIES=3
RETRY_COUNT=0
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
echo "Starting application (attempt $((RETRY_COUNT+1))) at $(date)"
# 使用nohup启动应用
nohup $APP_CMD >> "$LOG_FILE" 2>&1 &
APP_PID=$!
echo "$APP_PID" > "$PID_FILE"
echo "Application started with PID: $APP_PID"
# 等待进程结束
wait $APP_PID
EXIT_CODE=$?
echo "Application exited with code: $EXIT_CODE at $(date)"
# 检查是否需要重启
if [ $EXIT_CODE -eq 0 ] || [ $EXIT_CODE -eq 130 ]; then
echo "Normal exit, not restarting"
break
else
RETRY_COUNT=$((RETRY_COUNT+1))
echo "Abnormal exit, restarting in 5 seconds... (retry $RETRY_COUNT/$MAX_RETRIES)"
sleep 5
fi
done
if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
echo "Max retries reached. Giving up."
exit 1
fi
| 工具 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| nohup | 简单易用,无需额外安装,系统自带 | 无法重新连接查看输出,管理不便 | 简单的后台任务,无需交互的任务 |
| screen | 可以重新连接会话,多个窗口,会话共享 | 配置较复杂,需要额外学习 | 长时间运行需要交互的任务 |
| tmux | 功能强大,窗口分割,会话共享,高度可配置 | 学习曲线较陡,配置复杂 | 复杂的多任务管理,开发环境 |
| systemd | 系统级管理,自动重启,日志管理,资源控制 | 配置复杂,需要root权限 | 生产环境服务,需要系统级管理的应用 |
# nohup方式(简单,适合临时任务)
nohup /opt/myapp/bin/start.sh > /var/log/myapp.log 2>&1 &
# systemd方式(生产环境推荐)
sudo systemctl start myapp.service
sudo systemctl enable myapp.service # 开机自启
# systemd服务文件示例 /etc/systemd/system/myapp.service
[Unit]
Description=My Application
After=network.target
[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/start.sh
Restart=always
RestartSec=10
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=myapp
[Install]
WantedBy=multi-user.target
nohup.out,如果当前目录不可写,会创建在~/nohup.out&(后台运行):只是将命令放到后台执行,终端关闭时进程仍然会收到SIGHUP信号而终止。
nohup:忽略SIGHUP信号,即使终端关闭,进程也会继续运行。
通常两者结合使用:nohup command &
# 仅后台运行,终端关闭会终止
./script.sh &
# nohup后台运行,终端关闭继续运行
nohup ./script.sh &
解决方法:
# 1. 使用自定义日志文件并定期清理
nohup ./program > /var/log/program.log 2>&1 &
# 2. 使用logrotate管理日志
# 创建logrotate配置 /etc/logrotate.d/myprogram
/var/log/program.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 644 root root
}
# 3. 丢弃不需要的输出
nohup ./program > /dev/null 2>&1 &
# 4. 只保留错误日志
nohup ./program > /dev/null 2> error.log &
根据不同的重定向方式:
# 1. 查看默认输出文件
tail -f nohup.out
cat nohup.out
# 2. 查看自定义日志文件
tail -f /var/log/program.log
# 3. 实时查看输出(如果输出到文件)
tail -f /var/log/program.log
# 4. 查看最后N行
tail -100 /var/log/program.log
# 5. 搜索特定内容
grep "ERROR" /var/log/program.log
注意:如果输出被重定向到/dev/null,则无法查看输出内容。
nohup本身不支持开机自启,需要其他方法:
方法1:使用rc.local(简单但不推荐)
# 编辑/etc/rc.local
#!/bin/bash
nohup /path/to/your/command > /var/log/yourcommand.log 2>&1 &
exit 0
方法2:使用crontab(推荐)
# 编辑crontab -e
@reboot nohup /path/to/your/command > /var/log/yourcommand.log 2>&1 &
方法3:使用systemd服务(生产环境推荐)
# 创建systemd服务文件
sudo nano /etc/systemd/system/yourcommand.service
# 启用并启动服务
sudo systemctl enable yourcommand.service
sudo systemctl start yourcommand.service
方法4:使用init.d脚本(传统方法)
# 创建/etc/init.d/yourcommand脚本
sudo update-rc.d yourcommand defaults