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 esta 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 do $p
que apenas exibir na tela.)
A variável de ambiente SHELL é (de env):
SHELL=/bin/bash
/bin/bash --version
resultado:
GNU bash, version 3.1.17(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.
cat /proc/version
resultado:
Linux version 2.6.18.2-34-default (geeko@buildhost) (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
Respostas:
Uma maneira de fazer isso é:
Conforme apontado nos comentários, isso tem os efeitos colaterais de aparar espaços em branco à esquerda, interpretar seqüências de barra invertida e pular a última linha se estiver faltando um feed de linha final. Se estas são preocupações, você pode fazer:
Excepcionalmente, se o corpo do loop puder ler da entrada padrão , você poderá abrir o arquivo usando um descritor de arquivo diferente:
Aqui, 10 é apenas um número arbitrário (diferente de 0, 1, 2).
fonte
while read p || [[ -n $p ]]; do ...
e a variante de uma linha:
Essas opções ignoram a última linha do arquivo se não houver avanço de linha à direita.
Você pode evitar isso da seguinte maneira:
fonte
Opção 1a: loop While: linha única de cada vez: redirecionamento de entrada
Opção 1b: Loop while: Linha única de cada vez:
Abra o arquivo, leia a partir de um descritor de arquivo (neste caso, o descritor de arquivo nº 4).
fonte
done < $filename
pordone 4<$filename
(o que é útil se você quiser ler o nome do arquivo a partir de um parâmetro de comando, nesse caso, basta substituir$filename
por$1
)tail -n +2 myfile.txt | grep 'somepattern' | cut -f3
, durante a execução de comandos ssh dentro do loop (consome stdin); opção 2 aqui parece ser o único caminho?Isso não é melhor do que outras respostas, mas é mais uma maneira de realizar o trabalho em um arquivo sem espaços (consulte os comentários). Acho que geralmente preciso de uma linha para vasculhar listas em arquivos de texto sem a etapa extra de usar arquivos de script separados.
Esse formato me permite colocar tudo em uma linha de comando. Altere a parte "echo $ word" para o que você quiser e poderá emitir vários comandos separados por ponto e vírgula. O exemplo a seguir usa o conteúdo do arquivo como argumentos em outros dois scripts que você pode ter escrito.
Ou, se você pretende usá-lo como um editor de fluxo (learn sed), pode despejar a saída em outro arquivo da seguinte maneira.
Eu usei isso como escrito acima, porque usei arquivos de texto onde os criei com uma palavra por linha. (Ver comentários) Se você possui espaços que não deseja dividir suas palavras / linhas, fica um pouco mais feio, mas o mesmo comando ainda funciona da seguinte maneira:
Isso apenas diz ao shell para dividir apenas em novas linhas, não em espaços, depois retorna o ambiente ao que era anteriormente. Neste ponto, você pode considerar colocar tudo isso em um script shell, em vez de espremer tudo em uma única linha.
Boa sorte!
fonte
for
torna os tokens / linhas de entrada sujeitos a expansões de shell, o que geralmente é indesejável; tente o seguinte:for l in $(echo '* b c'); do echo "[$l]"; done
+ como você verá, o*
+ mesmo que originalmente um literal citado + se expanda para os arquivos no diretório atual.for
para iterar linhas de arquivo é uma má idéia. Além disso, o aspecto de expansão mencionado por @ mklement0 (embora isso provavelmente possa ser contornado com a inclusão de aspas escapadas, o que novamente torna as coisas mais complexas e menos legíveis).Mais algumas coisas não cobertas por outras respostas:
Lendo de um arquivo delimitado
Leitura da saída de outro comando, usando substituição de processo
Essa abordagem é melhor do que
command ... | while read -r line; do ...
porque o loop while é 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
Leitura relacionada: BashFAQ / 020 - Como posso encontrar e lidar com segurança com nomes de arquivos que contenham novas linhas, espaços ou ambos?
Lendo mais de um arquivo por vez
Com base na resposta de @ chepner aqui :
-u
é uma extensão do bash. Para compatibilidade com POSIX, cada chamada seria algo comoread -r X <&3
.Lendo um arquivo inteiro em uma matriz (versões do Bash anteriores a 4)
Se o arquivo terminar com uma linha incompleta (nova linha ausente no final), então:
Lendo um arquivo inteiro em uma matriz (versões Bash 4x e posteriores)
ou
E depois
Mais sobre os recursos
read
ereadarray
comandos do shell - GNUMais sobre
IFS
- WikipediaMensagens relacionadas:
fonte
command < input_filename.txt
você sempre pode fazerinput_generating_command | command
oucommand < <(input_generating_command)
Use um loop while, assim:
Notas:
Se você não definir
IFS
corretamente, perderá o recuo.Você quase sempre deve usar a opção -r com read.
Não leia linhas com
for
fonte
-r
opção?Note #2
é um link onde é descrito em detalhes ...-u
opção, você está falando de outro exemplo-u
?Suponha que você tenha este arquivo:
Existem quatro elementos que alterarão o significado da saída do arquivo lida por muitas soluções Bash:
Se você deseja que o arquivo de texto linha por linha, incluindo linhas em branco e linhas de terminação sem CR, use um loop while e faça um teste alternativo para a linha final.
Aqui estão os métodos que podem alterar o arquivo (em comparação com o que
cat
retorna):1) Perca a última linha e os espaços à esquerda e à direita:
(Se
while IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt
preferir, você preserva os espaços à esquerda e à direita, mas ainda perde a última linha se não terminar com CR)2) O uso da substituição de processo com o
cat
will lê o arquivo inteiro de uma só vez e perde o significado de linhas individuais:(Se você remover a
"
partir de$(cat /tmp/test.txt)
você ler a palavra arquivo por palavra, em vez de um só gole. Além disso, provavelmente, não o que se pretende ...)A maneira mais robusta e simples de ler um arquivo linha por linha e preservar todo o espaçamento é:
Se você deseja remover os espaços de liderança e negociação, remova a
IFS=
peça:(Um arquivo de texto sem terminação
\n
, embora bastante comum, é considerado quebrado no POSIX. Se você puder contar com a trilha\n
que não precisa,|| [[ -n $line ]]
nowhile
loop.)Mais no FAQ do BASH
fonte
Se você não deseja que sua leitura seja interrompida pelo caractere de nova linha, use -
Em seguida, execute o script com o nome do arquivo como parâmetro.
fonte
fonte
Aqui está o meu exemplo da vida real de como fazer loop de linhas de outra saída de programa, procurar substrings, remover 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.
Declarar a variável fora do loop, definir valor e usá-lo fora do loop requer que seja feito <<< "$ (...)" sintaxe . O aplicativo precisa ser executado dentro de um contexto do console atual. As aspas ao redor do comando mantêm novas linhas de fluxo de saída.
A correspondência de loop para substrings lê o par nome = valor , divide a parte direita do último caractere = , solta a primeira citação, solta a última citação, temos um valor limpo para ser usado em outro lugar.
fonte
Isso está chegando muito tarde, mas com o pensamento de que isso pode ajudar alguém, estou adicionando a resposta. Além disso, este pode não ser o melhor caminho.
head
O comando pode ser usado com-n
argumento para ler n linhas desde o início do arquivo e otail
comando da mesma forma pode ser usado para ler de baixo. Agora, para buscar a enésima linha do arquivo, dirigimos n linhas , canalizamos os dados para reduzir apenas 1 linha dos dados canalizados.fonte
sed
ouhead
+tail
é incrivelmente ineficiente e, é claro, levanta a questão de por que você simplesmente não usa uma das outras soluções aqui. Se você precisar saber o número da linha, adicione um contador ao seuwhile read -r
loop ou usenl -ba
para adicionar um prefixo de número de linha a cada linha antes do loop.@ Peter: Isso pode funcionar para você-
Isso retornaria a saída
fonte