bash, eval, защитить трубы и двоеточие в строке

Вот самая простая дистиллированная версия моей проблемы. Сложность, которая остается, имеет веские причины, даже если здесь она не очевидна. Кроме того, скрипт является внутренним и не имеет возможности выполнить вредоносный код, поэтому eval работает отлично; не нужно слышать о том, насколько это зло... ;) Предположим на данный момент, что вертикальные черты и двоеточия в ключевой строке являются обязательными разделителями.

key="target|platform|repo:revision"
hashname="hashmap"
declare -A $hashname

eval $hashname[$key]=0
echo $(eval $hashname[$key])

Конечно, последние две строки имеют проблемы, потому что eval работает с каналами и двоеточиями внутри переменной $key. Мой вопрос: как я могу защитить такую ​​​​строку от eval? И мне нужен eval, потому что я имею в виду имя хэш-карты, а не ее саму. Заранее спасибо.

==================================================================================

Хорошо, я на самом деле начинаю думать, что моя проблема не в каналах, двоеточии и eval. Итак, позвольте мне вставить настоящий код

function print_hashmap()
{
    local hashname=$1
    for key in `eval echo \$\{\!$hashname[@]\}`; do
        local deref="$hashname[$key]"
        local value=${!deref}
        echo "key=${key} value=${value}"
    done
}
function create_permutations2()
{
    local hashname=$1   ; shift
    local builds=$1     ; shift
    local items=$1      ; shift
    local separators=$@

    echo "create_permutations(hashname=${hashname}, builds=${builds}, items=${items}, separators=${separators})"

    if [ NULL != "${builds}" ]; then
        for build in `echo $builds | tr ',' ' '`; do
            local target="${build%%:*}"
            local platforms="${build##*:}"
            for platform in `echo $platforms | tr '|' ' '`; do
                local key=
                local ref=
                if [ NULL != "${items}" ]; then
                    if [ NULL == "${separators}" ]; then separators=' '; fi
                    for separator in $separators; do
                        items=`echo $items | tr $separator ' '`
                    done
                    for item in $items; do
                        key="${target}|${platform}|${item}"
                        ref="$hashname[$key]"
                        declare "$ref"=0
                    done
                else
                    key="${target}|${platform}"
                    ref="$hashname[$key]"
                    declare "$ref"=0
                fi
            done
        done
    fi

    echo "created the following permutations:"
    print_hashmap $hashname
}
builds="k:r|s|w,l:r|s|w"
repos="c:master,m:master"
hashname="hashmap"
declare -A $hashname

create_permutations2 $hashname $builds $repos ","
print_hashmap $hashname

Недавно я изменил свой код, чтобы он соответствовал предложениям FatalError по использованию ref вместо eval, и ошибка та же самая: синтаксическая ошибка в выражении (маркер ошибки рядом с: master)


person Scott Idler    schedule 05.02.2012    source источник
comment
Вы пытались избежать символов канала? например: \| вместо |   -  person Gigi    schedule 05.02.2012
comment
Что ты пытаешься сделать? Это не то, как вы получаете доступ к членам массива   -  person Kevin    schedule 05.02.2012
comment
@ Кевин, это не массив. Это хэш.   -  person Scott Idler    schedule 05.02.2012
comment
@ScottIdler И вы индексируете их как массивы. Я только что проверил, и вам нужно {}   -  person Kevin    schedule 06.02.2012
comment
Кроме того, ваш последний eval в подоболочке попытается выполнить 0, если он сработает.   -  person Kevin    schedule 06.02.2012


Ответы (4)


Может быть, удваивает кавычки вокруг $key?

eval $hashname["$key"]=0
echo $(eval $hashname["$key"]=0)
person oHo    schedule 05.02.2012

Пытаться:

eval $hashname'[$key]'=0
eval result=$hashname'[$key]'

Это передает $ в eval вместо того, чтобы сначала расширять параметр.

person jilles    schedule 05.02.2012

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

eval $hashname["'"$key"'"]=0

Это защитило бы от всего, кроме одинарных кавычек в $key. Тем не менее, вы можете сделать то же самое с меньшим количеством головной боли. Вот мой обновленный скрипт для иллюстрации:

key="target|platform|repo:revision"
hashname="hashmap"
declare -A $hashname

echo "With eval:"
eval $hashname["'"$key"'"]=0
eval echo \${$hashname["'"$key"'"]}
echo

echo "Without eval:"
ref="$hashname[$key]"
echo ${!ref}
declare "$ref"="1"
echo ${!ref}

Обратите внимание, что я изменил вашу исходную строку eval, потому что это не имело для меня смысла — она пыталась выполнить результат, а не распечатать его. Во второй части вы можете использовать косвенное обращение для доступа к значению, а затем declare для присвоения ему значения. Тогда вам не нужно беспокоиться об интерпретации этих символов.

Это дает результат:

With eval:
0

Without eval:
0
1

Я не уверен на 100%, почему это не работает, но, похоже, это:

builds="k:r|s|w,l:r|s|w"
repos="c:master,m:master"
hashname="hashmap"
declare -A $hashname

function print_hashmap()
{
    local hashname=$1
    for key in `eval echo \$\{\!$hashname[@]\}`; do
        local deref="$hashname[$key]"
        local value=${!deref}
        echo "key=${key} value=${value}"
    done
}
function create_permutations2()
{
    local hashname=$1   ; shift
    local builds=$1     ; shift
    local items=$1      ; shift
    local separators=$@

    echo "create_permutations(hashname=${hashname}, builds=${builds}, items=${items}, separators=${separators})"

    if [ NULL != "${builds}" ]; then
        for build in `echo $builds | tr ',' ' '`; do
            local target="${build%%:*}"
            local platforms="${build##*:}"
            for platform in `echo $platforms | tr '|' ' '`; do
                local key=
                if [ NULL != "${items}" ]; then
                    if [ NULL == "${separators}" ]; then separators=' '; fi
                    for separator in $separators; do
                        items=`echo $items | tr $separator ' '`
                    done
                    for item in $items; do
                        key="${target}|${platform}|${item}"
                        ref="$hashname[$key]"
                        eval $hashname[\'$key\']=0
                    done
                else
                    key="${target}|${platform}"
                    eval $hashname[\'$key\']=0
                fi
            done
        done
    fi

    echo "created the following permutations:"
    print_hashmap $hashname
}

create_permutations2 $hashname $builds $repos ","
print_hashmap $hashname
person FatalError    schedule 05.02.2012
comment
Я не против использовать решение без eval, как вы продемонстрировали здесь. Я постараюсь посмотреть, смогу ли я заставить его работать с моей реальной проблемой. Однако можете ли вы объяснить свою предпоследнюю строку: declare $ref=1? Почему кавычки и почему декларировать? И требуются ли кавычки вокруг 1? - person Scott Idler; 05.02.2012
comment
Я изменил свой исходный пост, включив в него производственный код и пример использования, вызывающий такое поведение. Я реализовал ваше предложение и получил ту же ошибку. - person Scott Idler; 06.02.2012
comment
Кавычки вокруг 1 ничего не делают — просто моя привычка. Кавычки вокруг $ref здесь не имеют значения, но были бы, если бы, например, ваш ключ содержал пробел. - person FatalError; 06.02.2012
comment
Хорошо, а объявить? Я добавил свой полный пример с предложенными вами изменениями, и я все еще получаю сообщение об ошибке. - person Scott Idler; 06.02.2012

для этой небольшой части вопроса: «Мой вопрос в том, как я могу защитить такую ​​​​строку от eval?», Я просто упомяну «преобразование параметров» bash (см. справочную страницу), ${parameter@operator}. в частности, оператор Q возвращает форму в кавычках, которую можно безопасно использовать в качестве входных данных.

% echo ${key}
target|platform|repo:revision
% echo ${key@Q}
'target|platform|repo:revision'
person Greg Minshall    schedule 29.09.2019