Como processar um arquivo linha por linha em um script Linux Bash


0
Uma janela de terminal em um sistema de computador Linux.
Fatmawati Achmad Zaenuri / Shutterstock

É muito fácil ler o conteúdo de um arquivo de texto do Linux linha por linha em um script de shell – contanto que você lide com algumas pegadinhas sutis. Veja como fazer isso da maneira segura.

Arquivos, texto e expressões idiomáticas

Cada linguagem de programação possui um conjunto de expressões idiomáticas. Essas são as maneiras padrão e simples de realizar um conjunto de tarefas comuns. Eles são a maneira elementar ou padrão de usar um dos recursos da linguagem com a qual o programador está trabalhando. Eles se tornam parte do kit de ferramentas de projetos mentais de um programador.

Ações como ler dados de arquivos, trabalhar com loops e trocar os valores de duas variáveis ​​são bons exemplos. O programador conhecerá pelo menos uma maneira de atingir seus objetivos de maneira genérica ou simples. Talvez isso seja suficiente para o requisito em questão. Ou talvez eles embelezem o código para torná-lo mais eficiente ou aplicável à solução específica que estão desenvolvendo. Mas ter o idioma do bloco de construção na ponta dos dedos é um ótimo ponto de partida.

Conhecer e compreender expressões idiomáticas em uma linguagem também torna mais fácil aprender uma nova linguagem de programação. Saber como as coisas são construídas em uma linguagem e procurar o equivalente – ou o mais próximo – em outra linguagem é uma boa maneira de avaliar as semelhanças e diferenças entre as linguagens de programação que você já conhece e aquela que está aprendendo.

Lendo linhas de um arquivo: o One-Liner

No Bash, você pode usar um while loop na linha de comando para ler cada linha de texto de um arquivo e fazer algo com ela. Nosso arquivo de texto é chamado de “data.txt”. Ele contém uma lista dos meses do ano.

January
February
March
.
.
October
November
December

Nossa linha simples é:

while read line; do echo $line; done < data.txt

O while loop lê uma linha do arquivo e o fluxo de execução do pequeno programa passa para o corpo do loop. O echo comando escreve a linha de texto na janela do terminal. A tentativa de leitura falha quando não há mais linhas para serem lidas e o loop está concluído.

Um truque interessante é a capacidade de redirecionar um arquivo em um loop. Em outras linguagens de programação, você precisa abrir o arquivo, ler a partir dele e fechá-lo novamente quando terminar. Com o Bash, você pode simplesmente usar o redirecionamento de arquivo e deixar o shell lidar com todas as coisas de baixo nível para você.

Claro, essa linha não é terrivelmente útil. Linux já fornece o cat comando, que faz exatamente isso por nós. Criamos uma maneira prolixa de substituir um comando de três letras. Mas demonstra visivelmente os princípios da leitura de um arquivo.

Isso funciona bem o suficiente, até certo ponto. Suponha que temos outro arquivo de texto que contém os nomes dos meses. Neste arquivo, a seqüência de escape para um caractere de nova linha foi anexada a cada linha. Vamos chamá-lo de “data2.txt”.

Januaryn
Februaryn
Marchn
.
.
Octobern
Novembern
Decembern

Vamos usar nosso one-liner em nosso novo arquivo.

while read line; do echo $line; done < data2.txt

O caractere de escape da barra invertida ” ”Foi descartado. O resultado é que um “n” foi acrescentado a cada linha. O Bash está interpretando a barra invertida como o início de uma sequência de escape. Freqüentemente, não queremos que o Bash interprete o que está lendo. Pode ser mais conveniente ler uma linha em sua totalidade – sequências de escape de barra invertida e tudo – e escolher o que analisar ou substituir, dentro de seu próprio código.

Se quisermos fazer qualquer processamento ou análise significativa nas linhas de texto, precisaremos usar um script.

Lendo linhas de um arquivo com um script

Aqui está nosso script. Chama-se “script1.sh”.

#!/bin/bash

Counter=0

while IFS='' read -r LinefromFile || [[ -n "${LinefromFile}" ]]; do

    ((Counter++))
    echo "Accessing line $Counter: ${LinefromFile}"

done < "$1"

Definimos uma variável chamada Counter a zero, então definimos nosso while ciclo.

A primeira instrução na linha while é IFS='' . IFS significa separador de campo interno. Ele contém valores que o Bash usa para identificar os limites das palavras. Por padrão, o comando read remove os espaços em branco à esquerda e à direita. Se quisermos ler as linhas do arquivo exatamente como estão, precisamos definir IFS para ser uma string vazia.

Poderíamos definir isso uma vez fora do loop, assim como estamos definindo o valor de Counter . Mas com scripts mais complexos – especialmente aqueles com muitas funções definidas pelo usuário neles – é possível que IFS pode ser definido com valores diferentes em outras partes do script. Garantindo que IFS é definido como uma string vazia cada vez que o while loop iterates garante que sabemos qual será seu comportamento.

Vamos ler uma linha de texto em uma variável chamada LinefromFile . Estamos usando o -r (leia a barra invertida como um caractere normal) opção para ignorar as barras invertidas. Eles serão tratados como qualquer outro personagem e não receberão nenhum tratamento especial.

Existem duas condições que irão satisfazer o while loop e permitir que o texto seja processado pelo corpo do loop:

  • read -r LinefromFile : Quando uma linha de texto é lida com sucesso do arquivo, o read comando envia um sinal de sucesso para o while , e as while loop passa o fluxo de execução para o corpo do loop. Observe que o read O comando precisa ver um caractere de nova linha no final da linha de texto para considerá-lo uma leitura bem-sucedida. Se o arquivo não for um arquivo de texto compatível com POSIX, a última linha pode não incluir um caractere de nova linha. Se o read comando vê o final do marcador de arquivo (EOF) antes que a linha seja encerrada por uma nova linha, ele irá não trate-a como uma leitura bem-sucedida. Se isso acontecer, a última linha do texto não será passada para o corpo do loop e não será processada.
  • [ -n "${LinefromFile}" ] : Precisamos fazer algum trabalho extra para lidar com arquivos não compatíveis com POSIX. Essa comparação verifica o texto lido do arquivo. Se não for encerrado com um caractere de nova linha, esta comparação ainda retornará sucesso para o while ciclo. Isso garante que quaisquer fragmentos da linha final sejam processados ​​pelo corpo do loop.

Essas duas cláusulas são separadas pelo operador lógico OR ” || ”Para que se ou Se a cláusula retornar sucesso, o texto recuperado será processado pelo corpo do loop, haja ou não um caractere de nova linha.

No corpo do nosso loop, estamos incrementando o Counter variável por um e usando echo para enviar alguma saída para a janela do terminal. O número da linha e o texto de cada linha são exibidos.

Ainda podemos usar nosso truque de redirecionamento para redirecionar um arquivo em um loop. Nesse caso, estamos redirecionando $ 1, uma variável que contém o nome do primeiro parâmetro de linha de comando que foi passado para o script. Usando esse truque, podemos facilmente passar o nome do arquivo de dados em que queremos que o script trabalhe.

Copie e cole o script em um editor e salve-o com o nome de arquivo “script1.sh”. Use o chmod comando para torná-lo executável.

chmod +x script1.sh

Vamos ver o que nosso script faz com o arquivo de texto data2.txt e as barras invertidas nele contidas.

./script1.sh data2.txt

Cada caractere na linha é exibido literalmente. As barras invertidas não são interpretadas como caracteres de escape. Eles são impressos como caracteres regulares.

Passando a linha para uma função

Ainda estamos apenas ecoando o texto na tela. Em um cenário de programação do mundo real, provavelmente estaríamos prestes a fazer algo mais interessante com a linha de texto. Na maioria dos casos, é uma boa prática de programação lidar com o processamento posterior da linha em outra função.

Veja como poderíamos fazer isso. Este é “script2.sh”.

#!/bin/bash

Counter=0

function process_line() {

    echo "Processing line $Counter: $1"

}

while IFS='' read -r LinefromFile || [[ -n "${LinefromFile}" ]]; do

    ((Counter++))
    process_line "$LinefromFile"

done < "$1"

Nós definimos nosso Counter variável como antes, e então definimos uma função chamada process_line() . A definição de uma função deve aparecer antes a função é chamada primeiro no script.

Nossa função receberá a nova linha de texto lida em cada iteração do while ciclo. Podemos acessar esse valor dentro da função usando o $1 variável. Se houvesse duas variáveis ​​passadas para a função, poderíamos acessar esses valores usando $1 e $2 e assim por diante para mais variáveis.

O While loop é principalmente o mesmo. Existe apenas uma mudança dentro do corpo do loop. O echo linha foi substituída por uma chamada para o process_line() função. Observe que você não precisa usar os colchetes “()” no nome da função ao chamá-la.

O nome da variável que contém a linha de texto, LinefromFile , é colocado entre aspas quando é passado para a função. Isso atende às linhas que têm espaços. Sem as aspas, a primeira palavra é tratada como $1 pela função, a segunda palavra é considerada $2 , e assim por diante. O uso de aspas garante que toda a linha de texto seja tratada, como um todo, $1. Observe que este é não o mesmo $1 que contém o mesmo arquivo de dados passado para o script.

Porque Counter foi declarado no corpo principal do script e não dentro de uma função, pode ser referenciado dentro do process_line() função.

Copie ou digite o script acima em um editor e salve-o com o nome de arquivo “script2.sh”. Torne-o executável com chmod :

chmod +x script2.sh

Agora podemos executá-lo e passar um novo arquivo de dados, “data3.txt”. Tem uma lista dos meses e uma linha com muitas palavras.

January
February
March
.
.
October
November nMore text "at the end of the line"
December

Nosso comando é:

./script2.sh data3.txt

As linhas são lidas do arquivo e passadas uma a uma para o process_line() função. Todas as linhas são exibidas corretamente, incluindo a ímpar com backspace, aspas e várias palavras.

Os blocos de construção são úteis

Existe uma linha de pensamento que diz que um idioma deve conter algo único para aquele idioma. Não é uma crença minha. O importante é que ele faz bom uso da linguagem, é fácil de lembrar e fornece uma maneira confiável e robusta de implementar algumas funcionalidades em seu código.


Like it? Share with your friends!

0

What's Your Reaction?

hate hate
0
hate
confused confused
0
confused
fail fail
0
fail
fun fun
0
fun
geeky geeky
0
geeky
love love
0
love
lol lol
0
lol
omg omg
0
omg
win win
0
win

0 Comments

Your email address will not be published. Required fields are marked *

Choose A Format
Personality quiz
Series of questions that intends to reveal something about the personality
Trivia quiz
Series of questions with right and wrong answers that intends to check knowledge
Poll
Voting to make decisions or determine opinions
Story
Formatted Text with Embeds and Visuals
List
The Classic Internet Listicles
Countdown
The Classic Internet Countdowns
Open List
Submit your own item and vote up for the best submission
Ranked List
Upvote or downvote to decide the best list item
Meme
Upload your own images to make custom memes
Video
Youtube, Vimeo or Vine Embeds
Audio
Soundcloud or Mixcloud Embeds
Image
Photo or GIF
Gif
GIF format