Zsh, Atribuição de variável de matriz indireta sem usar eval

2

Eu tenho uma variável VARNAME que contém um nome de outra variável. Gostaria de atribuir a essa outra variável sem usar eval . Como posso fazer isso?

O motivo pelo qual não quero usar eval é o seguinte. Suponha primeiro uma função para prefixar a variável foo :

% prepend_foo() { foo=("$@" $foo) }
% foo=(1 2)
% print -l $foo
1
2
% prepend_foo x 'a b c' y
% print -l $foo
x
a b c
y
1
2
%

Agora, considere uma função generalizada para anexar a qualquer variável:

% prepend() { var=$1; shift; eval "$var=($@ ${(P)var})" }
% foo=(1 2)
% print -l $foo
1
2
% prepend foo x 'a b c' y
% print -l $foo
x
a
b
c
y
1
2
%

Como você pode ver, as variáveis com espaços são divididas em vários itens da matriz. Eu não pude combinar corretamente a citação para conseguir a coisa desejada.

No IRC, alguém sugeriu usar ${name::=word} , mas isso não funciona bem com matrizes:

21:23 < someone> > b=(bar baz); a=b; : ${(P)a::=(foo ${(P)a})}; typeset -p b
21:23 < machabot> someone: typeset b='(foo bar baz)'
21:23 < someone> dammit that's a string
    
por woky 18.05.2018 / 21:39

2 respostas

3

Para preceder elementos a um array, você pode simplesmente fazer:

a[1,0]=(more elements)

Ou você pode fazer:

a=(more elements "$a[@]")

Observe que fazendo:

a=(more elements $a)

removeria os elementos vazios em $a .

Agora, para criar uma função para isso, é para isso que o eval serve, mas você precisa ter a sintaxe correta:

prepend() {
  eval "${1}[1,0]"='("${@[2,-1]}")'
}

Veja como o ("${@[2,-1]}") está entre aspas simples, por isso é passado literalmente para eval .

Ou o caminho mais longo, mas não funcionaria para prepend var more elements (quando o nome da variável for var ):

prepend() {
  local var=$1; shift
  eval "$var"='("$@" "${'"$var"'[@]}")'
}

O código que você deseja eval avalia sobre prepend varname ... é:

 varname=("$@" "${varname[@]")

Você não deseja que "$@" seja expandido antes de ser passado para eval . Somente $var precisa ser expandido para lá.

Observe que o sinalizador de expansão da variável P é tão perigoso quanto eval quando usado com dados não-analisados.

var='x[$(uname>&2)0]'
echo "${(P)var}"

Será executado o comando uname .

    
por 18.05.2018 / 22:32
0

Sua função prepend pode ser escrita sem eval , se você percorrer os elementos para desimpedi-los, um de cada vez.

$ prepend () {
then> local i=$#*
then> while [[ $i > 1 ]]; do
then>   typeset -g "${1}[1,0]=$*[$i]"
then>   i=$((i - 1))
then> done
then> }
$ foo=(a 'b c' d)
$ prepend foo m 'n o p' q r
$ print -l $foo
m
n o p
q
r
a
b c
d
$ 

Observe que -g é necessário para o typeset aceitar a configuração de um elemento de matriz não local, mas não significa que a matriz referenciada por $1 não possa ser local para a função de chamada. Apenas significa "está tudo bem procurar a pilha de chamadas de função para encontrar este array".

    
por 29.06.2018 / 02:02