修复“正确操作nohup让程序始终在后台运行”中已发现的问题

本文详细解析了nohup命令的工作原理及其在Linux环境下如何正确使用的方法。强调了nohup命令用于后台运行程序的重要性,并指出在终端关闭后如何避免进程随之终止的问题。同时介绍了登录shell的概念及其在保持进程运行方面的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题:“抚琴煮酒”(余洪春)在《构建高可用Linux服务器(第二版)》的第119页中写到“产生此问题的原因是:虽然Shell中提示了nohup成功,但还是需要按键盘上的任意键退回到Shell输入命令窗口,然后通过在Shell中输入exit来退出终端,而不是每次在nohup执行成功后直接关闭终端。”

解决办法:使用登录shell防止终端关闭而导致进行自动退出的情况。我常用的命令行为:su -l -c "nohup /some/command.sh >/dev/null 2>&1 &" 。

PS:Red Hat/CentOS启动时在后台运行用户命令,可在/etc/rc.local文件中添加如下行:su -l -c "nohup /some/command.sh >/dev/null 2>&1 &"。

解释:

(1)su -lc表示以登录shell(-l)执行(-c)命令;

(2)经过脚本测试nohup 后面跟着的>/dev/null 2>&1只对nohup本身有作用,即很难使用nohup向/some/command.sh 传递参数,这一点还没有找到相关的资料。

什么是登录shell:(1)用户登陆时,输入用户名和密码后启动的shell;(2)通过带--login参数的命令:bash --login而启动的shell,对于用户来说,登陆shell和非登陆shell的主要区别是:启动shell时所执行的startup文件不同。非登陆shell执行的startup文件仅为:~/.bashrc,而登陆shell执行startup文件为:/etc/profile、~/.bash_profile、~/.bashrc。

什么是非登录shell:除了以上两种情况生成的用户对话,如直接运行bash命令,su某一个用户等。

什么是交互shell:一般的用户与Linux系统用命令行进行操作时使用的shell都为交互式shell。

什么是非交互shell:但执行脚本时,shell就工作在非交互式模式下,因为shell从第一条命令执行到最后一条然后退出,不与用户进行任何交互。

以上四个定义可以参阅以下链接:

linux下的bash与sh 详解以及例子

登录shell,交互式非登录shell,非交互式shell

什么是交互式登录 Shell


书中原文:

“我的Nginx负载均衡器监控Nginx进程的脚本nginx_pid需要放入后台不间断地运行,所以想用命令/bin/sh/data/nginx_pidsh &来达到此目的,在输入完命令后我就关闭了终端,可再次登录终端时发现此程序并没有运行。忽然想起可能是因为没有使用nohup命令,试了试,果然如此,带上nohup命令后就正常了。  
我们的很多程序只是普通的程序,即使它们使用&结尾,如果终端关闭,那么程序也会被关闭。为了能够在后台运行,我们需要使用nohup这个命令,原程序的标准输出被自动改到当前目录下的nohupout文件里,起到了log的作用。    
但是有时候这样做会有问题:如果把终端关闭,进程也会被自动关闭。查看nohupout可以看到在关闭终端的瞬间服务自动关闭了。    
产生此问题的原因是:虽然Shell中提示了nohup成功,但还是需要按键盘上的任意键退回到Shell输入命令窗口,然后通过在Shell中输入exit来退出终端,而不是每次在nohup执行成功后直接关闭终端。    
这个错误许多朋友(包括我)容易忽视,希望大家在工作中注意。”

扩充:

在nohup的手册中有一段话需要认真体会“If standard  input is a terminal, redirect it from /dev/null.  If standard output is a terminal, append output to ‘nohup.out’ if possible, ‘$HOME/nohup.out’ otherwise.  If standard error is a terminal, redirect it to standard output.  To save output to FILE, use ‘nohup COMMAND > FILE’.”。

什么是标准输入?什么是标准输出?什么是标准错误输出?这些问题都比较回答。可以参阅以下链接:

linux shell数据重定向(输入重定向与输出重定向)详细分析

Linux shell的标准输入、输出和错误

但“哪些操作或设备被认为是标准输入,哪些操作或设备被认为是标准输出”这两个问题不怎么好回答。从上面那段话可以看出终端既是一种标准输入设备又是一种标准输出设备(文字中提到的“终端”我认为只是用户的字符界面样式的屏幕而已。)。根据我的理解标准输入只能来自用户的输入(input)操作,可以认为所有的输入设备都是标准的输入,标准输出对应所有的输出设备如屏幕打印机等,重定向只是将不同的标准输入输出从这些寻常的途径变为用户指定了的途径罢了(改变了标准输入输出)。

<think>我们正在处理用户的问题程序或系统卡在read步骤无法继续执行的可能原因及解决方案。根据用户描述,问题发生在执行某个操作时,程序或系统在read步骤卡住,无法继续执行。首先,我们需要理解read步骤:在Shell脚本中,read命令用于从标准输入或文件中读取输入,并赋值给变量。如果read命令在等待输入时没有接收到数据,那么程序就会一直阻塞在那里,表现为“卡住”。可能的原因:1.输入源没有提供数据:例如,脚本中使用了read命令等待用户输入,但用户没有输入(可能是非交互式环境,如cron任务或后台运行)。2.输入重定向问题:脚本可能被设计为从文件或管道读取数据,但数据源没有正确提供数据(如文件不存在、管道未连接等)。3.文件描述符问题:read命令可能从特定的文件描述符读取(如使用`read-u`),而该文件描述符未正确打开或已关闭。4.定界符问题:使用`-d`选项指定了定界符,但输入中一直没有出现该定界符,导致read一直等待。5.超时设置:如果read没有设置超时(`-t`选项),那么它会无限期等待输入。6.信号中断:在等待输入时,如果程序接收到某些信号,可能会被挂起(如SIGTSTP),但这种情况较少见。解决方案:1.检查脚本运行环境:确认脚本是在交互式终端运行还是非交互式环境。如果是非交互式环境(如cron),则需要修改脚本,避免交互式输入(如改为从文件读取或使用默认值)。2.确保输入源可用:如果脚本设计为从文件读取,检查文件路径是否正确、文件是否存在、是否有读取权限。如果是管道,检查前一个命令是否成功执行并输出数据。3.使用超时选项:在read命令中使用`-t`选项设置超时时间,避免无限等待。例如:`read-t5-p"Enter:"var`,表示5秒后自动超时。4.检查定界符:如果使用了`-d`选项,确保输入中包含该定界符,或者调整脚本逻辑,避免无限等待。5.避免在非交互式环境使用交互式read:如果脚本需要在非交互式环境运行,可以考虑以下方法:-使用默认值:`read-p"Enter:"var||var="default"`-重定向输入:使用`<`从文件读取,或者使用管道提供输入。6.调试脚本:在脚本中添调试信息(如使用set-x),查看read命令的执行情况。另外,根据用户提供的引用[3]:read命令的`-d`选项可能会引起等待直到定界符出现的问题。因此,如果脚本中使用了`-d`选项,需要检查输入是否包含指定的定界符。举例说明:假设脚本中有如下命令:read-d';'input那么read会一直读取,直到遇到分号';'。如果输入中没有分号,则read会一直等待。解决方案是确保输入中包含分号,或者修改定界符为一个更可能出现的字符(如换行符),或者使用超时。总结步骤:1.确定read命令的具体用法(是否有选项,如-d,-t,-u等)。2.检查输入源是否正常。3.考虑运行环境(交互式/非交互式)。4.添超时或默认值处理。5.调试并测试。根据上述分析,我们可以给出以下回答:</think>### 程序卡在 `read` 步骤的原因及解决方案 当程序Shell 脚本在 `read` 命令处卡住时,通常是由于输入源未正确提供数据导致的阻塞等待。以下是常见原因及解决方法: --- #### **一、常见原因** 1. **输入源未就绪** - 脚本设计为从**标准输入(stdin)** 读取数据,但未提供输入源(如未重定向文件、管道断开或用户未输入)。 - 示例:`read var` 在非交互式环境(如 cron 任务)中会无限等待输入[^1]。 2. **管道/重定向问题** - 上游命令未输出数据或管道断裂: ```bash command1 | command2 # 若 command1 无输出,command2 的 read 会阻塞 ``` 3. **定界符未匹配(`-d` 参数)** - 使用 `read -d` 指定结束符,但输入中未出现该字符: ```bash read -d ';' input # 未输入分号则永久等待[^3] ``` 4. **文件描述符冲突** - 脚本中重定向了标准输入(如 `exec < file`),后续 `read` 尝试从错误描述符读取。 5. **终端交互环境异常** - 在后台运行的脚本(如 `script.sh &`)尝试从终端读取,但终端已被挂起(SIGTTIN 信号)。 --- #### **二、解决方案** ##### 1. **确保输入源可用** - **交互式脚本**:明确提示用户输入(使用 `-p`): ```bash read -p "Enter data: " data # 显示提示避免用户困惑[^2] ``` - **非交互式脚本**:改用文件或变量输入: ```bash echo "default" | read var # 通过管道提供输入 read var < input.txt # 从文件读取 ``` ##### 2. **设置超时(`-t` 参数)** - 添超时避免无限等待: ```bash if read -t 5 -p "Enter in 5s: " var; then echo "Got: $var" else echo "Timeout!" # 5秒后自动继续 fi ``` ##### 3. **检查定界符逻辑** - 确保 `-d` 指定的字符会在输入中出现,或改用默认换行符: ```bash read -d ',' var # 仅在确定输入含逗号时使用 ``` ##### 4. **修复管道/重定向** - 验证上游命令是否正常输出: ```bash command1 | tee debug.log | command2 # 记录管道数据调试 ``` - 避免重定向冲突:恢复标准输入后再用 `read`: ```bash exec 3<&0 # 备份 stdin 到描述符 3 exec < file # 重定向 # ...处理文件... exec 0<&3 # 恢复 stdin read -p "Now safe to read" var ``` ##### 5. **处理后台任务** - 禁止后台任务读取终端: ```bash nohup script.sh > /dev/null & # 脱离终端运行 ``` --- #### **三、调试技巧** - **打印文件描述符状态**: ```bash lsof -p $$ # 查看当前进程打开的文件描述符 ``` - **跟踪脚本执行**: ```bash bash -x script.sh # 逐步调试 ``` - **检查信号阻塞**: ```bash trap -l # 列出信号,确保未屏蔽 SIGTTIN(终端输入) ``` --- > **关键总结**:`read` 阻塞本质是**输入饥饿**。通过预置输入源、设置超时、避免非交互环境阻塞,可彻底解决问题。若涉及复杂重定向,需严格管理文件描述符[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值