4
votes

Comment imprimer des lignes avec des champs dupliqués?

J'ai besoin d'imprimer des lignes avec des champs dupliqués, j'ai essayé avec sed cela ne fonctionne pas.
Le fichier d'entrée comporte deux lignes:

sed -rn '/(\b\w+\b).*\b\1\b/ p' input_file

La sortie ne doit être que la deuxième ligne, car elle contient des chaînes (champs) exactement dupliquées.
Mais il imprime les deux lignes en utilisant la commande ci-dessous

s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u1
s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0

Merci
RKP


6 commentaires

première ligne: s1 / s2 / s3 / s4 / s5 / u0 a1_b2_c3_d4_e5_f6_g7 s1 / s2 / s3 / s4 / s5 / u1


deuxième ligne: s1 / s2 / s3 / s4 / s5 / u0 a1_b2_c3_d4_e5_f6_g7 s1 / s2 / s3 / s4 / s5 / u0


Veuillez ajouter la sortie souhaitée pour cet exemple d'entrée à votre question.


La première ligne contient de nombreuses chaînes dupliquées. La chaîne "s" est dupliquée, sont les chaînes "s1" et "s1 /" et "s2 / s3". Pour qu'un ordinateur fasse ce que vous voulez, vous devez décrire avec précision ce que vous voulez.


Veuillez spécifier le délimiteur de champ. Est-ce / ou espace ou quoi?


Si nous avons répondu à votre question, pourriez-vous accepter une réponse / un vote favorable comme indiqué dans: stackoverflow.com/help/someone-answers


8 Réponses :


1
votes

Le mieux que je puisse dire à partir de votre question, tout ce dont vous avez besoin est:

$ awk '$1==$3' file
s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0

Si ce n'est pas tout ce dont vous avez besoin, mettez à jour votre question pour fournir un échantillon d'entrée / sortie plus représentatif.


0 commentaires

1
votes

[@BenjaminW. a fait remarquer à juste titre que j'ai légèrement mal interprété la question. Ma réponse est laissée ci-dessous pour référence mais je la retire comme réponse candidate à la question.]

Cela fait ce que vous voulez:

sort input_file | uniq -d

Le tri trie le contenu du fichier d'entrée de sorte qu'une fois triées, les lignes identiques apparaissent les unes à côté des autres. La commande uniq réduisait généralement les lignes répétées, mais lorsqu'elle est appelée avec l'option -d , elle n'imprime que les lignes répétées.

Bien sûr, ma solution n'est acceptable que si l'utilisation de sed n'est pas obligatoire.


1 commentaires

Je pense que la question est d’identifier les lignes qui contiennent des chaînes dupliquées, et non les lignes qui sont des doublons d’autres lignes.



0
votes

Vous pouvez utiliser awk pour le faire:

awk '{for(i=1;i<NF;i++)for(j=i+1;j<=NF;j++)if($i==$j)next; print}'

Ce n'est pas limité à 3 colonnes, et peu importe où se produit le doublon.

Si vous voulez l'inverse, imprimez les lignes sans doublons:

awk '{for(i=1;i<NF;i++)for(j=i+1;j<=NF;j++)if($i==$j){print;next}}' input_file


0 commentaires

1
votes

Cela pourrait fonctionner pour vous (GNU sed):

sed -E 'h;s/\s*(\S+)\s*/\n\1\n/g;/(\n[^\n]+\n).*\1/!d;g' file

Faites une copie de la ligne courante dans l'espace d'attente.

Remplacez tout espace par des nouvelles lignes soit- côté des chaînes non espacées.

Supprimez la ligne frelatée s'il n'y a pas de doublons.

Sinon, remplacez l'espace du motif par la copie de la ligne d'origine de l'espace d'attente et imprimez.


0 commentaires

2
votes

Ajout de solutions GENERIC avec seulement 1 boucle. Donc, cela cherchera si 2 champs sont identiques sur la ligne complète (pratique au cas où vous NE voudriez PAS coder en dur le nombre de champs).

awk '                           ##Starting awk program here.
{                               ##Starting main BLOCK here.
  delete a
  for(i=1;i<=NF;i++){           ##Starting a for loop which runs from i=1 to till value of NF here, where NF is out of the box variable of awk.
    if(++a[$i]>1){              ##Checking condition if value of array a whose index is $1 is greater than 1 here, if yes then run following.
      print                     ##Printing current line now, as per OP if 2 fields are equal line should be printed.
      next                      ##Using next keyword for skipping all further statements and skipping basically for loop to save time if a match is found then NO need to run it further.
    }                           ##Closing BLOCK for if condition.
  }                             ##Closing BLOCK for fopr loop here.
}                               ##Closing main BLOCK here.
'   Input_file                  ##Mentioning Input_file name here.

Avec vos exemples, la sortie sera la suivante.

s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0

Explication : Ajout d'une explication détaillée pour le code ci-dessus maintenant.

awk '{delete a;for(i=1;i<=NF;i++){if(++a[$i]>1){print;next}}}'  Input_file


5 commentaires

@Raj KP, pourriez-vous s'il vous plaît vérifier ma solution GENERIC une fois et me faire savoir si cela vous aide.


Belle utilisation du tableau associatif ;-)


En fait, votre solution ne fonctionne pas ... Parce que vous ne supprimez pas le contenu du tableau associatif après print et avant next ... Vous devez ajouter un delete a . Si vous essayez d'exécuter votre commande sur mon entrée , elle imprimera trop ...


@Allan, merci Allan et très belle prise, je l'ai fait au début (j'écrivais d'abord une autre solution) puis plus tard j'ai oublié, j'ai apprécié votre aide ici, bravo mon pote :)


En fait, vous pouvez le condenser davantage avec la condition if (a [$ i] ++) . Bonne réflexion cependant.




2
votes

Avec grep si -P est disponible ou avec perl

$ cat ip.txt
s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u1
s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
2.5 42 32.5 abc
3.14 3.14 123
part cop par

$ grep -P '(?<!\S)(\S++).*(?<!\S)\1(?!\S)' ip.txt
s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
3.14 3.14 123

$ perl -ne 'print if /(?<!\S)(\S++).*(?<!\S)\1(?!\S)/' ip.txt
s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
3.14 3.14 123
  • (? assertion pour aucun caractère autre qu'un espace
  • (\ S ++) capture tous les caractères non blancs, le quantificateur possessif garantit que les champs partiels ne correspondent pas
  • . * n'importe quel nombre de caractères intermédiaires
  • (? correspond au champ entier, courtoisie assertions de recherche pour les caractères autres que des espaces


0 commentaires

2
votes

Utilisation de Perl - regex et backreference

$ perl -lane ' %k=/(\S+)(?<=(.))/g ; print if scalar(@F) != scalar(keys %k) ' input
s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
a b b
a a

$

Merci @Sundeep pour avoir découvert la capture subtile et @zdim pour avoir aidé à la corriger

avec les entrées ci-dessous p >

$ cat  input
a b c
s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u1
s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
1 2 3
a b c
a b b
a a
1
2.5 42 32.5 abc
part cop par
spar cop par

$ perl -nle ' print if /(?:^|\s)(\S+)\s+.*?(?<=\s)\1(?:\s+|$)/ms ' input
s1/s2/s3/s4/s5/u0 a1_b2_c3_d4_e5_f6_g7 s1/s2/s3/s4/s5/u0
a b b
a a

$

Une autre méthode utilisant hash / lookbehind

perl -nle ' print if /(?:^|\s)(\S+)\s+.*?(?<=\s)\1(?:\s+|$)/ms ' file


0 commentaires