web-dev-qa-db-fra.com

Comment lire à partir de deux fichiers d'entrée en utilisant la boucle while

Je voulais savoir s'il existe un moyen de lire à partir de deux fichiers d'entrée dans une boucle imbriquée while une ligne à la fois. Par exemple, disons que j'ai deux fichiers FileA et FileB.

FichierA:

[jaypal:~/Temp] cat filea
this is File A line1
this is File A line2
this is File A line3

FichierB:

[jaypal:~/Temp] cat fileb
this is File B line1
this is File B line2
this is File B line3

exemple de script actuel:

[jaypal:~/Temp] cat read.sh 
#!/bin/bash
while read lineA
    do echo $lineA 
    while read lineB
        do echo $lineB 
        done < fileb
done < filea

Exécution:

[jaypal:~/Temp] ./read.sh 
this is File A line1
this is File B line1
this is File B line2
this is File B line3
this is File A line2
this is File B line1
this is File B line2
this is File B line3
this is File A line3
this is File B line1
this is File B line2
this is File B line3

Problème et sortie souhaitée:

Cela boucle complètement sur FileB pour chaque ligne de FileA. J'ai essayé d'utiliser continue, break, exit mais aucun d'eux n'est destiné à atteindre le résultat que je recherche. Je voudrais que le script lise une seule ligne du fichier A, puis une ligne du fichier B et quitte la boucle et continue avec la deuxième ligne du fichier A et la deuxième ligne du fichier B. Quelque chose de similaire au script suivant -

[jaypal:~/Temp] cat read1.sh 
#!/bin/bash
count=1
while read lineA
    do echo $lineA 
        lineB=`sed -n "$count"p fileb`
        echo $lineB
        count=`expr $count + 1`
done < filea

[jaypal:~/Temp] ./read1.sh 
this is File A line1
this is File B line1
this is File A line2
this is File B line2
this is File A line3
this is File B line3

Est-ce possible de le faire avec la boucle while?

27
jaypal singh

Si vous savez avec certitude qu'aucun caractère n'apparaîtra jamais dans le premier fichier, vous pouvez utiliser coller.

Exemple de collage à l'aide de l'onglet de délimitation par défaut:

paste file1 file2 | while IFS="$(printf '\t')" read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done

Exemple de collage avec @:

paste -d@ file1 file2 | while IFS="@" read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done

Notez que cela suffit si le caractère est garanti de ne pas apparaître dans le premier fichier. En effet, read ignorera IFS lors du remplissage de la dernière variable. Ainsi, même si @ Apparaît dans le deuxième fichier, il ne sera pas divisé.

Exemple de collage utilisant certaines fonctionnalités bash pour un code sans doute plus propre:

while IFS=$'\t' read -r f1 f2
do
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done < <(paste file1 file2)

Fonctionnalités Bash utilisées: chaîne c ansi ($'\t') Et substitution de processus (<(...)) à éviter la boucle while dans un problème de sous-shell .

Si vous ne pouvez pas être certain qu'aucun caractère n'apparaîtra jamais dans les deux fichiers, vous pouvez utiliser descripteurs de fichiers .

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  printf 'f1: %s\n' "$f1"
  printf 'f2: %s\n' "$f2"
done 3<file1 4<file2

Pas beaucoup testé. Peut se casser sur des lignes vides.

Les descripteurs de fichiers numéro 0, 1 et 2 sont déjà utilisés pour stdin, stdout et stderr, respectivement. Les descripteurs de fichiers à partir de 3 et plus sont (généralement) gratuits. Le manuel bash met en garde contre l'utilisation de descripteurs de fichiers supérieurs à 9, car ils sont "utilisés en interne".

Notez que les descripteurs de fichiers ouverts sont hérités des fonctions Shell et des programmes externes. Les fonctions et programmes héritant d'un descripteur de fichier ouvert peuvent lire (et écrire dans) le descripteur de fichier. Vous devez prendre soin de fermer tous les descripteurs de fichiers qui ne sont pas requis avant d'appeler une fonction ou un programme externe.

Voici le même programme que ci-dessus avec le travail réel (l'impression) séparé du méta-travail (lecture ligne par ligne de deux fichiers en parallèle).

work() {
  printf 'f1: %s\n' "$1"
  printf 'f2: %s\n' "$2"
}

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  work "$f1" "$f2"
done 3<file1 4<file2

Maintenant, nous prétendons que nous n'avons aucun contrôle sur le code de travail et que ce code, pour une raison quelconque, essaie de lire à partir du descripteur de fichier 3.

unknowncode() {
  printf 'f1: %s\n' "$1"
  printf 'f2: %s\n' "$2"
  read -r yoink <&3 && printf 'yoink: %s\n' "$yoink"
}

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  unknowncode "$f1" "$f2"
done 3<file1 4<file2

Voici un exemple de sortie. Notez que la deuxième ligne du premier fichier est "volée" dans la boucle.

f1: file1 line1
f2: file2 line1
yoink: file1 line2
f1: file1 line3
f2: file2 line2

Voici comment fermer les descripteurs de fichiers avant d'appeler du code externe (ou tout autre code d'ailleurs).

while true
do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  # this will close fd3 and fd4 before executing anycode
  anycode "$f1" "$f2" 3<&- 4<&-
  # note that fd3 and fd4 are still open in the loop
done 3<file1 4<file2
33
lesmana

Ouvrez les deux fichiers sur différents descripteurs de fichiers . Redirigez l'entrée du read intégré vers le descripteur auquel le fichier que vous souhaitez est connecté. Dans bash/ksh/zsh, vous pouvez écrire read -u 3 au lieu de read <&3.

while IFS= read -r lineA && IFS= read -r lineB <&3; do
  echo "$lineA"; echo "$lineB"
done <fileA 3<fileB

Cet extrait s'arrête lorsque le fichier le plus court a été traité. Voir Lire deux fichiers dans une boucle IFS while - Existe-t-il un moyen d'obtenir un résultat zéro diff dans ce cas? si vous souhaitez continuer le traitement jusqu'à la fin des deux fichiers.

Voir aussi Quand utiliseriez-vous un descripteur de fichier supplémentaire? pour plus d'informations sur les descripteurs de fichiers, et Pourquoi `while IFS = read` est-il utilisé si souvent, au lieu de` IFS =; while read ..`? pour une explication de IFS= read -r.

Je sais que vous voulez un script Shell, mais vous voudrez peut-être jeter un œil à la commande paste.

4
lutzky

Essayez la commande ci-dessous:

paste -d '\n' inp1.txt inp2.txt > outfile.txt
3
Shree

Alternativement, je suppose que vous pouvez Slurper le fichier dans une variable de tableau liant chaque ligne du fichier dans le tableau [line_of_file_index] en utilisant la commande mapfile de bash. Cependant, je ne sais pas si c'est uniquement pour Bash3 supérieur ou Bash4.

http://wiki.bash-hackers.org/commands/builtin/mapfile

0
Nikhil Mulley