我试图让脚本连续运行一个循环,在后台更新变量,以便该变量在每次使用时都是最新的。经过近一个小时的寻找,我什么也没得到,这很令人惊讶,因为似乎有人会在我之前想做这件事。
我的脚本应该设置$SESSIONLENGTH
为会话长度的格式化字符串......
#!/bin/bash
while true
do
SECONDSINSESSION=$SECONDS
SECONDSINMINUTE=60
SECONDSINHOUR=3600
SECONDSINDAY=86400
DAYSINSESSION=$(expr "$SECONDSINSESSION" / "$SECONDSINDAY")
DAY_REMAINDER=$(expr "$SECONDSINSESSION" % "$SECONDSINDAY")
HOURSINSESSION=$(expr "$DAY_REMAINDER" / "$SECONDSINHOUR")
HOUR_REMAINDER=$(expr "$DAY_REMAINDER" % "$SECONDSINHOUR")
MINUTESINSESSION=$(expr "$HOUR_REMAINDER" / "$SECONDSINMINUTE")
SECONDSINSESSION=$(expr "$HOUR_REMAINDER" % "$SECONDSINMINUTE")
SESSIONLENGTH="$DAYSINSESSION days, $HOURSINSESSION hours, $MINUTESINSESSION minutes, and $SECONDSINSESSION seconds."
sleep 1
done &
因此,当我验证它正在运行jobs
(确实如此)并使用时echo $SESSIONLENGTH
,该值要么没有更新,要么根本不存在。
谁能帮我这个?
快速编辑:还应该注意的是,该脚本需要通过source
或运行.
。
答案1
解决了OP的问题后,我想我应该解释这个问题并选择解决方案。
编写了基本脚本后,正在使用该source
功能(或其别名.
)对其进行测试。在这种情况下,脚本运行正常。它之所以有效,是因为 shell 变量位于 shell 进程内。
当作为后台进程运行(或仅作为命令调用)时,该脚本作为一个单独的进程运行,从 shell 进程派生。 shell进程对新进程上的变量一无所知,解释一下原因
该值要么未更新,要么根本不存在。
两个进程可以通过多种方式进行通信,但我们在聊天中选择的一种是使用临时文件。在命令提示符或另一个脚本中,OP 只能cat myfile
查看变量的最新值。
这种方法适合这种情况,因为
- 它符合 OP 当前的专业水平
- 数据量低
- 业绩预期较低。
我们注意到文件系统正在处理并发问题,并且在这种情况下OP不关心竞争条件和其他并发问题。
如果一次只能运行脚本的一份副本,则文件名可以是常量。否则应分配一个临时文件;大多数 shell 都有分配临时文件的方法。如果这更多的是生产场景,则脚本应该花一些精力来清理旧文件。
答案2
当您使用时bash
,您trap
可以调试信号,以便SESSIONLENGTH
在每个命令之前重新计算:
trap 'source /path/to/sessionlength' DEBUG
源sessionlength
脚本只需是循环的内容,不需要其周围的while
和sleep
部分:
SECONDSINSESSION=$SECONDS
SECONDSINMINUTE=60
SECONDSINHOUR=3600
SECONDSINDAY=86400
DAYSINSESSION=$(expr "$SECONDSINSESSION" / "$SECONDSINDAY")
DAY_REMAINDER=$(expr "$SECONDSINSESSION" % "$SECONDSINDAY")
HOURSINSESSION=$(expr "$DAY_REMAINDER" / "$SECONDSINHOUR")
HOUR_REMAINDER=$(expr "$DAY_REMAINDER" % "$SECONDSINHOUR")
MINUTESINSESSION=$(expr "$HOUR_REMAINDER" / "$SECONDSINMINUTE")
SECONDSINSESSION=$(expr "$HOUR_REMAINDER" % "$SECONDSINMINUTE")
SESSIONLENGTH="$DAYSINSESSION days, $HOURSINSESSION hours, $MINUTESINSESSION minutes, and $SECONDSINSESSION seconds."
bash
还提供算术展开,因此您可以expr
通过使用避免每次生成 6 个进程
DAYSINSESSION=$((SECONDSINSESSION/SECONDSINDAY))
ETC。
答案3
您的脚本/方法存在一些技术问题,但是暂时忘记了技术细节,在直观的层面上,您想要做的是设置一些计数过程,然后在未来的某个时刻 - 您想要到交互地通过尝试直接引用变量值来查询进程以了解事情的进展情况。
你不能在 bash 中执行此操作,因为 bash shell 是同步语言解释器,这意味着当 bash 进程正在运行while
循环时,您不能中断它并说出相当于以下内容的编程:
“嘿 bash,停止你正在做的事情,然后去访问一些内存(变量
$SESSIONLENGTH
),然后将其值打印到屏幕上,然后继续你正在做的事情”
你想做的事情可以通过以下方式解决并发性并且是您在异步(nodejs、nginx)平台或运行事件循环(如 vim 或 Web 浏览器中的 javascript)的单线程进程中执行的操作类型。
来自维基百科并发文章
并发系统是一种计算可以在不等待所有其他计算完成的情况下进行的系统;多个计算可以同时进行
我会添加限定符或者至少在用户看来好像不止一项计算同时进行。
那么你可以在 bash 中做什么呢?您可以 - 正如所建议的那样,让 bash 将其输出发送到其他地方,然后从单独的进程读取数据,或者您可以将代码的修改版本放入文件中:
会话状态.sh
for i in seq 1 2 3 4 5
do
SECONDSINSESSION=$SECONDS
SECONDSINMINUTE=60
SECONDSINHOUR=3600
SECONDSINDAY=86400
DAYSINSESSION=$(expr "$SECONDSINSESSION" / "$SECONDSINDAY")
DAY_REMAINDER=$(expr "$SECONDSINSESSION" % "$SECONDSINDAY")
HOURSINSESSION=$(expr "$DAY_REMAINDER" / "$SECONDSINHOUR")
HOUR_REMAINDER=$(expr "$DAY_REMAINDER" % "$SECONDSINHOUR")
MINUTESINSESSION=$(expr "$HOUR_REMAINDER" / "$SECONDSINMINUTE")
SECONDSINSESSION=$(expr "$HOUR_REMAINDER" % "$SECONDSINMINUTE")
SESSIONLENGTH="$DAYSINSESSION days, $HOURSINSESSION hours, $MINUTESINSESSION minutes, and $SECONDSINSESSION seconds."
echo $SESSIONLENGTH
sleep 2
done
然后来源:
$ source session_state.sh
0 days, 0 hours, 46 minutes, and 26 seconds.
0 days, 0 hours, 46 minutes, and 28 seconds.
0 days, 0 hours, 46 minutes, and 30 seconds.
0 days, 0 hours, 46 minutes, and 32 seconds.
0 days, 0 hours, 46 minutes, and 34 seconds.
0 days, 0 hours, 46 minutes, and 36 seconds.
然后查询变量
$ echo $SESSIONLENGTH
0 days, 0 hours, 46 minutes, and 36 seconds.
您将获得该变量的最终状态,但只有在该过程完成之后。
在同步、单线程 shell 上,您最初想要的一种理论解决方案是
- 创建一个第二道工序监控你的键盘输入
- 与 bash 有一些预先建立的安排,因此第二个进程可以
- 队列您的用户生成请求,以便在 bash 时
- 完成了当前的帧/代码单元,它会
- 检查队列中的用户请求,
- 运行它们,然后
- 从上次停止的地方继续
换句话说事件循环。
答案4
据我了解您的要求,您想知道您的脚本已经运行了多长时间。
如果您在脚本启动时创建文件,那么您就有开始时间。自纪元以来 crond 开始时间的示例:
$ stat -c %Y /var/run/crond.pid
1447709241
自 crond 启动以来的第二次:
$ echo $(( $(date +%s)-$(stat -c %Y /var/run/crond.pid) ))
21740420
显示为日期:
$ date -d @$(( $(date +%s)-$(stat -c %Y /var/run/crond.pid) )) +%F\ %T
1970-09-09 16:09:56
如果应该读取变量的程序无法执行上述计算,那么您应该将 $SESSIONLENGTH 写入文件,然后其他进程应该读取该文件。