web-dev-qa-db-fra.com

Comment boucler sur les lignes d'un fichier?

Disons que j'ai ce fichier:

hello
world
hello world

Ce programme

#!/bin/bash

for i in $(cat $1); do
    echo "tester: $i"
done

les sorties

tester: hello
tester: world
tester: hello
tester: world

J'aimerais que l'itération de for sur chaque ligne ignore les espaces blancs individuellement, c'est-à-dire que les deux dernières lignes devraient être remplacées par

tester: hello world

L'utilisation de guillemets for i in "$(cat $1)"; entraîne l'attribution de i à l'ensemble du fichier à la fois. Que dois-je changer?

63
Tobias Kienzler

Avec for et IFS :

#!/bin/bash

IFS=$'\n'       # make newlines the only separator
set -f          # disable globbing
for i in $(cat < "$1"); do
  echo "tester: $i"
done

Notez cependant qu'il sautera les lignes vides car newline étant un caractère d'espace blanc IFS, ses séquences comptent pour 1 et les caractères de début et de fin sont ignoré. Avec zsh et ksh93 (pas bash), vous pouvez le changer en IFS=$'\n\n' pour que les sauts de ligne ne soient pas traités spécialement, cependant notez que tous les caractères de fin de ligne (ce qui inclut les lignes vides de fin) seront toujours supprimés par la commande substitution.

Ou avec read (plus cat):

#!/bin/bash

while IFS= read -r line; do
  echo "tester: $line"
done < "$1"

Là, les lignes vides sont conservées, mais notez que cela sauterait la dernière ligne si elle n'était pas correctement délimitée par un caractère de nouvelle ligne.

76
wag

Pour ce que ça vaut, je dois le faire assez souvent et je ne me souviens jamais de la façon exacte d'utiliser while IFS= read..., j'ai donc défini la fonction suivante dans mon profil bash:

# iterate the line of a file and call input function
iterlines() {
    (( $# < 2 )) && { echo "Usage: iterlines <File> <Callback>"; return; }
    local File=$1
    local Func=$2
    n=$(cat "$File" | wc -l)
    for (( i=1; i<=n; i++ )); do
        "$Func" "$(sed "${i}q;d" "$File")"
    done
}

Cette fonction détermine d'abord le nombre de lignes dans le fichier, puis utilise sed pour extraire ligne après ligne et transmet chaque ligne comme argument de chaîne unique à une fonction donnée. Je suppose que cela pourrait devenir vraiment inefficace avec des fichiers volumineux, mais cela n'a pas été un problème pour moi jusqu'à présent (suggestions sur la façon d'améliorer cet accueil bien sûr).

L'utilisation est assez douce IMO:

>> cat example.txt # note the use of spaces, whitespace, etc.
a/path

This is a sentence.
"wi\th quotes"
$End
>> iterlines example.txt echo # preserves quotes, $ and whitespace
a/path

This is a sentence.
"wi\th quotes"
$End
>> x() { echo "$#"; }; iterlines example.txt x # line always passed as single input string
1
1 
1
1
1
1
Jonathan H