web-dev-qa-db-fra.com

Pourquoi ne pouvez-vous pas utiliser cat pour lire un fichier ligne par ligne où chaque ligne a des délimiteurs

J'ai un fichier texte qui contient quelque chose comme ceci:

abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma

J'ai écrit un script

for i in `cat file`
do
   echo $i
done

Pour une raison quelconque, la sortie du script ne produit pas le fichier ligne par ligne mais le rompt au niveau des virgules, ainsi que de la nouvelle ligne. Pourquoi est chat ou "pour bla dans cat xyz "faire cela et comment puis-je faire en sorte de ne PAS le faire? Je sais que je peux utiliser un

while read line
do
   blah balh blah
done < file

mais je veux savoir pourquoi cat ou le "for blah in" fait cela pour approfondir ma compréhension des commandes unix. La page de manuel de Cat ne m'a pas aidé et la recherche ou la boucle dans le manuel bash n'a donné aucune réponse ( http://www.gnu.org/software/bash/manual/bashref.html =). Merci d'avance pour votre aide.

17
Classified

Le problème n'est pas dans cat, ni dans la boucle for en soi; c'est dans l'utilisation de guillemets. Lorsque vous écrivez:

for i in `cat file`

ou mieux):

for i in $(cat file)

ou (dans bash):

for i in $(<file)

le Shell exécute la commande et capture la sortie sous forme de chaîne, en séparant les mots au niveau des caractères dans $IFS. Si vous voulez que les lignes soient saisies dans $i, Vous devez soit jouer avec IFS, soit utiliser la boucle while. La boucle while est meilleure s'il existe un danger que les fichiers traités soient volumineux; il n'a pas à lire tout le fichier en mémoire d'un coup, contrairement aux versions utilisant $(...).

IFS='
'
for i in $(<file)
do echo "$i"
done

Les guillemets autour du "$i" Sont généralement une bonne idée. Dans ce contexte, avec le $IFS Modifié, ce n'est en fait pas critique, mais les bonnes habitudes sont quand même de bonnes habitudes. Cela compte dans le script suivant:

old="$IFS"
IFS='
'
for i in $(<file)
do
   (
   IFS="$old"
   echo "$i"
   )
done

lorsque le fichier de données contient plusieurs espaces entre les mots:

$ cat file
abc                  123,         comma
the   quick   brown   fox
jumped   over   the   lazy   dog
comma,   comma
$ 

Production:

$ sh bq.sh
abc                  123,         comma
the   quick   brown   fox
jumped   over   the   lazy   dog
comma,   comma
$

Sans les guillemets doubles:

$ cat bq.sh
old="$IFS"
IFS='
'
for i in $(<file)
do
   (
   IFS="$old"
   echo $i
   )
done
$ sh bq.sh
abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma
$
20
Jonathan Leffler

Vous pouvez utiliser la variable IFS pour spécifier que vous voulez une nouvelle ligne comme séparateur de champ:

IFS=$'\n'
for i in `cat file`
do
   echo $i
done
6
cforbish

la boucle for couplée à un changement du séparateur de champ interne (IFS) lira le fichier comme prévu

pour une entrée

abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma

Pour boucle couplée à un changement IFS

old_IFS=$IFS
IFS=$'\n'
for i in `cat file`
do
        echo $i
done
IFS=$old_IFS

résulte en

abc 123, comma
the quick brown fox
jumped over the lazy dog
comma, comma
3
Dan675

IFS - Le séparateur de champ interne peut être réglé pour obtenir ce que vous voulez.

Pour lire une ligne entière à la fois, utilisez: IFS = ""

2
Sudheej
cat filename | while read i
do
    echo $i
done
0
user2954660