Qual é a melhor prática para retornar muitos valores de uma função bash?
Exemplo 1:
Script de função:
function mysqlquery {
local dbserver='localhost'
local dbuser='user'
local dbpass='pass'
local db='mydb'
mysql -h "$dbserver" -u "$dbuser" -p "$dbpass" --skip-column-names --raw -e "$*" "$db"
if [ $? -ne 0 ]; then
return 1
fi
}
Script-fonte:
for XY in $(mysqlquery "select XY from ABC where DEF = 123" 2>/dev/null);do
dosomethingwith $XY
done
if mysqlquery "select XY from ABC where DEF = 123" 2>/dev/null; then
echo true
fi
Exemplo2:
Script de função:
function mysqlquery {
local dbserver='localhost'
local dbuser='user'
local dbpass='pass'
local db='mydb'
result=$(mysql -h "$dbserver" -u "$dbuser" -p "$dbpass" -e "$*" "$db" 2>/dev/null)
if [ $? -ne 0 -o -z "$result" ]; then
return 1
fi
}
Script-fonte:
result=$(mysqlquery "select XY from ABC where DEF = 123" 2>/dev/null)
for XY in $result;do
dosomethingwith $XY
done
if mysqlquery "select XY from ABC where DEF = 123" 2>/dev/null; then
echo true
fi
Ou existem mais abordagens para retornar várias informações (muito mais do que um único valor int)?
Sim,
bash
'sreturn
só podem retornar números e inteiros entre 0 e 255.Para um shell que pode retornar qualquer coisa (listas de coisas), você pode olhar para
es
:Agora, em shells do tipo Korn como
bash
, você sempre pode retornar os dados em uma variável pré-definida. E essa variável pode estar em qualquer tipo suportado pelo shell.Para
bash
, isso pode ser escalar, arrays esparsos (arrays associativos com chaves restritas a números inteiros positivos) ou arrays associativos com chaves não vazias (nem a chave nem os valores podem conter caracteres NUL).Veja também
zsh
com arrays normais e arrays associativos sem essas restrições.O equivalente da
f
es
função acima pode ser feito com:Agora,
mysql
as consultas geralmente retornam tabelas, ou seja, arrays bidimensionais. O único shell que conheço que possui arrays multidimensionais éksh93
(comobash
se não suportasse caracteres NUL em suas variáveis).ksh
também suporta variáveis compostas que seriam úteis para retornar tabelas com seus cabeçalhos.Ele também suporta a passagem de variáveis por referência.
Então aí você pode fazer:
Ou:
Agora, para obter a saída
mysql
e armazená-la em algumas variáveis, precisamos analisar essa saída que é texto com colunas da tabela separadas por caracteres TAB e linhas separadas por NL e alguma codificação para os valores para permitir que contenham NL e TAB.Sem
--raw
,mysql
produziria um NL como\n
, um TAB como\t
, uma barra invertida como\\
e um NUL como\0
.ksh93
também temread -C
que ler texto formatado como uma definição de variável (não muito diferente de usareval
), então você pode fazer:Para ser usado como
Ou para uma variável composta:
Para ser usado como:
Observe que os nomes dos cabeçalhos (
firstname
,lastname
acima) devem ser identificadores de shell válidos.Em
bash
ouzsh
ouyash
(apesar de que os índices de array começam em 1 em zsh e yash e sózsh
podem armazenar caracteres NUL), você sempre pode retornar um array por coluna,awk
gerando o código para defini-los:Para ser usado como:
Adicione um
set -o localoptions
comzsh
oulocal -
com bash4.4+ antes deset -o pipefail
para que a configuração dessa opção seja local para a função, como naksh93
abordagem.Observe que, em tudo o que foi dito acima, não estamos convertendo de volta os
\0
s em NULs reaisbash
ouksh93
os engasgaríamos. Você pode querer fazer isso se estiver usandozsh
para poder trabalhar com BLOBs, mas observe quegsub(/\\0/, "\0", s)
não funcionaria com todas asawk
implementações.De qualquer forma, aqui, eu usaria linguagens mais avançadas do que um shell como perl ou python para fazer esse tipo de coisa.
Bem, depende de que tipo de formato de saída você deseja/precisa. A maneira mais fácil é provavelmente apenas imprimir a saída da função, dessa forma a função se comporta como qualquer outro comando. Outra maneira seria definir alguma variável (possivelmente um array associativo) de dentro da função. Isso tem a vantagem de que diferentes itens são separados de forma limpa, mas pode ser necessário codificar algumas variáveis.
A função em seu primeiro exemplo implementa o primeiro: tudo o que o cliente mysql imprime da função vai para a saída padrão da função. Dado que os dados já vêm como um fluxo de bytes, é bom mantê-los como tal.
Embora aqui, a questão seja o que fazer com a saída.
for x in $(somecmd) ...
não é bom, pois a saída desomecmd
é dividida em palavras e processada para globs de nome de arquivo. Geralmente é melhor usarwhile read ...
, consulte Como posso ler um arquivo (fluxo de dados, variável) linha por linha (e/ou campo por campo)?Para ler a saída
mysql
linha por linha, você poderia fazerou com função
Observe que você não precisa de
if [ $? -ne 0 ]; then return 1
: o valor de retorno da função é o mesmo do último comando. Não que seja fácil olhar para o valor de retorno se você estiver usando a função para alimentar um tubo.