web-dev-qa-db-fra.com

Identifier les lignes en double dans un fichier sans les supprimer?

J'ai mes références sous forme de fichier texte avec une longue liste d'entrées et chacune a deux champs (ou plus).

La première colonne est l'URL de la référence; la deuxième colonne est le titre qui peut varier un peu en fonction de la manière dont l'entrée a été effectuée. Idem pour le troisième champ qui peut être ou ne pas être présent.

Je souhaite identifier mais pas supprimer les entrées dont le premier champ (URL de référence) est identique. Je sais à propos de sort -k1,1 -u mais cela supprimera automatiquement (de manière non interactive) tout sauf le premier hit. Y a-t-il un moyen de me le faire savoir afin que je puisse choisir lequel retenir?

Dans l'extrait ci-dessous de trois lignes qui ont le même premier champ (http://unix.stackexchange.com/questions/49569/), j'aimerais conserver la ligne 2 car elle comporte des balises supplémentaires (sort, CLI) et supprimer les lignes n ° 1 et n ° 3:

http://unix.stackexchange.com/questions/49569/  unique-lines-based-on-the-first-field
http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field   sort, CLI
http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field

Existe-t-il un programme pour aider à identifier de tels "doublons"? Ensuite, je peux nettoyer manuellement en supprimant personnellement les lignes 1 et 3?

11
DK Bose

Si je comprends votre question, je pense que vous avez besoin de quelque chose comme:

for dup in $(sort -k1,1 -u file.txt | cut -d' ' -f1); do grep -n -- "$dup" file.txt; done

ou:

for dup in $(cut -d " " -f1 file.txt | uniq -d); do grep -n -- "$dup" file.txt; done

file.txt est votre fichier contenant des données vous concernant.

Dans la sortie, vous verrez le nombre de lignes et de lignes où le premier champ est trouvé deux fois ou plus.

8
Radu Rădeanu

Il s'agit d'un problème classique qui peut être résolu avec la commande uniqname__. uniqpeut détecter les doublons consécutifs lignes et supprimer les doublons (-u, --unique) ou ne conserver que les doublons (-d, --repeated).

Comme la commande de doublons de lignes n’est pas importante pour vous, vous devez d’abord la trier. Utilisez ensuite uniqpour n’imprimer que des lignes uniques:

sort yourfile.txt | uniq -u

Il existe également une option -c (--count) qui affiche le nombre de doublons pour l'option -d. Consultez la page de manuel de uniqpour plus de détails.


Si vous ne vous souciez vraiment pas des parties après le premier champ, vous pouvez utiliser la commande suivante pour rechercher les clés en double et imprimer chaque numéro de ligne correspondant (ajoutez un autre | sort -n pour que le résultat soit trié par ligne):

 cut -d ' ' -f1 .bash_history | nl | sort -k2 | uniq -s8 -D

Puisque vous voulez voir les lignes en double (en utilisant le premier champ comme clé), vous ne pouvez pas utiliser directement uniqname__. Le problème qui rend l'automatisation difficile est que les parties du titre varient, mais un programme ne peut pas déterminer automatiquement quel titre doit être considéré comme le titre final.

Voici un script AWK (enregistrez-le dans script.awk) qui prend votre fichier texte en entrée et imprime toutes les lignes en double afin que vous puissiez choisir les fichiers à supprimer. (awk -f script.awk yourfile.txt)

#!/usr/bin/awk -f
{
    # Store the line ($0) grouped per URL ($1) with line number (NR) as key
    lines[$1][NR] = $0;
}
END {
    for (url in lines) {
        # find lines that have the URL occur multiple times
        if (length(lines[url]) > 1) {
            for (lineno in lines[url]) {
                # Print duplicate line for decision purposes
                print lines[url][lineno];
                # Alternative: print line number and line
                #print lineno, lines[url][lineno];
            }
        }
    }
}
10
Lekensteyn

Si je lis ceci correctement, tout ce dont vous avez besoin est quelque chose comme

awk '{print $1}' file | sort | uniq -c | 
    while read num dupe; do [[ $num > 1 ]] && grep -n -- "$dupe" file; done

Cela affichera le numéro de la ligne contenant la dupe et la ligne elle-même. Par exemple, en utilisant ce fichier:

foo bar baz
http://unix.stackexchange.com/questions/49569/  unique-lines-based-on-the-first-field
bar foo baz
http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field   sort, CLI
baz foo bar
http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field

Il produira cette sortie:

2:http://unix.stackexchange.com/questions/49569/  unique-lines-based-on-the-first-field
4:http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field   sort, CLI
6:http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field

Pour imprimer uniquement le numéro de la ligne, vous pouvez faire

awk '{print $1}' file | sort | uniq -c | 
 while read num dupe; do [[ $num > 1 ]] && grep -n -- "$dupe" file; done | cut -d: -f 1

Et pour n'imprimer que la ligne:

awk '{print $1}' file | sort | uniq -c | 
while read num dupe; do [[ $num > 1 ]] && grep -n -- "$dupe" file; done | cut -d: -f 2-

Explication:

Le script awk imprime simplement le premier champ du fichier séparé par des espaces. Utilisez $N pour imprimer le Nième champ. sort le trie et uniq -c compte les occurrences de chaque ligne.

Ceci est ensuite passé à la boucle while qui enregistre le nombre d’occurrences sous le nom $num et la ligne sous le nom $dupe. Si $num est supérieur à un (il est donc dupliqué au moins une fois), il recherchera cette ligne dans le fichier, en utilisant -n pour l’afficher. numéro de ligne. -- indique à grep que ce qui suit n'est pas une option de ligne de commande, utile lorsque $dupe peut commencer par -.

2
terdon

Nul doute que le plus verbeux de la liste pourrait probablement être plus court:

#!/usr/bin/python3
import collections
file = "file.txt"

def find_duplicates(file):
    with open(file, "r") as sourcefile:
        data = sourcefile.readlines()
    splitlines = [
        (index, data[index].split("  ")) for index in range(0, len(data))
        ]
    lineheaders = [item[1][0] for item in splitlines]
    dups = [x for x, y in collections.Counter(lineheaders).items() if y > 1]
    dupsdata = []
    for item in dups:
        occurrences = [
            splitlines_item[0] for splitlines_item in splitlines\
                       if splitlines_item[1][0] == item
            ]
        corresponding_lines = [
            "["+str(index)+"] "+data[index] for index in occurrences
            ]
        dupsdata.append((occurrences, corresponding_lines))

    # printing output   
    print("found duplicates:\n"+"-"*17)
    for index in range(0, len(dups)):
        print(dups[index], dupsdata[index][0])
        lines = [item for item in dupsdata[index][1]]
        for line in lines:
            print(line, end = "")


find_duplicates(file)

donne sur un fichier texte comme:

monkey  banana
dog  bone
monkey  banana peanut
cat  mice
dog  cowmeat

une sortie comme:

found duplicates:
-----------------
dog [1, 4]
[1] dog  bone
[4] dog  cowmeat
monkey [0, 2]
[0] monkey  banana
[2] monkey  banana peanut

Une fois que vous avez choisi les lignes à supprimer:

removelist = [2,1]

def remove_duplicates(file, removelist):
    removelist = sorted(removelist, reverse=True)
    with open(file, "r") as sourcefile:
        data = sourcefile.readlines()
    for index in removelist:
        data.pop(index)
    with open(file, "wt") as sourcefile:
        for line in data:
            sourcefile.write(line)

remove_duplicates(file, removelist)
1
Jacob Vlijm

Voici comment je l'ai résolu:

file_with_duplicates:

1,a,c
2,a,d
3,a,e <--duplicate
4,a,t
5,b,k <--duplicate
6,b,l
7,b,s
8,b,j
1,b,l
3,a,d <--duplicate
5,b,l <--duplicate

Fichier trié et déduit par les colonnes 1 et 2:

sort -t',' -k1,1 -k2,2 -u file_with_duplicates

Fichier trié uniquement par colonnes 1 et 2:

sort -t',' -k1,1 -k2,2 file_with_duplicates

Montrer seulement la différence:

diff <(sort -t',' -k1,1 -k2,2 -u file_with_duplicates) <(sort -t',' -k1,1 -k2,2 file_with_duplicates)

 3a4
   3,a,d
 6a8
   5,b,l
0
Clint Smith

Voir le file.txt trié suivant:

addons.mozilla.org/en-US/firefox/addon/click-to-play-per-element/ ::: C2P per-element
addons.mozilla.org/en-us/firefox/addon/prospector-oneLiner/ ::: OneLiner
askubuntu.com/q/21033 ::: What is the difference between gksudo and gksu?
askubuntu.com/q/21148 ::: openoffice calc sheet tabs (also askubuntu.com/q/138623)
askubuntu.com/q/50540 ::: What is Ubuntu's Definition of a "Registered Application"?
askubuntu.com/q/53762 ::: How to use lm-sensors?
askubuntu.com/q/53762 ::: how-to-use-to-use-lm-sensors
stackoverflow.com/q/4594319 ::: bash - Shell replace cr\lf by comma
stackoverflow.com/q/4594319 ::: Shell replace cr\lf by comma
wiki.ubuntu.com/ClipboardPersistence ::: ClipboardPersistence
wiki.ubuntu.com/ClipboardPersistence ::: ClipboardPersistence - Ubuntu Wiki
www.youtube.com/watch?v=1olY5Qzmbk8 ::: Create new mime types in Ubuntu
www.youtube.com/watch?v=2hu9JrdSXB8 ::: Change mouse cursor
www.youtube.com/watch?v=Yxfa2fXJ1Wc ::: Mouse cursor size

Comme la liste est courte, je peux voir (après le tri) qu'il existe trois jeux de doublons.

Ensuite, par exemple, je peux choisir de conserver:

askubuntu.com/q/53762 ::: How to use lm-sensors?

plutôt que

askubuntu.com/q/53762 ::: how-to-use-to-use-lm-sensors

Mais pour une liste plus longue, ce sera difficile. Sur la base des deux réponses, l’une suggérant uniq et l’autre suggérant cut, je constate que cette commande me donne le résultat souhaité:

$ cut -d " " -f1 file.txt | uniq -d
askubuntu.com/q/53762
stackoverflow.com/q/4594319
wiki.ubuntu.com/ClipboardPersistence
$
0
DK Bose