带关联数组的bash函数的作用域问题

带关联数组的bash函数的作用域问题,bash,scope,associative-array,memoization,Bash,Scope,Associative Array,Memoization,我有一个bash脚本,它使用jq在一些JSON中查找“依赖项”数据,并进行闭包(查找依赖项的依赖项,等等) 这很好,但可能会非常慢,因为它可能会一遍又一遍地查找相同的依赖项,所以我想把它记下来 我尝试使用全局关联数组将参数与结果关联,但该数组似乎没有存储任何内容 我已将相关代码提取到以下演示中: #!/usr/bin/env bash # Associative arrays must be declared; use 'g' for global declare -Ag MYCACHE fu

我有一个bash脚本,它使用
jq
在一些JSON中查找“依赖项”数据,并进行闭包(查找依赖项的依赖项,等等)

这很好,但可能会非常慢,因为它可能会一遍又一遍地查找相同的依赖项,所以我想把它记下来

我尝试使用全局关联数组将参数与结果关联,但该数组似乎没有存储任何内容

我已将相关代码提取到以下演示中:

#!/usr/bin/env bash

# Associative arrays must be declared; use 'g' for global
declare -Ag MYCACHE
function expensive {
    # Look up $1 in MYCACHE
    if [ "${MYCACHE[$1]+_}" ]
    then
        echo "Using cached version" >> /dev/stderr
        echo "${MYCACHE[$1]}"
        return
    fi

    # Not found, perform expensive calculation
    RESULT="foo"

    echo "Caching result" >> /dev/stderr
    MYCACHE["$1"]="$RESULT"

    # Check if the result was cached
    if [ "${MYCACHE[$1]+_}" ]
    then
        echo "Cached" >> /dev/stderr
    else
        abort "Didn't cache"
    fi

    # Done
    echo "$RESULT"
}

function abort {
    echo "$1" >> /dev/stderr
    exit 1
}

# Run once, make sure result is "foo"
[[ "x$(expensive "hello")" = "xfoo" ]] ||
    abort "Failed for hello"

# Run again, make sure "Using cached version" is in stderr
expensive "hello" 2>&1 > /dev/null | grep "Using cached version" ||
    abort "Didn't use cache"
以下是我的结果:

$ ./foo.sh 
Caching result
Cached
Didn't use cache
我们得到的
Cached
这一事实似乎表明我正在正确地存储和查找值,但它们在调用
昂贵的
时不会被保留,因为我们点击了
没有使用cache
分支

对我来说,这似乎是一个范围问题,可能是由
声明
引起的。然而,
declare-A
似乎是使用关联数组的必要条件

以下是我的bash版本:

$ bash --version
GNU bash, version 4.3.42(1)-release (i686-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$bash--版本
GNUBash,版本4.3.42(1)-发行版(i686 pc linux GNU)
版权所有(C)2013免费软件基金会。
许可证GPLv3+:GNU GPL版本3或更高版本
这是自由软件;您可以自由更改和重新分发它。
在法律允许的范围内,不存在任何担保。
除了弄清楚我正在经历的行为外,我还想了解在bash中记忆函数的其他方法(最好不要涉及文件系统,即使它是基于RAM的)

您有一些问题:

  • declare-g
    仅在函数内部有意义。在外部,变量已经是全局变量

  • 全局变量仅对声明它的进程是全局的。不能在进程之间共享全局变量

  • 在命令替换中运行
    昂贵的
    会在单独的进程中运行,因此它创建和填充的缓存会随该进程一起消失

  • 作为管道的第一个命令运行
    昂贵的
    ,也会创建一个新进程;它使用的缓存仅对该进程可见

  • 您可以通过确保仅在当前shell中使用

    expensive "hello" > tmp.txt && read result < tmp.txt
    [[ $foo = foo ]] || abort ...
    expensive "hello" 2>&1 > /dev/null < <(grep "Using cached version") ||
    abort "Didn't use cache"
    
    昂贵的“hello”>tmp.txt&&read result&1>/dev/null<您有几个问题:

  • declare-g
    仅在函数内部有意义。在外部,变量已经是全局变量

  • 全局变量仅对声明它的进程是全局的。不能在进程之间共享全局变量

  • 在命令替换中运行
    昂贵的
    会在单独的进程中运行,因此它创建和填充的缓存会随该进程一起消失

  • 作为管道的第一个命令运行
    昂贵的
    ,也会创建一个新进程;它使用的缓存仅对该进程可见

  • 您可以通过确保仅在当前shell中使用

    expensive "hello" > tmp.txt && read result < tmp.txt
    [[ $foo = foo ]] || abort ...
    expensive "hello" 2>&1 > /dev/null < <(grep "Using cached version") ||
    abort "Didn't use cache"
    
    昂贵的“hello”>tmp.txt&&read result昂贵的“hello”2>&1>/dev/null<您的第3点似乎有点偏离;您正在将
    grep
    输送到
    昂贵的
    ,而不是相反的方向(它给出
    grep:(标准输入):输入/输出错误
    )看起来
    [[“x$(…)”=“xfoo”]]
    行是使用子进程的行,因此没有填充缓存。
    grep
    行实际上是按原样工作的,如果在它上面放置了一个
    昂贵的
    调用来填充缓存。是的,我没有仔细阅读原文。管道对我来说更突出,但是
    $(…)
    也在子流程中运行
    昂贵的
    ;您正在将
    grep
    输送到
    昂贵的
    ,而不是相反的方向(它给出
    grep:(标准输入):输入/输出错误
    )看起来
    [[“x$(…)”=“xfoo”]]
    行是使用子进程的行,因此没有填充缓存。
    grep
    行实际上是按原样工作的,如果在它上面放置了一个
    昂贵的
    调用来填充缓存。是的,我没有仔细阅读原文。管道对我来说更加突出,但是
    $(…)
    也在子流程中运行
    昂贵的