Eu tenho uma variável que contém saída multilinha de um comando. Qual é a maneira mais eficiente de ler a linha de saída por linha da variável?
Por exemplo:
jobs="$(jobs)"
if [ "$jobs" ]; then
# read lines from $jobs
fi
Você pode usar um loop while com substituição de processo:
while read -r line
do
echo "$line"
done < <(jobs)
Uma maneira ideal de ler uma variável de múltiplas linhas é definir uma IFS
variável em branco e printf
a variável com uma nova linha à direita:
# Printf '%s\n' "$var" is necessary because printf '%s' "$var" on a
# variable that doesn't end with a newline then the while loop will
# completely miss the last line of the variable.
while IFS= read -r line
do
echo "$line"
done < <(printf '%s\n' "$var")
Nota: Conforme o shellcheck sc2031 , o uso da subestação de processo é preferível a um tubo para evitar [sutilmente] criar um subshell.
Além disso, saiba que, ao nomear a variável, jobs
isso pode causar confusão, pois esse também é o nome de um comando shell comum.
while IFS= read
.... Se você quer evitar \ interpretação, então useread -r
echo
paraprintf %s
, para que seu script funcionasse mesmo com entradas não domesticadas./tmp
diretório seja gravável, pois depende de ser capaz de criar um arquivo de trabalho temporário. Se você se encontrar em um sistema restrito com/tmp
apenas leitura (e não pode ser alterado por você), ficará feliz com a possibilidade de usar uma solução alternativa, por exemplo, com oprintf
cano.printf "%s\n" "$var" | while IFS= read -r line
Para processar a saída de uma linha de comando por linha ( explicação ):
Se você já possui os dados em uma variável:
printf %s "$foo"
é quase idêntico aecho "$foo"
, mas é impresso$foo
literalmente, enquanto queecho "$foo"
pode ser interpretado$foo
como uma opção para o comando echo se começar com a-
e pode expandir as seqüências de barra invertida$foo
em alguns shells.Observe que, em alguns shells (ash, bash, pdksh, mas não ksh ou zsh), o lado direito de um pipeline é executado em um processo separado; portanto, qualquer variável que você definir no loop é perdida. Por exemplo, o seguinte script de contagem de linhas imprime 0 nesses shells:
Uma solução alternativa é colocar o restante do script (ou pelo menos a parte que precisa do valor do
$n
loop) em uma lista de comandos:Se agir nas linhas não vazias for bom o suficiente e a entrada não for grande, você poderá usar a divisão de palavras:
Explicação: a configuração
IFS
de uma única nova linha faz com que a divisão de palavras ocorra somente em novas linhas (em oposição a qualquer caractere de espaço em branco na configuração padrão).set -f
desativa o globbing (ou seja, expansão de curinga), que de outra forma ocorreria com o resultado de uma substituição de comando$(jobs)
ou de uma variável substitutino$foo
. Ofor
loop atua em todas as partes de$(jobs)
, que são todas as linhas não vazias na saída do comando. Por fim, restaure as globbing e asIFS
configurações.fonte
local IFS=something
. Não afetará o valor do escopo global. IIRC,unset IFS
você não volta ao padrão (e certamente não funciona se não fosse o padrão anteriormente).Problema: se você usar o loop while, ele será executado em subshell e todas as variáveis serão perdidas. Solução: use para loop
fonte
while read
loop no bash significa que o loop while está em um subshell, portanto, as variáveis não são globais.while read;do ;done <<< "$var"
torna o corpo do loop não um subconjunto. (Bash recentes tem a opção de colocar o corpo de umcmd | while
loop não em um subnível, como ksh sempre teve.)Nas versões recentes do bash, use
mapfile
oureadarray
para ler com eficiência a saída do comando em matrizesIsenção de responsabilidade: exemplo horrível, mas você pode criar um comando melhor do que você mesmo
fonte
readarray
uma função e chame a função algumas vezes.Referências:
while
IFS
read
fonte
-r
é uma boa ideia também; Ela impede que\` interpretation... (it is in your links, but its probably worth mentioning, just to round out your
IFS = `(que é essencial para evitar a perda de espaço em branco)Você pode usar <<< para simplesmente ler a variável que contém os dados separados por nova linha:
fonte