You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Bash子shell循环中修改父Shell全局变量

解决子shell无法更新父shell全局变量的问题

你的问题根源很明确:当你用&将函数后台运行时,它会在子shell中执行,子shell拥有独立的环境变量空间。你在get_load里修改的LASTLOAD只存在于这个子shell中,父shell和另一个后台运行的flip_led子shell完全看不到这个更新——这就是变量一直没变化的原因。

下面给你两种实用的解决方案,替代全局变量的传递方式:

方案1:使用命名管道(FIFO)实现进程间通信

命名管道是Linux中进程间传递数据的经典方式,适合这种持续传递数值的场景。

修改后的完整脚本

readonly LEDTRIGGER="/sys/class/leds/led0/trigger"
readonly LEDBRIGHTNESS="/sys/class/leds/led0/brightness"
# 创建命名管道
LOAD_FIFO="/tmp/load_fifo"
mkfifo "$LOAD_FIFO"
# 确保脚本退出时清理管道
trap 'rm -f "$LOAD_FIFO"; exit' INT TERM EXIT

ISRUNNING=1

function get_load() {
    local user1 sys1 idle1 user2 sys2 idle2 u s i load percent
    while [[ $ISRUNNING -eq 1 ]]; do
        # 获取初始CPU数据
        read -r user1 sys1 idle1 <<<"$(grep 'cpu ' /proc/stat | awk '{print $2" "$4" "$5}')"
        sleep 1
        # 获取1秒后的CPU数据
        read -r user2 sys2 idle2 <<<"$(grep 'cpu ' /proc/stat | awk '{print $2" "$4" "$5}')"
        u=$(echo "scale=4;$user2-$user1" | bc)
        s=$(echo "scale=4;$sys2-$sys1" | bc)
        i=$(echo "scale=4;$idle2-$idle1" | bc)
        # 计算负载百分比(0-1之间的小数)
        load=$(echo "scale=4;($u+$s)*100/($u+$s+$i)" | bc)
        percent=$(echo "scale=4;$load/100" | bc)
        # 将结果写入管道(覆盖式写入,确保每次都是最新值)
        echo "$percent" > "$LOAD_FIFO"
    done
}

function flip_led() {
    local lastload remainder
    while [[ $ISRUNNING -eq 1 ]]; do
        # 从管道读取最新的负载值(阻塞等待,直到有数据)
        read -r lastload < "$LOAD_FIFO"
        # 计算熄灭时长
        remainder=$(echo "scale=2;1-$lastload" | bc)
        echo 1 | sudo tee "$LEDBRIGHTNESS" >/dev/null
        echo "led on for $lastload, off for $remainder"
        sleep "$lastload"
        echo 0 | sudo tee "$LEDBRIGHTNESS" >/dev/null
        sleep "$remainder"
    done
}

# 初始化LED状态
echo none | sudo tee "$LEDTRIGGER" >/dev/null
echo 0 | sudo tee "$LEDBRIGHTNESS" >/dev/null

# 启动两个后台进程
get_load &
flip_led &

# 等待后台进程(可选,避免脚本直接退出)
wait

方案说明

  • 我们创建了一个命名管道/tmp/load_fifo,get_load每秒将计算好的负载值写入管道,flip_led每次循环从管道读取最新值。
  • 用trap命令确保脚本退出(比如按Ctrl+C)时自动删除管道,避免残留文件。
  • 管道的读取是阻塞的,但因为get_load每秒写入一次,所以flip_led能实时获取最新数据。

方案2:使用临时文件存储负载值

如果觉得管道有点复杂,用临时文件传递数据也是简单有效的方式:

修改后的核心部分

# 临时文件路径
LOAD_FILE="/tmp/load_value"
# 初始化文件
echo "0" > "$LOAD_FILE"
trap 'rm -f "$LOAD_FILE"; exit' INT TERM EXIT

function get_load() {
    # ...(前面计算逻辑不变)
    # 将结果写入临时文件
    echo "$percent" > "$LOAD_FILE"
}

function flip_led() {
    local lastload remainder
    while [[ $ISRUNNING -eq 1 ]]; do
        # 读取临时文件中的最新值
        lastload=$(cat "$LOAD_FILE")
        remainder=$(echo "scale=2;1-$lastload" | bc)
        # ...(后面LED控制逻辑不变)
    done
}

方案说明

  • get_load每秒将最新负载值写入临时文件,flip_led每次循环读取文件内容。
  • 这种方式比管道更直观,适合简单的数值传递场景,但要注意文件读写的原子性(这里因为是覆盖写入,问题不大)。

为什么全局变量不行?

再强调一次:子shell是父shell的副本,它的变量修改完全独立,不会影响父shell或其他子shell。除非你用进程间通信的方式(比如管道、文件、套接字),否则无法直接在子shell之间共享变量更新。

内容的提问来源于stack exchange,提问作者ibrokemypie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 10:22:57