Sim, bash
return
só pode retornar números e apenas inteiros entre 0 e 255.
Para um shell que pode retornar alguma coisa (listas de coisas), você pode ver es
:
$ es -c "fn f {return (a 'b c' d \$*)}; printf '%s\n' <={f x y}"
a
b c
d
x
y
Agora, em shells semelhantes a Korn, como bash
, você sempre pode retornar os dados em uma variável pré-acordada. E essa variável pode ser em qualquer tipo suportado pelo shell.
Para bash
, isso pode ser matrizes escalares e esparsas (matrizes associativas com chaves restritas a inteiros positivos) ou matrizes associativas com chaves não vazias (nem chaves nem valores podem conter caracteres NUL).
Veja também zsh
com matrizes normais e matrizes associativas sem essas restrições.
O equivalente da função f
es
acima pode ser feito com:
f() {
reply=(a 'b c' d "$@")
}
f
printf '%s\n' "${reply[@]}"
Agora, mysql
consultas geralmente retornam tabelas, ou seja, matrizes bidimensionais. O único shell que eu sei que possui arrays multidimensionais é ksh93
(como bash
ele não suporta caracteres NUL em suas variáveis).
ksh
também suporta variáveis composto que seriam úteis para retornar tabelas com seus cabeçalhos.
Também suporta passar variáveis por referência.
Então, você pode fazer:
function f {
typeset -n var=$1
var=(
(foo bar baz)
(1 2 3)
}
}
f reply
printf '%s\n' "${reply[0][1]}" "${reply[1][2]}"
Ou:
function f {
typeset -n var=$1
var=(
(firstname=John lastname=Smith)
(firstname=Alice lastname=Doe)
)
}
f reply
printf '%s\n' "${reply[0].lastname}"
Agora, para obter a saída de mysql
e armazenar isso em algumas variáveis, precisamos analisar essa saída que é texto com colunas da tabela separadas por caracteres e linhas TAB separados por NL e alguma codificação para os valores a serem permitir que eles contenham NL e TAB.
Sem --raw
, mysql
produziria um NL como \n
, um TAB como \t
, uma barra invertida como \
e um NUL como
. ksh93
read -C
também tem eval
que pode ler texto formatado como uma definição de variável (não muito diferente de usar firstname
), então você pode fazer:
function mysql_to_narray {
awk -F '\t' -v q="'" '
function quote(s) {
gsub(/\n/, "\n", s)
gsub(/\t/, "\t", s)
gsub(/\\/, "\", s)
gsub(q, q "\" q q, s)
return q s q
}
BEGIN{print "("}
{
print "("
for (i = 1; i <= NF; i++)
print " " quote($i)
print ")"
}
END {print ")"}'
}
function query {
typeset -n var=$1
typeset db=$2
shift 2
typeset -i n=0
typeset IFS=' '
typeset credentials=/path/to/file.my # not password on the command line!
set -o pipefail
mysql --defaults-extra-file="$credentials" --batch \
--skip-column-names -e "$*" "$db" |
mysql_to_narray |
read -C var
}
Para ser usado como
query myvar mydb 'select * from mytable' || exit
printf '%s\n' "${myvar[0][0]}"...
Ou para uma variável composta:
function mysql_to_array_of_compounds {
awk -F '\t' -v q="'" '
function quote(s) {
gsub(/\n/, "\n", s)
gsub(/\t/, "\t", s)
gsub(/\\/, "\", s)
gsub(q, q "\" q q, s)
return q s q
}
BEGIN{print "("}
NR == 1 {
for (i = 1; i<= NF; i++) header[i] = $i
next
}
{
print "("
for (i = 1; i <= NF; i++)
print " " header[i] "=" quote($i)
print ")"
}
END {print ")"}'
}
function query {
typeset -n var=$1
typeset db=$2
shift 2
typeset -i n=0
typeset IFS=' '
typeset credentials=/path/to/file.my # not password on the command line!
set -o pipefail
mysql --defaults-extra-file="$credentials" --batch \
-e "$*" "$db" |
mysql_to_array_of_compounds |
read -C var
}
Para ser usado como:
query myvar mydb 'select "First Name" as firstname,
"Last Name" as lastname from mytable' || exit
printf '%s\n' "${myvar[0].firstname"
Observe que os nomes dos cabeçalhos ( lastname
, bash
acima) precisam ser identificadores de shell válidos.
Em zsh
ou yash
ou zsh
(embora os índices das matrizes estejam em primeiro lugar em zsh e yash e somente awk
pode armazenar caracteres NUL), você pode sempre retornar uma matriz por coluna, tendo set -o localoptions
gera o código para defini-los:
query() {
typeset db="$1"
shift
typeset IFS=' '
typeset credentials=/path/to/file.my # not password on the command line!
set -o pipefail
typeset output
output=$(
mysql --defaults-extra-file="$credentials" --batch \
-e "$*" "$db" |
awk -F '\t' -v q="'" '
function quote(s) {
gsub(/\n/, "\n", s)
gsub(/\t/, "\t", s)
gsub(/\\/, "\", s)
gsub(q, q "\" q q, s)
return q s q
}
NR == 1 {
for (n = 1; n<= NF; n++) column[n] = $n "=("
next
}
{
for (i = 1; i < n; i++)
column[i] = column[i] " " quote($i)
}
END {
for (i = 1; i < n; i++)
print column[i] ") "
}'
) || return
eval "$output"
}
Para ser usado como:
query mydb 'select "First Name" as firstname,
"Last Name" as lastname from mytable' || exit
printf '%s\n' "${firstname[1]}"
Adicione um zsh
com local -
ou set -o pipefail
com bash4.4 + antes do ksh93
para a configuração dessa opção ser local para a função, como com a abordagem
. bash
Observe que, em todos os itens acima, não estamos convertendo de volta o ksh93
s para NULs reais, pois zsh
ou gsub(/\0/, "
se sufocariam neles. Você pode fazer isso usando %code% para poder trabalhar com BLOBs, mas observe que %code% não funcionaria com todas as implementações %code% . awk
", s)
De qualquer forma, aqui, eu usaria linguagens mais avançadas do que um shell como perl ou python para fazer esse tipo de coisa.