GNU/Linux >> Tutoriels Linux >  >> Linux

Comment extraire/modifier des lignes dans un fichier texte dont les données sont séparées en champs ?

Comment puis-je manipuler des données basées sur des champs à partir de la ligne de commande ? Par exemple

  • Comment puis-je imprimer uniquement les lignes dont le Nième champ est foo ?
  • Comment puis-je imprimer uniquement les lignes dont le Nième champ n'est pas foo ?
  • Comment puis-je imprimer uniquement les lignes dont le Nième champ correspond à foo ?
  • Comment puis-je changer le champ N en foo ? ?

Existe-t-il une approche standard ou un ensemble d'outils facilitant la manipulation des données de terrain sur les systèmes * nix ?

Réponse acceptée :

Il existe deux approches de base que l'on peut utiliser lorsqu'on traite des champs :i) utiliser un outil qui comprend les champs; ii) utiliser une expression régulière. Des deux, le premier est généralement à la fois plus robuste et plus simple.

De nombreux outils couramment disponibles sur *nix sont soit explicitement conçus pour traiter les champs, soit dotés d'astuces astucieuses pour le faciliter.

1. Utilisez un outil qui comprend les champs

1.1 ok

L'outil classique ici est awk . Il divisera automatiquement chaque ligne d'entrée en champs (le séparateur de champs est un espace blanc par défaut mais peut être modifié à l'aide de -F flag) et les champs sont alors disponibles pour le awk script comme $nn est le numéro de champ. Le 1er champ est $1 , le deuxième $2 etc.

  • Imprimer les lignes dont le 3ème champ est foo .

    awk '$3=="foo"' file
    

    Changer le délimiteur en :

    awk -F":" '$3=="foo"' file
    

    L'action par défaut de awk est d'imprimer. Par conséquent, les commandes ci-dessus imprimeront toutes les lignes dont le 3ème champ est foo . Lors de l'utilisation de -F , vous pouvez définir des séparateurs de champs arbitraires et même utiliser des expressions régulières.

  • Comment puis-je imprimer uniquement les lignes dont le 3ème champ n'est pas foo ?

    awk '$3!="foo"' file
    
  • Comment puis-je imprimer uniquement les lignes dont le 3ème champ correspond à foo ?

    Si vous cherchez simplement des champs qui correspondent à un modèle (par exemple, foo correspond à foobar ), utilisez ~ au lieu de == :

    awk '$3~/foo/' file
    
  • Comment puis-je imprimer uniquement les lignes dont le 3ème champ ne correspond pas à foo ?

    awk '$3!~/foo/' file
    
  • Comment puis-je changer le 3ème champ en foo ?

    awk '$3="foo"' file
    

1.2 Perl

Un autre choix est perl one-liners. Comme awk, Perl est un langage de script complet mais peut également être exécuté comme un programme en ligne de commande prenant un script en entrée. Son comportement est modifié par des commutateurs de ligne de commande, dont les plus pertinents pour cette question sont :

  • -e :le script qui perl devrait s'exécuter ;
  • -n :lit le fichier d'entrée ligne par ligne ;
  • -p :affiche chaque ligne d'entrée après application du script donné par -e;
  • -l  :supprimer les nouvelles lignes de fin de chaque ligne d'entrée et ajouter une nouvelle ligne à chaque print appeler ;
  • -a :awk-mode, divise chaque ligne d'entrée dans le tableau @F;
  • -F :le séparateur de champ pour -a .

Une différence importante avec awk est-ce perl est -a switch divise les fichiers en un tableau. En Perl, les tableaux commencent à 0 et non à 1. Cela signifie que le 2ème champ est en fait $F[1] et non $F[2] . Avec tout cela à l'esprit, le perl les équivalents de ce qui précède sont :

  • Imprimer les lignes dont le 3ème champ est foo .

    perl -ane 'print if $F[2] eq "foo"' file
    

    Changer le délimiteur en :

    perl -F":" -ane 'print if $F[2] eq "foo"' file
    

    Contrairement à awk , perl ne peut pas utiliser d'expressions régulières comme délimiteurs de champ. Il doit s'agir d'un caractère ou d'une chaîne spécifique.

  • Comment puis-je imprimer uniquement les lignes dont le 3ème champ n'est pas foo ?

    perl -ane 'print unless $F[2] eq "foo"' file
    
  • Comment puis-je imprimer uniquement les lignes dont le 3ème champ correspond à foo ?

    perl -ane 'print if $F[2]=~/foo/' file
    
  • Comment puis-je imprimer uniquement les lignes dont le 3ème champ ne correspond pas à foo ?

    perl -lane 'print unless $F[2]=~/foo/' file
    
  • Comment puis-je changer le 3ème champ en foo ?

    Celui-ci est un peu plus lourd en Perl. L'approche habituelle consiste à modifier la valeur dans le @F tableau, puis imprimez le tableau. Avec de simples fichiers séparés par des espaces, c'est facile :

    perl -lane '$F[2]="foo"; print "@F"' file
    

    Avec un délimiteur différent, vous devrez join le tableau. Sinon, il sera imprimé en les séparant par des espaces :

    perl -F: -lane '$F[2]="foo"; print join ":",@F' file
    

2. Utiliser des expressions régulières

L'idée ici est d'utiliser une expression régulière ("regex" en abrégé) qui définit la position de la chaîne cible dans la ligne. Par exemple, dans un fichier dont les champs sont séparés par : , on peut trouver le 2ème champ en faisant correspondre tout jusqu'au 1er : (le 1er champ) puis en recherchant le second :

^[^:]*:[^:]*:

Cette expression régulière signifie :

  • ^ :le début de la ligne ;
  • [^] :une classe de caractères inversée. [^:] signifie "tout sauf : ";
  • *  :0 ou plus du motif précédent ;
  • : :un : littéral;

Pris ensemble, cela signifie que le premier [^:]* est le premier champ et le second est le deuxième champ. Évidemment, ce n'est pas très pratique si vous cherchez le 14ème champ mais cela peut être utile pour des choses plus simples. Alors, comment implémentons-nous cela pour manipuler nos données ? Il existe divers outils qui peuvent le faire; dans ces exemples, j'utiliserai sed mais vous pourriez faire des choses très similaires avec awk , perl ou python .

  • Comment puis-je imprimer uniquement les lignes dont le 2ème champ est foo ?

    sed -n '/^[^:]*:foo:/p' file
    

    Le -n supprime la sortie normale et le /regex/p signifie "imprimer toutes les lignes auxquelles l'expression régulière correspond.

  • Comment puis-je imprimer uniquement les lignes dont le 2ème champ n'est pas foo ?

    sed '/^[^:]*:foo:/d' file
    

    L'inverse logique de ce qui précède. Ici, le /regex/d signifie "supprimer toutes les lignes auxquelles l'expression régulière correspond.

  • Comment puis-je imprimer uniquement les lignes dont le 2ème champ correspond à foo ?

    sed -n '/^[^:]*:[^:]*foo/p' file
    
  • Comment puis-je imprimer uniquement les lignes dont le 2ème champ ne correspond pas à foo ?

    sed '/^[^:]*:[^:]*foo/d' file
    
  • Comment puis-je changer le 2ème champ en foo ?

    sed 's/([^:]*:)[^:]*/1foo/' file 
    

    Ou, depuis sed la substitution peut directement adresser une occurrence de motifs par sa répétition avec un simple indicateur numérique :

    sed 's/[^:]*/foo/2' file
    

Linux
  1. Comment supprimer les lignes en double dans un fichier texte ?

  2. Comment supprimer plusieurs lignes aléatoires d'un fichier texte à l'aide de Sed ?

  3. Que sont les modes Vim ? Comment les changer ?

  4. Transformer plusieurs lignes en une seule ligne séparée par des virgules

  5. Comment diviser un fichier texte en plusieurs fichiers *.txt ?

Script bash :comment lire des données à partir de fichiers texte

Comment imprimer des lignes dupliquées dans un fichier texte sous Linux

Comment joindre plusieurs lignes en une seule dans un fichier sous Linux

Comment extraire des adresses e-mail d'un fichier texte sous Linux

Comment importer des données dans Apache Solr

Comment faire écho dans le fichier