AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 408543
Accepted
FaxMax
FaxMax
Asked: 2017-12-03 16:12:16 +0800 CST2017-12-03 16:12:16 +0800 CST 2017-12-03 16:12:16 +0800 CST

Como uma função bash pode retornar vários valores?

  • 772

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)?

bash shell-script
  • 2 2 respostas
  • 38851 Views

2 respostas

  • Voted
  1. Best Answer
    Stéphane Chazelas
    2017-12-04T10:27:48+08:002017-12-04T10:27:48+08:00

    Sim, bash's returnsó 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:

    $ es -c "fn f {return (a 'b c' d \$*)}; printf '%s\n' <={f x y}"
    a
    b c
    d
    x
    y
    

    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 zshcom arrays normais e arrays associativos sem essas restrições.

    O equivalente da f esfunção acima pode ser feito com:

    f() {
      reply=(a 'b c' d "$@")
    }
    f
    printf '%s\n' "${reply[@]}"
    

    Agora, mysqlas consultas geralmente retornam tabelas, ou seja, arrays bidimensionais. O único shell que conheço que possui arrays multidimensionais é ksh93(como bashse não suportasse caracteres NUL em suas variáveis).

    kshtambé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:

    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 mysqle 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, mysqlproduziria um NL como \n, um TAB como \t, uma barra invertida como \\e um NUL como \0.

    ksh93também tem read -Cque ler texto formatado como uma definição de variável (não muito diferente de usar eval), 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 ( firstname, lastnameacima) devem ser identificadores de shell válidos.

    Em bashou zshou yash (apesar de que os índices de array começam em 1 em zsh e yash e só zshpodem armazenar caracteres NUL), você sempre pode retornar um array por coluna, awkgerando 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 set -o localoptionscom zshou local -com bash4.4+ antes de set -o pipefailpara que a configuração dessa opção seja local para a função, como na ksh93abordagem.

    Observe que, em tudo o que foi dito acima, não estamos convertendo de volta os \0s em NULs reais bashou ksh93os engasgaríamos. Você pode querer fazer isso se estiver usando zshpara poder trabalhar com BLOBs, mas observe que gsub(/\\0/, "\0", s)não funcionaria com todas as awkimplementaçõ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.

    • 13
  2. ilkkachu
    2017-12-04T08:18:13+08:002017-12-04T08:18:13+08:00

    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 de somecmdé dividida em palavras e processada para globs de nome de arquivo. Geralmente é melhor usar while 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 mysqllinha por linha, você poderia fazer

    mysql -h "$dbserver" etc. etc. | while read -r line ; do
        dosomethingwith "$line"
    done
    

    ou com função

    mysqlquery() {
        ...
        mysql -h "$dbserver" etc. etc. 2>/dev/null
    }
    mysqlquery | while read -r line ; do ...
    

    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.

    • 1

relate perguntas

  • Problema estranho ao passar variáveis ​​do arquivo de texto

  • Enquanto a linha lê mantendo os espaços de escape?

  • MySQL Select com função IN () com array bash

  • ordem de substituição de processos `te` e `bash`

  • Execute um script muito lento até que seja bem-sucedido

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Matriz JSON para bash variáveis ​​usando jq

    • 4 respostas
  • Marko Smith

    A data pode formatar a hora atual para o fuso horário GMT? [duplicado]

    • 2 respostas
  • Marko Smith

    bash + lê variáveis ​​e valores do arquivo pelo script bash

    • 4 respostas
  • Marko Smith

    Como posso copiar um diretório e renomeá-lo no mesmo comando?

    • 4 respostas
  • Marko Smith

    conexão ssh. Conexão X11 rejeitada devido a autenticação incorreta

    • 3 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Marko Smith

    comando systemctl não funciona no RHEL 6

    • 3 respostas
  • Marko Smith

    rsync porta 22 e 873 uso

    • 2 respostas
  • Marko Smith

    snap /dev/loop em 100% de utilização -- sem espaço livre

    • 1 respostas
  • Marko Smith

    chave de impressão jq e valor para todos no subobjeto

    • 2 respostas
  • Martin Hope
    EHerman Matriz JSON para bash variáveis ​​usando jq 2017-12-31 14:50:58 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Drux A data pode formatar a hora atual para o fuso horário GMT? [duplicado] 2017-12-26 11:35:07 +0800 CST
  • Martin Hope
    AllisonC Como posso copiar um diretório e renomeá-lo no mesmo comando? 2017-12-22 05:28:06 +0800 CST
  • Martin Hope
    Steve Como as permissões de arquivo funcionam para o usuário "root"? 2017-12-22 02:46:01 +0800 CST
  • Martin Hope
    Bagas Sanjaya Por que o Linux usa LF como caractere de nova linha? 2017-12-20 05:48:21 +0800 CST
  • Martin Hope
    Cbhihe Altere o editor padrão para vim para _ sudo systemctl edit [unit-file] _ 2017-12-03 10:11:38 +0800 CST
  • Martin Hope
    showkey Como baixar o pacote não instalá-lo com o comando apt-get? 2017-12-03 02:15:02 +0800 CST
  • Martin Hope
    youxiao Por que os diretórios /home, /usr, /var, etc. têm o mesmo número de inode (2)? 2017-12-02 05:33:41 +0800 CST
  • Martin Hope
    user223600 gpg — o comando list-keys gera uid [ desconhecido ] depois de importar a chave privada para uma instalação limpa 2017-11-26 18:26:02 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve