2016-03-15 3 views
0

У меня есть сценарий bash, который использует jq для поиска «зависимых» данных в некоторых JSON и принимает закрытие (найти зависимости зависимостей зависимостей и т. Д.).Scope issue memoising bash function with associative array

Это прекрасно работает, но может быть очень медленным, так как он может снова и снова находить одни и те же зависимости, поэтому я хотел бы его увековечить.

Я попытался использовать глобальный ассоциативный массив для сопоставления аргументов с результатами, но массив, похоже, ничего не хранит.

я извлек соответствующий код в следующей демо:

#!/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, кажется, указывает, что я храню и смотреть но они не сохраняются во всех ссылках expensive, так как мы попали в ветвь Didn't use cache.

Для меня это выглядит как предмет, возможно, вызванный declare. Однако declare -A, по-видимому, является требованием для использования ассоциативных массивов.

Вот моя версия Баша:

$ 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. 

Как выяснить поведение я испытываю, я бы также оценить альтернативные пути функций memoising в Баше (желательно ничего, что прикасается к файловой системе, даже если это RAM на основе)

ответ

1

у вас есть несколько вопросов:

  1. declare -g имеет смысл только внутри функции. Снаружи переменная уже является глобальной.

  2. Глобальная переменная глобальна только для процесса , в котором оно объявлено. Вы не можете использовать глобальные переменные в процессах.

  3. Запуск expensive внутри подстановки команды делает это в отдельном процессе, поэтому создаваемый и заполняемый им кеш исчезает с этим процессом.

  4. Выполнение expensive в качестве первой команды трубопровода также создает новый процесс; используемый кэш, видим только для этого процесса.

Вы можете обойти это, убедившись, что expensive выполняется только в текущей оболочке с

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 оптимизирована для запуска новых процессов и управления файлами ввода/вывода.

+0

Ваша точка 3 кажется немного выключенной; вы переводите 'grep' в' дорогой', а не наоборот (он дает 'grep: (стандартный ввод): Ошибка ввода/вывода') – Warbo

+0

Похоже на' [["x $ (...) "=" xfoo "]]' строка - это та, которая использует подпроцесс и, следовательно, не заполняет кеш. Линия 'grep' фактически работает как есть, если над ней помещен« дорогой »вызов, чтобы заполнить кеш. – Warbo

+0

Правильно, я не читал оригинал достаточно близко. Труба выдалась мне больше, но '$ (...)' * также * работает 'дорогой' в подпроцессе. – chepner