Atribua dinamicamente o array ao valor de uma variável. Eval?

1

Estou usando o Bash 4.

Estou experimentando algumas experiências com o Bash. Eu quero atribuir dinamicamente uma matriz para o valor de uma variável. Se você ler o código abaixo, será mais fácil de entender.

myFunc =RESULT_PREF 'bar' 'baz'

function myFunc() {
    local params
    parser params "$@"
}


function parser() {
    if [ '=' = "${2::1}" ]; then
        local name="${1}"
        shift 2
        local param_array=( "$@" )

        # In PHP, I'd do:
        # $$name = $param_array

        # The ideal outcome:
        # $params should contain the values 'bar' and 'baz'.
    fi
}

Como faço o que eu mencionei nos comentários do meu código? Eu não consigo fazer isso usando eval. Mesmo se eu pudesse, eu li tantas coisas ruins sobre eval.

Algum conselho?

    
por Housni 24.10.2015 / 01:42

2 respostas

1
myFunc(){
    parse "$1" &&                          #parse() is a test
    eval "  shift                          #$1 is a valid, eval-safe name
            local name$1 ${1#=}="'("$@")'  #$1 is expanded before shift
}

parse()
    case ${1#=} in
    ("$1"|""|[0-9]*|*[!_[:alnum:]]*) ! :   #return false for all invalid names
    esac

Ele também funciona sem eval dado outra etapa de execução ou duas:

myFunc(){
    local -a "name$1[@]" "${1#=}"='("${@:2}")' &&
    [ -z "${1%=*}" ] && printf %s\n "${!name}"
}

Nesse caso, apenas permito que o local builtin faça toda a validação e atribuição de uma só vez. Basicamente, se o comando local puder atribuir "${1#=}"=("${@#"$1"}") com sucesso e "${1%=*}" for nulo, você saberá que a atribuição ocorreu com sucesso. Qualquer erro de sintaxe - como nomes de shell inválidos - falhará automaticamente e return com erro durante a atribuição local , e a validação simples [ test ] que segue é tudo o que é necessário para garantir que você não usa t acidentalmente local name=morename=("${@#"$1"}") .

O resultado natural disso é que quando um nome ruim é passado como =$1 , o shell automaticamente imprimirá uma mensagem de erro significativa para stderr e fará todo o tratamento de erros automaticamente sem confusão.

Assim:

myFunc =goodname 1 2 3 'and some     ;more' &&
myFunc =bad-name 1 2 3 'and some     ;more'
1
2
3
and some     ;more
bash: local: 'bad-name=("${@:2}")': not a valid identifier

Observe que fazer function name (){ list ;} definitivamente não é o que você pretende fazer.

Se você pretende localizar as armadilhas da função e semelhantes, precisará de function name { list ;} .

Se você pretende compartilhar todos os estados, mas suas local ly variáveis definidas com o shell atual, então function name (){ list ;} ou name () list são equivalentes porque a palavra-chave function é ignorada pelo shell (exceto que alguns shells que o implementam tendem a analisar incorretamente o seguinte list incorretamente se ele não estiver contido nas chaves quando name é seguido por () .

    
por 24.10.2015 / 10:00
3

No bash 4.3+, existe uma maneira de definir uma referência de variável que você pode ver em help declare :

-n: make NAME a reference to the variable named by its value

Portanto, um script como esse exibe uma linha a :

n(){ declare -n g="$1"; g=a; }
n b; printf '%s\n' "$b";

Então, veja como você deve fazer isso em parser :

parser() {
    [ "$1" ] || ! echo 'no name given' >&2 || return 42
    if [[ "$2" == =* ]]; then
        declare -n name="$1"
        shift 2
        name=("$@")
    fi
}

Usar eval funciona, mas pode ser inseguro se você expandir o rhs cedo demais:

shopt -e extglob # bash >= 3
[[ "$1" == [[:alpha:]_]*([[:word:]]) ]] || return 42 # invalid varname
# mikeserv has another solution for filtering, basically expanding
# the inverse of the expression to basic, portable globs
# Do this:
eval "$1"'=( "$@" )'
eval "$foo=\$bar"
# But not:
eval "$1=(\"$@\")"
eval "$foo=$bar"

Acho que você saberá o que fazer com eval . Mas simplesmente por uma possibilidade maior de aceitar a minha resposta, deixe-me escrevê-la por completo:

shopt -s extglob
parser() {
    [[ "$1" == [[:alpha:]_]*([[:word:]]) ]] || ! echo 'bad varname' >&2 || return 42
    if [[ "$2" == =* ]]; then
        name="$1"
        shift 2
        eval "$name=(\"\$@\")"
    fi
}
    
por 24.10.2015 / 02:02