Preciso ler a saída de um comando em meu script em uma matriz. O comando é, por exemplo:
ps aux | grep | grep | x
e dá a saída linha por linha assim:
10
20
30
Preciso ler os valores da saída do comando em uma matriz e, em seguida, farei algum trabalho se o tamanho da matriz for menor que três.
Respostas:
As outras respostas vai quebrar se a saída de comando contém espaços (que é bastante frequente) ou glob personagens como
*
,?
,[...]
.Para obter a saída de um comando em uma matriz, com uma linha por elemento, existem essencialmente três maneiras:
Com Bash≥4 uso
mapfile
- é o mais eficiente:Caso contrário, um loop lendo a saída (mais lento, mas seguro):
Conforme sugerido por Charles Duffy nos comentários (obrigado!), O seguinte pode funcionar melhor do que o método de loop no número 2:
Certifique-se de usar exatamente este formulário, ou seja, certifique-se de ter o seguinte:
IFS=$'\n'
na mesma linha daread
instrução: isso só definirá a variável de ambiente apenasIFS
para aread
instrução. Portanto, isso não afetará o resto do seu script. O objetivo desta variável é informarread
para interromper o fluxo no caractere EOL\n
.-r
: Isso é importante. Dizread
para não interpretar as barras invertidas como sequências de escape.-d ''
: observe o espaço entre a-d
opção e seu argumento''
. Se você não deixar um espaço aqui, o''
nunca será visto, pois desaparecerá na etapa de remoção da citação quando o Bash analisar a instrução. Isso indicaread
para parar de ler no byte nulo. Algumas pessoas escrevem como-d $'\0'
, mas não é realmente necessário.-d ''
é melhor.-a my_array
dizread
para preencher a matrizmy_array
enquanto lê o fluxo.printf '\0'
instrução depoismy_command
, para queread
retorne0
; na verdade, não é grande coisa se você não fizer isso (você apenas obterá um código de retorno1
, o que está certo se você não usarset -e
- o que você não deveria usar de qualquer maneira), mas apenas tenha isso em mente. É mais limpo e semanticamente correto. Observe que isso é diferente deprintf ''
, que não produz nada.printf '\0'
imprime um byte nulo, necessárioread
para parar de ler ali (lembra da-d ''
opção?).Se você puder, ou seja, se você tiver certeza de que seu código será executado em Bash≥4, use o primeiro método. E você pode ver que é mais curto também.
Se você quiser usar
read
, o loop (método 2) pode ter uma vantagem sobre o método 3 se você quiser fazer algum processamento à medida que as linhas são lidas: você tem acesso direto a ele (por meio da$line
variável no exemplo que dei), e você também tem acesso às linhas já lidas (por meio da matriz${my_array[@]}
no exemplo que dei).Observe que
mapfile
fornece uma maneira de ter um retorno de chamada avaliado em cada linha lida e, na verdade, você pode até mesmo dizer para ele apenas chamar esse retorno de chamada a cada N linhas lidas; dê uma olhada nashelp mapfile
opções-C
e-c
nele contidas. (Minha opinião sobre isso é que é um pouco desajeitado, mas pode ser usado às vezes se você tiver apenas coisas simples para fazer - eu realmente não entendo por que isso foi implementado em primeiro lugar!).Agora vou dizer por que o seguinte método:
está quebrado quando há espaços:
Então, algumas pessoas irão recomendar o uso
IFS=$'\n'
para consertá-lo:Mas agora vamos usar outro comando, com globs :
Isso é porque eu tenho um arquivo chamado
t
no diretório atual ... e esse nome de arquivo é correspondido pelo glob[three four]
... neste ponto, algumas pessoas recomendariam usarset -f
para desabilitar o globbing: mas olhe só: você tem que alterarIFS
e usarset -f
para corrigir um técnica quebrada (e você nem mesmo está consertando)! ao fazer isso, estamos realmente lutando contra o shell, não trabalhando com o shell .aqui estamos trabalhando com o shell!
fonte
mapfile
antes, é exatamente o que eu estava perdendo há anos. Eu acho que as versões recentes dobash
tem tantos novos recursos interessantes, eu deveria apenas passar alguns dias lendo os documentos e escrevendo um bom cheatsheet.< <(command)
em scripts de shell, a linha shebang deve ser#!/bin/bash
- se executado como#!/bin/sh
, o bash sairá com um erro de sintaxe.bash my_script.sh
e não com o comando shsh my_script.sh
sh
edash
não sabem nada sobre arrays, exceto, é claro, para a$@
matriz de parâmetros posicionais ).IFS=$'\n' read -r -d '' -a my_array < <(my_command && printf '\0')
- ele funciona corretamente no bash 3.x, e também passa por um status de saída com falha demy_command
para oread
.Você pode usar
para armazenar a saída do comando
<command>
na matrizmy_array
.Você pode acessar o comprimento dessa matriz usando
Agora o comprimento está armazenado em
my_array_length
.fonte
VAR="$(<command>)"
e entãomy_array=("$VAR")
oumy_array+=("$VAR")
Imagine que você vai colocar os arquivos e nomes de diretório (na pasta atual) em um array e contar seus itens. O script seria assim;
Ou você pode iterar sobre essa matriz adicionando o seguinte script:
Observe que este é o conceito central e a entrada é considerada como sanitizada antes, ou seja, remover caracteres extras, manusear Strings vazias e etc. (o que está fora do tópico deste tópico).
fonte