Como processar um arquivo linha por linha em um script bash do Linux

Conteúdo

Uma janela de terminal em um sistema de computador Linux.

É bastante fácil ler o conteúdo de um arquivo de texto do Linux linha por linha em um script de shell, contanto que você enfrente alguns problemas sutis. Veja como fazer isso com segurança.

Registros, texto e expressões idiomáticas

Cada linguagem de programação tem um conjunto de expressões idiomáticas. Estas são as maneiras padrão e fáceis de realizar um conjunto de tarefas comuns. Eles são a forma elementar ou padrão de usar uma das características da linguagem com a qual o programador está trabalhando. Eles se tornam parte de um kit de ferramentas do programador de blueprint mental.

Ações como ler dados de arquivos, trabalhar com loops e trocar os valores de duas variáveis ​​são bons exemplos. O programador saberá pelo menos uma maneira de atingir seus objetivos de maneira genérica ou básica. Talvez isso seja o suficiente para o requisito em questão.. Ou talvez eles embelezem o código para torná-lo mais eficiente ou aplicável à resposta específica que estão desenvolvendo. Mas ter a linguagem básica ao seu alcance é um ótimo ponto de partida.

Conhecer e compreender expressões idiomáticas em uma linguagem também facilita a aquisição de uma nova linguagem de programação. Saber como as coisas são construídas em uma linguagem e procurar algo semelhante - 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 você está aprendendo.

Leia as linhas de um arquivo: Uma linha

Na festa, 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 “dados.txt”. Contém uma lista de meses do ano.

January
February
March
.
.
October
November
December

Nosso single de linha única é:

enquanto ler linha; fazer eco $line; feito < dados.txt

a while loop lê uma linha do arquivo, e o fluxo de execução do pequeno programa passa para o corpo loop. a echo tipo de comando a linha de texto na janela do terminal. A tentativa de leitura falha quando não há mais linhas para ler e o ciclo é terminado.

Um bom truque é a capacidade de redirecionar um arquivo para um loop. Em outras linguagens de programação, Eu precisaria abrir o arquivo, leia e feche-o novamente quando terminar. Com Bash, você pode apenas usar o redirecionamento de arquivo e deixar o shell lidar com todas as coisas de baixo nível para você.

Desde já, esta frase não é muito útil. Linux já fornece cat comando, o que exatamente isso faz por nós. Criamos um longo caminho para substituir um comando de três letras. Mas isso demonstra visivelmente os princípios de ler um arquivo..

Isso funciona muito bem., até certo ponto. Suponha que temos outro arquivo de texto que contém os nomes dos meses. Neste arquivo, a sequência de fuga para um personagem newline foi adicionado a cada linha. Nós vamos chamá-lo “dados2.txt”.

Januaryn
Februaryn
Marchn
.
.
Octobern
Novembern
Decembern

Vamos usar nosso one-liner em nosso novo arquivo.

enquanto ler linha; fazer eco $line; feito < dados2.txt

O personagem de fuga de barras traseiras ” "Foi descartado.. O resultado é que um “n” para cada linha. O Bash está interpretando a barra invertida como o início de um sequência de fuga. Frequentemente, 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 mais) e selecione o que analisar ou substituir você mesmo, dentro do seu próprio código.

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

Leia as linhas de um arquivo com um script

Aqui está o nosso script. Se chama “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ço, então nós definimos nosso while círculo.

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

Poderíamos determinar isso uma vez fora do circuito, da mesma forma que definimos o valor de Counter . Mas com scripts mais complexos, especialmente aqueles com muitas funções definidas pelo usuário, é viá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 O loop itera garante que sabemos qual será o 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) ignorar 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 permite que o texto seja processado pelo corpo do loop:

  • read -r LinefromFile : Quando uma linha de texto é lida corretamente do arquivo, a read O comando envia um sinal de sucesso para o while , e ele while loop passa o fluxo de execução para o corpo do loop. Observe que o read O comando precisa ver um personagem de nova linha no final da linha de texto para considerá-la uma leitura bem-sucedida. Se o arquivo não for um POSIX arquivo de texto compatível, a a última linha pode não incluir um novo caractere de linha. Se ele read comando ver o final do marcador de arquivo (EOF) antes que a linha termine com uma nova linha, não trate isso 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á renderizada.
  • [ -n "${LinefromFile}" ] : Precisamos fazer algum trabalho adicional para lidar com arquivos não compatíveis com POSIX. Esta comparação verifica o texto que é lido do arquivo. Se não terminar com um caractere de nova linha, esta comparação ainda retornará sucesso para while círculo. 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 ou ” || “De modo que se algum A cláusula retorna sucesso, o texto recuperado é processado pelo corpo do loop, se há um caractere de nova linha ou não.

No corpo do nosso loop, estamos aumentando 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 para um loop. Para este caso, estamos redirecionando $ 1, uma variável contendo o nome do primeiro parâmetro da linha de comando que você passou para o script. Com este truque, podemos passar facilmente o nome do arquivo de dados em que queremos que o script trabalhe.

copiar e colar o script em um editor e salvá-lo com o nome do 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 que ele contém.

./script1.sh data2.txt

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

Passe a linha para uma função

Ainda estamos 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 pós-processamento da linha em outra função.

É assim que poderíamos fazer. Isto é “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 depois definimos uma função chamada process_line() . A definição de uma função deve aparecer antes de a função é chamada primeiro no script.

Nossa função receberá a linha de texto que acabou de ler em cada iteração do while círculo. Podemos acessar esse valor dentro da função usando o $1 variável. Se eles tivessem passado duas variáveis ​​para a função, poderíamos acessar esses valores usando $1 e $2 e assim por diante para mais variáveis.

Leihile loop é basicamente o mesmo. Há apenas uma mudança dentro do corpo do loop. a echo A linha foi substituída por uma chamada para process_line() Função. Observe que você não precisa usar os colchetes "()”No nome da função quando você a está chamando.

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

Devido a que 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.

copiar ou digitar o script acima em um editor e salvá-lo com o nome do arquivo “script2.sh”. Torne-o executável com chmod :

chmod + x script2.sh

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

January
February
March
.
.
October
November nMore text "no final da linha"
Dezembro

Nosso comando é:

./script2.sh dados.txt

As linhas são lidas a partir do arquivo e passou um por um para o process_line() Função. Todas as linhas são exibidas corretamente, incluindo aquele com o recuo, aspas e várias palavras.

Blocos de construção são úteis

Existe uma linha de pensamento que diz que uma linguagem deve conter algo único para aquela linguagem. Essa não é uma crença que eu subscrevo. O importante é que ele faz bom uso da linguagem, é fácil de lembrar e fornece uma maneira confiável e robusta de implementar algumas funções em seu código.

Assine a nossa newsletter

Nós não enviaremos SPAM para você. Nós odiamos isso tanto quanto você.