linux - variable - Looping através do conteúdo de um arquivo no Bash




bash script for i in file (8)

Como faço para percorrer cada linha de um arquivo de texto com o Bash ?

Com este script:

echo "Start!"
for p in (peptides.txt)
do
    echo "${p}"
done

Eu recebo essa saída na tela:

Start!
./runPep.sh: line 3: syntax error near unexpected token `('
./runPep.sh: line 3: `for p in (peptides.txt)'

(Mais tarde, quero fazer algo mais complicado com o $p que apenas a saída para a tela.)

A variável de ambiente SHELL é (de env):

SHELL=/bin/bash

/bin/bash --version saída de /bin/bash --version :

GNU bash, version 3.1.17(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.

saída cat /proc/version :

Linux version 2.6.18.2-34-default ([email protected]) (gcc version 4.1.2 20061115 (prerelease) (SUSE Linux)) #1 SMP Mon Nov 27 11:46:27 UTC 2006

O arquivo peptides.txt contém:

RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL

@ Peter: Isso poderia funcionar para você-

echo "Start!";for p in $(cat ./pep); do
echo $p
done

Isso retornaria a saída

Start!
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL

Aqui está o meu exemplo da vida real como fazer um loop de linhas de saída de outro programa, verificar substrings, colocar aspas duplas da variável, usar essa variável fora do loop. Eu acho que muitos estão fazendo essas perguntas mais cedo ou mais tarde.

##Parse FPS from first video stream, drop quotes from fps variable
## streams.stream.0.codec_type="video"
## streams.stream.0.r_frame_rate="24000/1001"
## streams.stream.0.avg_frame_rate="24000/1001"
FPS=unknown
while read -r line; do
  if [[ $FPS == "unknown" ]] && [[ $line == *".codec_type=\"video\""* ]]; then
    echo ParseFPS $line
    FPS=parse
  fi
  if [[ $FPS == "parse" ]] && [[ $line == *".r_frame_rate="* ]]; then
    echo ParseFPS $line
    FPS=${line##*=}
    FPS="${FPS%\"}"
    FPS="${FPS#\"}"
  fi
done <<< "$(ffprobe -v quiet -print_format flat -show_format -show_streams -i "$input")"
if [ "$FPS" == "unknown" ] || [ "$FPS" == "parse" ]; then 
  echo ParseFPS Unknown frame rate
fi
echo Found $FPS

Declare a variável fora do loop, defina o valor e use-a fora do loop requer a sintaxe done <<< "$ (...)" . A aplicação precisa ser executada dentro de um contexto do console atual. As cotações em torno do comando mantêm novas linhas do fluxo de saída.

Correspondência de loop para substrings, em seguida, lê o par name = value , divide a parte do lado direito do último caractere = , desce a primeira cotação, desce a última cotação, temos um valor limpo para ser usado em outro lugar.


Mais algumas coisas não cobertas por outras respostas:

Lendo de um arquivo delimitado

# ':' is the delimiter here, and there are three fields on each line in the file
# IFS set below is restricted to the context of `read`, it doesn't affect any other code
while IFS=: read -r field1 field2 field3; do
  # process the fields
  # if the line has less than three fields, the missing fields will be set to an empty string
  # if the line has more than three fields, `field3` will get all the values, including the third field plus the delimiter(s)
done < input.txt

Leitura da saída de outro comando, usando substituição de processo

while read -r line; do
  # process the line
done < <(command ...)

Essa abordagem é melhor que o command ... | while read -r line; do ... command ... | while read -r line; do ... command ... | while read -r line; do ... porque o laço while aqui é executado no shell atual em vez de um subshell como no caso do último. Veja o post relacionado Uma variável modificada dentro de um loop while não é lembrada .

Lendo de uma entrada delimitada nula, por exemplo, find ... -print0

while read -r -d '' line; do
  # logic
  # use a second 'read ... <<< "$line"' if we need to tokenize the line
done < <(find /path/to/dir -print0)

Related read: BashFAQ / 020 - Como posso encontrar e manipular com segurança nomes de arquivos contendo novas linhas, espaços ou ambos?

Lendo de mais de um arquivo de cada vez

while read -u 3 -r line1 && read -u 4 -r line2; do
  # process the lines
  # note that the loop will end when we reach EOF on either of the files, because of the `&&`
done 3< input1.txt 4< input2.txt

Baseado na resposta de @chepner's here :

-u é uma extensão bash. Para compatibilidade POSIX, cada chamada seria algo como read -r X <&3 .

Lendo um arquivo inteiro em um array (versões Bash anteriores a 4)

while read -r line; do
    my_array+=("$line")
done < my_file

Se o arquivo terminar com uma linha incompleta (nova linha ausente no final), então:

while read -r line || [[ $line ]]; do
    my_array+=("$line")
done < my_file

Lendo um arquivo inteiro em um array (versões Bash 4x e posteriores)

readarray -t my_array < my_file

ou

mapfile -t my_array < my_file

E depois

for line in "${my_array[@]}"; do
  # process the lines
done

Posts relacionados:

  • Criando uma matriz a partir de um arquivo de texto no Bash
  • Qual é a diferença entre as abordagens para ler um arquivo que tem apenas uma linha?
  • Bash enquanto lê loop extremamente lento comparado ao gato, por quê?

Se você não quer que sua leitura seja quebrada por um caractere de nova linha, use -

#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
    echo "$line"
done < "$1"

Em seguida, execute o script com o nome do arquivo como parâmetro.


Uma maneira de fazer isso é:

while read p; do
  echo "$p"
done <peptides.txt

Como apontado nos comentários, isso tem os efeitos colaterais de aparar os espaços em branco iniciais, interpretar seqüências de contrabarra e pular a linha à direita se estiver faltando um avanço de linha final. Se estas são preocupações, você pode fazer:

while IFS="" read -r p || [ -n "$p" ]
do
  printf '%s\n' "$p"
done < peptides.txt

Excepcionalmente, se o corpo do loop puder ler a partir da entrada padrão , você poderá abrir o arquivo usando um descritor de arquivo diferente:

while read -u 10 p; do
  ...
done 10<peptides.txt

Aqui, 10 é apenas um número arbitrário (diferente de 0, 1, 2).



#!/bin/bash
#
# Change the file name from "test" to desired input file 
# (The comments in bash are prefixed with #'s)
for x in $(cat test.txt)
do
    echo $x
done

cat peptides.txt | while read line
do
   # do something with $line here
done






io