grep le bloc exact de lignes (contenu du fichier1) du fichier2

9

J'ai deux fichiers, file1et file2.

Le contenu de l'échantillon de file1est:

A B
C D
E F
G H

et le contenu de file2est comme:

A B
few other lines
E F
few more other lines
A B
C D
E F
G H
few more other lines
G H

Je souhaite donc rechercher le bloc de file1contenu entier file2uniquement. Cela signifie que la sortie ne doit contenir que ces lignes:

A B
C D
E F
G H

veuillez noter que: - seules les lignes qui se rejoignent devraient faire partie de la sortie.

sachin
la source
Je ne comprends pas votre question. Si vous souhaitez uniquement imprimer le contenu exact de file1et rien d'autre, utilisez simplement cat file1.
Wildcard
@Wildcard, il veut voir si le fichier2 contient exactement le même contenu que le fichier1. Pensez-y comme si vous cherchiez un chapitre spécifique dans un livre
Sergiy Kolodyazhnyy
Je vote pour rouvrir cela car les "membres de l'ensemble" sont composés de plusieurs lignes (je n'ai pas repéré cela au début), ce qui est légèrement plus complexe que les lignes simples gérées par la réponse acceptée à la question en double proposée.
Kusalananda
1
Il ne s'agit pas d'ensembles. Si vous souhaitez marquer cela comme un doublon, trouvez au moins une autre question sur les expressions régulières multi-lignes.
Michael Vehrs

Réponses:

11

grepest assez stupide en ce qui concerne les modèles multilignes, mais la traduction de tous les caractères \nde nouvelle ligne du modèle et du texte pour rechercher en caractères NUL \0avant de les comparer résout ce problème. Il est évidemment également nécessaire de traduire \0dans la sortie vers \n.

Voici votre commande, en supposant qu'elle file1contient le modèle dans lequel vous souhaitez rechercher file2:

grep -aof <(tr '\n' '\0' < file1) <(tr '\n' '\0' < file2) | tr '\0' '\n'

Exemple de sortie pour vos fichiers donnés:

A B
C D
E F
G H

Explication:

  • <(tr '\n' '\0' < file1)crée un objet semblable à un fichier FIFO / nommé pipe / temporaire égal à file1, mais avec tous les caractères de nouvelle ligne traduits en caractères NUL.
  • <(tr '\n' '\0' < file2)fait de même, mais pour file2.
  • grep -f PATTERN_FILE INPUT_FILErecherche le motif (s) à partir PATTERN_FILEde INPUT_FILE.
  • Le -adrapeau de greppermet la correspondance sur les fichiers binaires. Ceci est nécessaire car sinon il ignorerait les fichiers contenant des caractères non imprimables comme \0.
  • Le -odrapeau de grepfait qu'il imprime uniquement la séquence correspondante, pas toute la ligne où il a été trouvé.
  • | tr '\0' '\n' convertit tous les caractères NUL de la sortie de la commande sur le côté gauche en caractères de nouvelle ligne.
Byte Commander
la source
6

Ce qui suit est maladroit, mais fonctionne avec GNU awk:

awk -v RS="$(<file1)" '{print RT}' file2
Michael Vehrs
la source
3

Juste pour le plaisir en pure bash

mapfile -t <file1
while read line ; do
    [ "$line" = "${MAPFILE[i++]}" ] || { ["$line" = "$MAPFILE" ] && i=1 || i=0; }
    [ $i -eq ${#MAPFILE[*]} ] && { printf "%s\n" "${MAPFILE[@]}"; i=0; }
done <file2
Costas
la source
3

Voici un peu plus élégant grep+ perl:

$ grep -Pzo "$(perl -pe 's/\n/\\n/g' file1.txt )"  file2.txt                    
A B
C D
E F
G H

Cependant, il y a un gros problème. S'il y a un saut de ligne final dans file1, le modèle ne sera pas correct, autrement dit: A B\nC D\nE F\nG H\n\n.

(Merci spécial @terdon pour avoir fourni la partie perl)

Comme costas l'a noté, on peut utiliser perl -0pe 's/\n(\n+$)?/\\n/g' à la place de l'autre perlcommande pour éviter la nouvelle ligne de fin dans lefile1.txt

Sergiy Kolodyazhnyy
la source
1
S'il y a une nouvelle ligne de fin et que ce n'est pas l'OP que vous voulez trouver perl -0pe 's/\n(\n+$)?/\\n/g'. Sans -0le gmodificateur d'expression régulière est en sus.
Costas
1

Je ne sais pas trop ce que vous voulez que la sortie soit, mais c'est facile à faire avec des langues qui ne sont pas exclusivement orientées ligne (surtout si les deux fichiers peuvent être lus en mémoire). Voici un script python qui vous indiquera le nombre de correspondances.

import sys
find = open(sys.argv[1]).read()
hay = open(sys.argv[2]).read()
print("The text occurs", hay.count(find), "times")

Vous souhaitez imprimer file1autant de fois que cela correspond? Remplacez la dernière ligne par ceci:

print(find * hay.count(find))

Vous pouvez tout regrouper dans un appel ou un alias de ligne de commande, si vous voulez vraiment:

python -c 'import sys; print("The text occurs", open(sys.argv[2]).read().count(open(sys.argv[1]).read()), "times")' file1 file2
alexis
la source
1
grep -lir 'A B \n D C \n whatever' ./folder_to_search

le résultat sera tous les fichiers avec une correspondance de texte exacte

meyerson
la source
0

Voici une autre approche utilisant python (testé avec python3 3.5.2, sans aucune plainte de pylint3 1.5.6):

""" Locate entire file contents contiguous in other file """

import sys
import re
from mmap import mmap, PROT_READ

def memmap(name):
    """ Return memoryview of readonly mmap """
    with open(name, 'rb') as file:
        return memoryview(mmap(file.fileno(), 0, access=PROT_READ))

def finder(needle, haystack):
    """ Return iterator """
    return re.compile(re.escape(needle)).finditer(haystack)

print(tuple(finder(*(memmap(name) for name in sys.argv[1:3]))))

La gestion des arguments de ligne de commande via sys.argvest certes simpliste. Vous pouvez faire bien d'autres choses avec la valeur de retour de findersur les deux memoryviewobjets que vous transmettez, en plus de la transmettre tuple. Chaque SRE_Matchélément fourni par l'itérateur renvoyé par finderpossède une variété de méthodes, dont un échantillonnage est résumé dans la printsortie (le span, par exemple, indique la plage d'octets de chaque correspondance).

Eirik Fuller
la source