Bash:如何在后台while循环中检索变量值

4

以以下bash脚本为例。有两个循环,第一个在后台执行,第二个打印myvar的值:

#!/bin/bash

myvar=AAA

while true;
do
    sleep 3
    myvar=BBB
    sleep 3
    myvar=CCC
done &

while true;
do
    echo "${myvar}"
    sleep 1
done

我实际得到的输出:
AAA
AAA
AAA
...

The output I would like to get:

AAA
BBB
CCC
BBB
CCC
...
2个回答

7
这是由于&在第一个while循环中创建了一个新的子shell。我相信你需要使用一些IPC来解决这个问题。使用管道或命名管道实现生产者/消费者设置是合理的。以下是一个粗略的示例:
#!/bin/bash

myvar=AAA

while true;
do
    sleep 3
    myvar_piped=BBB
    echo $myvar_piped # this goes to the pipe.
    sleep 1
done | # this connects the two loops.

while true;
do
    # if we consumed something (timeout=1) print it, else print our own variable.
    if read -t 1 myvar_piped #
    then
        echo "${myvar_piped}"
    else
        echo "${myvar}"
    fi  
done

输出:

AAA
AAA
AAA
BBB
AAA
AAA
AAA
AAA
BBB

谢谢。我是Bash新手,你能给我一些例子吗? - mt22
+1 for the example, 但是第一个循环不再在后台执行了,现在这两个循环是顺序执行的,对吧? - mt22
@Lorenzo:不,管道将第一个循环连接到第二个循环,并且两者并行执行,除非您可以执行等待输入等同步操作。将其视为在命令行中构建的任何其他管道,但是使用while循环而不是命令。 - Eduardo Ivanec
@Lorenzo:澄清一下:通常情况下,当您使用管道过滤文本(例如 ps auxww | grep root)时,并不是第一个命令的整个输出在传递到管道中的第二个命令之前就已经产生了。缓冲可能会发生,但所有命令都像是不同 shell 的子进程一样并行运行(实际上它们属于大多数 Unix 实现中的同一进程组,这可能会影响调度,但那是完全不同的话题)。 - Eduardo Ivanec

5

实际上,在父Shell中没有直接读取变量的方法。

第一个循环在子Shell中运行,因为有&;子Shell的内存完全独立于主Shell的内存,并且除了在子Shell上运行调试器之外,没有任何方法可以从父Shell访问子进程的内存。

如果您可以修改子Shell进程以每秒写入其变量的值,则父进程可能能够检测到该值。 或者,如果子Shell将变量写入具有已知名称的文件每次更改变量,则可以在父进程中随时读取该文件:

#!/bin/bash

tmp=$(mktemp)

trap "rm -f $tmp; exit 1" 0 1 2 3 13 15

myvar=AAA
echo $myvar > $tmp

while true;
do
    sleep 3
    myvar=BBB
    echo $myvar > $tmp
    sleep 3
    myvar=CCC
    echo $myvar > $tmp
done &

while cat $tmp
do
    sleep 1
done

rm -f $tmp
trap 0
会确保在大多数情况下(信号HUP、INT、QUIT、PIPE和TERM)删除临时文件。

谢谢。你觉得一个更强大的脚本语言会更适合我的需求吗? - mt22
1
在任何脚本语言中设置共享内存都不太吸引人,尽管我认为Perl和Python可以胜任这项工作。你仍然需要安排它们写入和读取共享内存,因此你必须能够定位共享内存中的变量。我认为某种基于文件的IPC系统可能更适合你。更强大的脚本语言能够在父进程中保持文件打开状态并倒回和重新读取,而不是每秒重新打开文件。另一方面,在循环中有一个sleep 1时,性能并不那么关键。 - Jonathan Leffler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接