Saída para variável com um comando indireto (eval)

1

O que há de errado com este comando indireto quando executado com o eval?

#!/bin/bash
OS=AIX
host=myhost

CMD_AIX="(o=\'host \"$host\" \')"
CMD=\$CMD_$OS

echo $CMD
eval echo $CMD

eval "$CMD"

Ouput:

$ myscript.sh
$CMD_AIX
(o='host "myhost" ')
myscript.sh: line 12: (o='host: command not found

EDIT - NOTAS: Está em uma estrutura muito heterogênea com cerca de 40 servidores, misturando SCO, AIX e Linux, várias versões de cada um, incluindo variações de 32 e 64 bits. O SCO e o AIX não podem ser atualizados. Bash e Korn shell são comuns em todos os servidores, mas estou limitado à versão 3 do bash (no SCO), o Ksh tem várias diferenças entre eles, então estamos preferindo o bash, que não tem suporte para matrizes associativas na v3 não pode ser atualizado e está fora de minhas decisões, e esses servidores começarão a ser substituídos pelo AIX 7 e pelo Linux em alguns meses).

Há um grande sistema a ser mantido que inclui alguns scripts já baseados em sh e bsh, nós não podemos e não podemos reconstruí-lo agora, apenas titular a manutenção para resistir a alguns meses até o final da substituição.

A abordagem usada nesses scripts é amplamente difundida em uma grande solução que gera alguns scripts, os quais não podemos mudar do zero.

O código de amostra acima é apenas um snippet adotado, não é o código real. Por favor, não considere a lógica, apenas o problema da indireção (eval).

SOLUÇÃO: tenho uma solução que funcionou muito bem apenas encadeando o eval como:

torun='eval $CMD'
output='eval torun'

Esta resposta e , que funcionou muito bem e respondeu à pergunta, foi estranhamente votado muito.

    
por Luciano 25.05.2015 / 18:15

5 respostas

2

Você tem uma variável CMD cujo valor é a string $CMD_AIX e o valor da variável CMD_AIX é a string (o='host "myhost" ') . Você quer interpretar o valor de CMD_AIX como um comando shell e executá-lo.

Para executar o valor de CMD_AIX como um comando shell, você precisa construir um comando shell que faça isso. É para isso que eval :

eval "$CMD_AIX"

Este comando, por sua vez, é construído dinamicamente. Então você precisa executá-lo com eval :

dispatcher=…
eval "$dispatcher"

Dada a forma como o comando interno é construído, obtemos

dispatcher="eval \"\$CMD_$OS\""
eval "$dispatcher"

Ou cortar uma variável:

eval "eval \"\$CMD_$OS\""

Observe que o comando (o='host "myhost" ') é inútil: atribui a saída de host à variável o em uma subshell; o valor não é disponibilizado fora da subcamada. Remova os parênteses. Além disso, host \"$host\" é estranho: você está colocando o valor da variável host entre aspas duplas; Não importa, pois os nomes de host não contêm caracteres especiais de shell, mas para usar o valor da variável ao avaliar o snippet de shell, você deve deixar host unexpanded aqui.

#!/bin/sh
OS=AIX # should probably be OS='uname -r'
host=myhost
CMD_AIX="o=\'host \"\$host\"\'"
eval "eval \"\$CMD_$OS\""

Se você tiver ksh93 ou bash ≥ 4.0, você poderá simplificar esse script usando matrizes associativas. Em vez de preencher o código para cada sistema operacional em uma variável diferente, coloque-os em uma matriz associativa .

#!/usr/bin/env bash
typeset -A commands
commands[AIX]="o=\'host \"\$host\"\'"
OS=AIX
host=myhost
if [ -n "${commands[$OS]}" ]; then
  eval "${commands[$OS]}"
else
  echo >&2 "Operating system $OS not supported"
  exit 2
fi
    
por 25.05.2015 / 23:41
2

Eu acho que o problema com o que você está fazendo lá é que não faz sentido algum. Sem ofensa, mas quero dizer que acho que você só tem a idéia errada sobre o que o eval faz. Parece que você está tentando usá-lo apenas para executar um comando - essa é a funcionalidade básica do shell e não exige que o shell tente interpretar suas últimas expansões como novos comandos - que é o que o eval faz.

Do jeito que você tem todas as suas citações escritas, você está protegendo qualquer coisa de ser avaliado de qualquer maneira. Ou melhor, você está protegendo o primeiro passo completamente - e assim nada acontece até a segunda passagem. O problema com tudo isso é que você pode simplesmente não fazer uma segunda avaliação (e uma segunda camada de aspas) .

Parece-me que você está tentando executar um determinado comando com base no valor de $OS - talvez ...? Bem, este é um dos recursos API mais acessíveis no shell .

Por exemplo:

OS=AIX
host=myhost
_cmd_AIX() { host "$host"; }
_cmd_WIN() { exit "$((LOSE=1))"; }

Você vê - você pode executar essas funções definidas citadas. Seu nome de comando não precisa de nenhum segundo intérprete especial para ter significado - nem mesmo quando ocorre como resultado de uma expansão. Só precisa estar na posição de comando. Eles ainda aceitarão parâmetros assim - então você poderia passar $host como $1 se quisesse. Você acabou de ligar assim:

"_cmd_$OS"

O $OS var expandirá para AIX e a palavra de comando resultante será _cmd_AIX - que é uma função de shell predefinida cujo nome ocorre como um todo e uma palavra de shell devidamente delimitada na posição de comando - e assim é executado. Bem desse jeito. Nenhuma contorção é necessária - e até vem com sua própria matriz.

Redefina $OS a qualquer momento para o nome de algum outro sufixo válido para outros comportamentos.

    
por 27.05.2015 / 22:05
1

com zsh :

$ echo $CMD
$CMD_AIX
$ echo ${(e)CMD}
(o='host "myhost" ')
$ echo ${(e)${(e)CMD}}
(o=myhost has address 1.2.3.4)

O sinalizador de expansão (e) serve para avaliar as expansões no valor da variável que está sendo expandida.

Com ksh93:

$ function CMD.get { eval ".sh.value=\"$_\""; }
$ function CMD_AIX.get { eval ".sh.value=\"$_\""; }
$ echo "$CMD_AIX"
(o=myhost has address 1.2.3.4)
$ echo "$CMD"
(o=myhost has address 1.2.3.4)
$ CMD=\$USER
$ echo "$CMD"
stephane

CMD.get é uma função de disciplina que é invocada a qualquer momento em que $CMD é expandida, normalmente usada para definir o que essa expansão deve ser para permitir variáveis com conteúdo dinâmico. Aqui nós o definimos como retornando a avaliação de seu conteúdo dentro de aspas duplas (que assume que o valor não contém aspas duplas).

Você também pode usar tipos:

typeset -T evaluating_variable=(
  function get { eval ".sh.value=\"$_\""; }
)

OS=AIX host=myhost
evaluating_variable CMD_AIX='(o='host "'$host'"')'
evaluating_variable CMD=\$CMD_$OS

Agora, por que você gostaria de fazer isso em um script de shell é outra questão.

    
por 27.05.2015 / 22:18
-2

eval eval $CMD na última linha corrige o problema

    
por 25.05.2015 / 18:44
-3

Isso resolveu o problema:

eval 'eval echo $CMD'

em vez de:

eval "$CMD"
    
por 25.05.2015 / 18:37