Listar variáveis do shell com um prefixo fixo

3

Eu procurei isto e não consegui encontrar uma resposta, peço desculpas antecipadamente se tiver sido solicitado anteriormente.

Estou usando o shell no FreeBSD ( /bin/sh ) e quero despejar para stdout todas as variáveis shell ( não ambiente) começando com _myvar_ . O mais próximo que posso obter é set | grep '^_myvar_' , mas isso apenas elimina a primeira linha de variáveis multilinhas (algumas serão multilinhas e eu preciso delas na íntegra), e pode ser propenso a erros em casos de borda patológica, como uma cadeia contendo " myvar "que acontece com quebra de linha logo antes dessa parte.

se eu pudesse listar apenas nomes de variáveis (não valores), eu poderia filtrar dentro de um do...read...while e obter os valores um de cada vez apenas para vars com nomes correspondentes, mas não consigo encontrar uma maneira de fazer isso . Eu também não posso filtrar a saída completa, porque não há nenhuma maneira determinista de saber se uma linha de saída contém uma continuação ou uma nova variável, que não tem problemas de borda com strings contendo _myvar_ , = ou newline (\n) caracteres, ou possivelmente, espaços à direita. Eu não quero modificar o ambiente, porque o código está incluído em outro código e o ambiente tem que ser estável para ele.

Não é um problema para a saída / lista incluir quaisquer variáveis de ambiente correspondentes, se isso ajudar (se houver alguma - é extremamente improvável que eles o façam)

Existe uma maneira de fazer isso?

    
por Stilez 13.08.2018 / 13:33

2 respostas

3

Como o set do FreeBSD sh é gerado em um formato que é adequado para a reinicialização do shell, você pode fazer:

out() case $1 in (_myvar_*) printf '%s\n' "${1%%=*}"; esac
eval "$(set | sed 's/^/out /')"

Isso é o prefixo de cada linha da saída de set com "out " e tem isso avaliado como código de shell (onde out é uma função que imprime a subseqüência de seu primeiro argumento até o primeiro = ) .

sed também inseriria "out " no conteúdo da variável multilinha, mas isso ainda seria incluído no argumento de que nossa função out recebe e passa do primeiro = , portanto, na parte em que estamos não exibindo.

Por exemplo, em uma saída set como:

TERM=xterm
USER=stephane
_myvar_foo='line1
line2
line3'

Estaríamos avaliando:

out TERM=xterm
out USER=stephane
out _myvar_foo='line1
out line2
out line3'

Mas ainda assim, como out é chamado apenas 3 vezes para essas 3 variáveis.

Para imprimir o nome e o valor da variável:

out() case $1 in (_myvar_*)
  eval 'printf "name: \"%s\" value: \"%s\"\n" "${1%%=*}" "${'"${1%%=*}"'}"'
esac
eval "$(set | sed 's/^/out /')"

Note que só gera variáveis , e não outros tipos de parâmetros como $- , parâmetros posicionais ...

Essa abordagem funciona apenas para implementações sh , em que set gera apenas variáveis escalar (não funciona para matrizes ou matrizes associativas ou variáveis compostas, em que out var=(x) se torna um erro de sintaxe) . Aquelas conchas que possuem outros tipos de variáveis geralmente possuem também melhores recursos de introspecção.

Em zsh :

typeset -pm '_myvar_*'

ou apenas para os nomes

echo $parameters[(I)_myvar_*]

Em bash :

v=("${!_myvar_@}"); ((${#v[@]})) && typeset -p -- "${v[@]}"
echo "${!_myvar_@}"
    
por 13.08.2018 / 22:27
1

Acabei com a seguinte simplificação da solução @ StéphaneChazelas, porque não precisei de mais. Postando abaixo caso isso ajude alguém. Mas Stéphane encontrou a resposta, esta é apenas uma versão modificada, nada mais.

#!/bin/sh

get_vars() {
  if printf '%s' "$1" | grep -qE '^_myvar_'; then
    printf '%s\n' "$1" | sed 's/^get_vars //'
  fi
}

eval "$( set | sed 's/^/get_vars /' )"

A chamada eval prefixa cada linha, mas devido à maneira como set exibe seus valores, ela realmente cria um único comando get_vars VARNAME=VALUE para cada variável. Avaliar isso executa os comandos. Em seguida, cada vez que é chamado, get_vars verifica se a variável capturada na linha atual corresponde à regex necessária e, em caso positivo, remove o prefixo "get_vars " de qualquer segunda linha e outras linhas e exibe o resultado.

Mas, para reiterar, tudo isso é trabalho de Stéphane. É um truque inteligente! Espero que esta simplificação ajude alguém no futuro a procurar por isso.

    
por 14.08.2018 / 11:25