范围问题 memoising bash 函数与关联数组
Scoping issue memoising bash function with associative array
我有一个bash脚本,它使用jq
在某些JSON中查找'dependency'数据,并采取闭包(找到依赖关系的依赖关系,等)。
这工作正常,但可能会很慢,因为它可能会一遍又一遍地查找相同的依赖关系,所以我想记住它。
我尝试使用全局关联数组将参数与结果相关联,但该数组似乎没有存储任何内容。
我已经将相关代码提取到以下演示中:
#!/usr/bin/env bash
# Associative arrays must be declared; use 'g' for global
declare -Ag MYCACHE
function expensive {
# Look up in MYCACHE
if [ "${MYCACHE[]+_}" ]
then
echo "Using cached version" >> /dev/stderr
echo "${MYCACHE[]}"
return
fi
# Not found, perform expensive calculation
RESULT="foo"
echo "Caching result" >> /dev/stderr
MYCACHE[""]="$RESULT"
# Check if the result was cached
if [ "${MYCACHE[]+_}" ]
then
echo "Cached" >> /dev/stderr
else
abort "Didn't cache"
fi
# Done
echo "$RESULT"
}
function abort {
echo "" >> /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
的事实似乎表明我正在正确存储和查找值,但是由于我们点击了 Didn't use cache
分支。
对我来说这似乎是一个范围界定问题,可能是由 declare
引起的。但是,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 中使用其他方法来记忆函数(最好不要接触文件系统,即使它是基于 RAM 的)
您有几个问题:
declare -g
仅在函数内部有意义。在外面,一个变量已经是全局的了。
全局变量仅对声明它的 进程 是全局的。不能跨进程共享全局变量。
运行 expensive
在命令替换中是在一个单独的进程中执行的,因此它创建和填充的缓存随该进程一起消失。
运行 expensive
作为管道的第一个命令也会创建一个新进程;它使用的缓存仅对该进程可见。
您可以通过确保 expensive
在当前 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"
然而,Shell 脚本并不是为这种类型的数据处理而设计的。如果缓存很重要,请使用对数据结构和数据内存处理有更好支持的不同语言。 Shell 针对启动新进程和管理 input/output 文件进行了优化。
我有一个bash脚本,它使用jq
在某些JSON中查找'dependency'数据,并采取闭包(找到依赖关系的依赖关系,等)。
这工作正常,但可能会很慢,因为它可能会一遍又一遍地查找相同的依赖关系,所以我想记住它。
我尝试使用全局关联数组将参数与结果相关联,但该数组似乎没有存储任何内容。
我已经将相关代码提取到以下演示中:
#!/usr/bin/env bash
# Associative arrays must be declared; use 'g' for global
declare -Ag MYCACHE
function expensive {
# Look up in MYCACHE
if [ "${MYCACHE[]+_}" ]
then
echo "Using cached version" >> /dev/stderr
echo "${MYCACHE[]}"
return
fi
# Not found, perform expensive calculation
RESULT="foo"
echo "Caching result" >> /dev/stderr
MYCACHE[""]="$RESULT"
# Check if the result was cached
if [ "${MYCACHE[]+_}" ]
then
echo "Cached" >> /dev/stderr
else
abort "Didn't cache"
fi
# Done
echo "$RESULT"
}
function abort {
echo "" >> /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
的事实似乎表明我正在正确存储和查找值,但是由于我们点击了 Didn't use cache
分支。
对我来说这似乎是一个范围界定问题,可能是由 declare
引起的。但是,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 中使用其他方法来记忆函数(最好不要接触文件系统,即使它是基于 RAM 的)
您有几个问题:
declare -g
仅在函数内部有意义。在外面,一个变量已经是全局的了。全局变量仅对声明它的 进程 是全局的。不能跨进程共享全局变量。
运行
expensive
在命令替换中是在一个单独的进程中执行的,因此它创建和填充的缓存随该进程一起消失。运行
expensive
作为管道的第一个命令也会创建一个新进程;它使用的缓存仅对该进程可见。
您可以通过确保 expensive
在当前 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"
然而,Shell 脚本并不是为这种类型的数据处理而设计的。如果缓存很重要,请使用对数据结构和数据内存处理有更好支持的不同语言。 Shell 针对启动新进程和管理 input/output 文件进行了优化。