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 $n
où n
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 estfoo
. 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 quiperl
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 à chaqueprint
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