2016-04-13 3 views
1

Mon bash-foo est un peu rouillé en ce moment, donc je voulais voir s'il y avait une façon intelligente de supprimer les doublons partiels d'un fichier. J'ai un tas de fichiers contenant des milliers de lignes avec le format suivant:Supprimer les doublons partiels du fichier texte

String1|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 05:28:03|x 

Essentiellement, il est un tas de tuyaux chaînes délimitées, avec les deux dernières colonnes étant un horodatage et x. Ce que je voudrais faire est de concaténer tous mes fichiers, puis supprimer tous les doublons partiels. Je définis le doublon partiel comme une ligne dans le fichier qui correspond de String1 à String22, mais l'horodatage peut être différent.

Par exemple, un fichier contenant:

String1|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 05:28:03|x 
String1|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 12:12:12|x 
String124|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 05:28:03|x 

deviendrait:

String1|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 05:28:03|x 
String124|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 05:28:03|x 

(Peu importe qui horodatage est choisi).

Des idées?

Répondre

3

En utilisant awk vous pouvez le faire:

awk '{k=$0; gsub(/(\|[^|]*){2}$/, "", k)} !seen[k]++' file 

String1|String2|String3|String4|String5|String6|String7|09-Apr-2016 05:28:03|x 
String124|String2|String3|String4|String5|String6|String7|09-Apr-2016 05:28:03|x 

commande awk d'abord une k variables en supprimant 2 derniers champs de chaque ligne. Ensuite, il utilise un tableau associatif seen avec la clé k où il n'imprime que la première instance de la clé en stockant chaque clé de processus dans le tableau.

0

Si vous avez la version Bash 4, qui prend en charge les réseaux associatifs, il peut être fait assez efficacement dans Bash pur:

declare -A found 
while IFS= read -r line || [[ -n $line ]] ; do 
    strings=${line%|*|*} 
    if ((! ${found[$strings]-0})) ; then 
     printf '%s\n' "$line" 
     found[$strings]=1 
    fi 
done < "$file" 
+1

Remplacer «assez efficacement» par «très inefficace» - cela aurait un ordre de grandeur plus lent qu'un script awk équivalent pour un fichier raisonnablement grand. Voir [pourquoi-utilise-un-shell-loop-to-process-text-consider-mal-pratique] (http://unix.stackexchange.com/questions/169716/why-is-using-a-shell -loop-to-process-text-considered-bad-pratique). –

0

même idée avec @anubhava, mais je pense plus idiomatiques

$ awk -F'|' '{line=$0;$NF=$(NF-1)=""} !a[$0]++{print line}' file 

String1|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 05:28:03|x 
String124|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 05:28:03|x 
+1

C'est moins idiomatique (le nom du tableau idiomatique est 'vu []' et vous ne voudriez pas coder en dur une liste de champs à définir) et c'est fragile parce qu'il va remplacer tous les '|' dans '$ 0' avec des caractères vides de sorte que vous ne puissiez plus dire 'a | bc' de' ab | c' - ils deviendraient tous les deux 'abc' quand ils seraient utilisés dans 'a [$ 0] ++'. –