2
votes

Comment assembler plusieurs fichiers dans AWK?

J'essaie de rassembler une série de fichiers journaux .csv nommés par date (par exemple, 2019-02-24.csv ). Il y en a beaucoup, alors j'essaie de scénariser le processus. J'ai créé un script AWK qui combine des fichiers individuels:

$ for i in {01..02}; do "awk ' FNR==1 { while (/\"_time\",PIN,FULLNAME,OFFCODE,Acronym,Name/) getline; } 1 { print } ' 2019-01-$i.csv >> user_history.csv"; done
bash: awk ' FNR==1 { while (/"_time",PIN,FULLNAME,OFFCODE,Acronym,Name/) getline; } 1 { print } ' 2019-01-01.csv >> user_history.csv: No such file or directory
bash: awk ' FNR==1 { while (/"_time",PIN,FULLNAME,OFFCODE,Acronym,Name/) getline; } 1 { print } ' 2019-01-02.csv >> user_history.csv: No such file or directory

Mais j'échoue lorsque j'essaie de chaîner les commandes AWK ensemble avec une boucle de contrôle en BASH:

bash: awk ' FNR==1 { while (/"_time",PIN,FULLNAME,OFFCODE,Acronym,Name/) getline; } 1 { print } ' 2019-01-01.csv >> user_history.csv: No such file or directory

Quand je lance ceci, il imprime les commandes correctes sur la ligne de commande, mais les scripts awk sont pas exécuté (ils ne sont imprimés). Si je l'exécute sans echo , j'obtiens des erreurs m'indiquant que le fichier n'existe pas; bien que tous les fichiers soient présents:

for i in {01..28}; do echo "awk ' FNR==1 { while (/\"_time\",PIN,FULLNAME,OFFCODE,Acronym,Name/) getline; } 1 { print } ' 2019-01-$i.csv >> user_history.csv"; done

Que me manque-t-il dans ma boucle?


Voici un exemple condensé de la commande et de l'erreur messages:

awk ' FNR==1 { while (/"_time",PIN,FULLNAME,OFFICE,Acronym,Name/) getline; } 1 { print } ' 2019-01-01.csv >> usage_history.csv


2 commentaires

C'est bien que vous ayez montré ce que vous avez essayé de résoudre ce problème. Pourriez-vous s'il vous plaît poster un échantillon d'entrée et un échantillon de sortie dans votre message dans les balises de code et nous le faire savoir.


@ double-beep a changé le titre en "Comment assembler plusieurs fichiers dans AWK", espérons-le un peu mieux. Cela avait l'air un peu vague lorsque je me suis éloigné, désolé.


3 Réponses :


2
votes

Essayez ceci:

awk '!/"_time",PIN,FULLNAME,OFFCODE,Acronym,Name/' 2019-01-[0-3][0-9].csv >>user_history.csv

Les commandes après do ne doivent pas être entre guillemets.
Et ce que vous faisiez équivaut essentiellement à ignorer les lignes de titre.
Le {print} après 1 est inutile - un seul 1 implique {print} . Le 1 consiste à fournir un true .
- Lorsqu'il n'y a qu'une expression mais pas de bloc, le bloc implique de {imprimer}.
- Et seule une expression rationnelle vaut $ 0 ~ / regex / , et ici je l'ai annulée.

S'il n'y a pas d'autre commande dans la boucle, vous pouvez simplifier la boucle avec une awk commande:

awk '!/"_time",PIN,FULLNAME,OFFCODE,Acronym,Name/' 2019-01-{01..28}.csv >>user_history.csv

Mais celle-ci lancera une erreur et cessera de s'exécuter lorsqu'un des fichiers n'existait pas.

Une autre façon est:

for i in {01..28}; do awk '!/"_time",PIN,FULLNAME,OFFCODE,Acronym,Name/' 2019-01-$i.csv >>user_history.csv;done

Celui-ci ne correspondra qu'aux noms de fichiers, au lieu de les boucler.
Il n'arrêtera pas de s'exécuter ni ne lancera d'erreur, donc s'il manque un fichier, vous ne le saurez pas. Et il correspondra aux fichiers supplémentaires, le cas échéant.
Par exemple, il lira 2019-01-34.csv s'il existe.

Donc, si vous voulez les avertissements (les avertissements n'affecteront pas les résultats), mais que vous ne voulez pas que les commandes s'arrêtent, alors utilisez le premier pour boucle un. p>

Pièges:
[0-3] [1-9] ne correspondra pas à 10 , 20 et 30 , mais associez 32 à 39 .
[0-9] * correspondra à tout numéro plus long, mais avec 20 à 29 avant 3 ou de même, c'est l'ordre des chaînes.


3 commentaires

Parfait; merci @Tiw. Donc, je gâchais mes commandes awk en les mettant entre guillemets. Je l'ai. Je vous remercie.


J'ai décoché votre réponse par inadvertance pendant que je cliquais sur cette page. Mes excuses - aucune intention légère; vous et RavinderSingh13 avez joué un rôle déterminant pour m'aider.


@AFK a mis à jour la réponse pour vous montrer les avantages et les inconvénients de chaque manière. Puisque vous faites déjà de bons progrès, je suppose que vous en connaissez déjà certains ou tous, de toute façon je pense que c'est bien de les ajouter, du moins c'est bon pour les futurs lecteurs :)



2
votes

Pourriez-vous s'il vous plaît essayer ci-dessous.

awk '........' 2019-01-{01..29}.csv
awk: cannot open 2019-01-02.csv (No such file or directory)

Voici les points pourquoi on pourrait utiliser cette approche:

1- Utilisation de la boucle for et appel de la commande awk en cela à chaque fois sera exagéré. Nous devrions utiliser une approche intelligente lorsque awk pourrait lire plusieurs fichiers, alors nous devrions le poursuivre.

2- Vient maintenant la partie getline que vous avez essayée dans votre code, donc si nous voulons annuler une chaîne, annulez-la simplement en utilisant ! / string_to_be_skipped / afin qu'il ne recherche que les lignes qui n'ont PAS cette chaîne.

3 - En mentionnant le fichier (plusieurs fichiers) à une seule commande awk , j'ai utilisé 2019-01- [0-9] *. Csv pourquoi parce que puisque vous n'avez PAS dit si les fichiers le seront être créé quotidiennement ou pas si nous lui donnons un style de boucle et que ce fichier spécifique n'est PAS présent, nous obtiendrons une erreur. Pour un exemple, disons que j'utilise la commande awk suivante où j'ai intentionnellement supprimé le fichier nommé ( 2019-01-02.csv ).

awk '!/"_time",PIN,FULLNAME,OFFCODE,Acronym,Name/' 2019-01-[0-9]*.csv >> user_history.csv

Donc éviter ce genre de situations J'ai utilisé 2019-01- [0-9] *. csv où il ne recherchera que les fichiers qui ont des chiffres après 2019-01-0 et ne fonctionnera PAS en boucle et nous nous plaindrons du manque d'un fichier xyz etc.


6 commentaires

est-ce que mettre les nombres «jour» entre crochets garantit que les fichiers seront ramassés dans l'ordre? À l'origine, j'ai utilisé des accolades avec les points de suspension partiels {01..09} parce que j'ai compris que cela signifiait qu'il itérerait dans les fichiers dans l'ordre (par exemple, 01 puis 02 puis 03 etc. ) . Je crois que les crochets [01-09] signifieraient que n'importe quel nombre correspondra, non?


Oui, cela signifie que n'importe quel nombre correspondra, mais donner entre parenthèses comme je l'ai montré dans mon message signifie qu'il recherchera 1 à 29, comme indiqué dans l'exemple. Veuillez lire mon explication une fois aussi.


Je l'ai. OUI! Cela fonctionne bien. Veuillez regarder votre premier bloc de code, je pense qu'il y a un zéro supplémentaire avant le crochet - je l'ai changé en ceci - awk '! / "_ Time", PIN, FULLNAME, OFFCODE, Acronym, Name /' 2019- 01- [0-3] [1-9] .csv >> user_history.csv


Je construis des fichiers de test pour être plus approfondi (mes fichiers ont des centaines ou des milliers d'entrées dans chaque csv; trop de défilement ...


J'ai apporté une dernière modification à votre suggestion afin que le script gère plusieurs jours, mois et années (seulement 2018 et 2019 dans ce cas). Merci @ RavinderSingh13 pour votre aide et votre conversation - c'était très instructif.


Up1 pour votre patience. Lancer une erreur ou non peut être une fonctionnalité et peut être utilisé comme moyen de débogage pour d'autres erreurs. Il vaut mieux énoncer clairement la différence: cela {01..29} lancera une erreur et arrêtera de s'exécuter alors que [0-9] * ne le fera pas mais correspondra aux fichiers supplémentaires s'ils existent. Et [0-9] * lira les fichiers dans un ordre différent. Le propre tweak de l'OP a évité le problème d'ordre de lecture.



1
votes

Merci à @Tiw et @ RavinderSingh13 pour leurs conseils. Voici le dernier script awk qui fonctionne bien pour mon cas où j'ai des fichiers quotidiens de plusieurs jours, mois et années (seulement 2018 et 2019 dans ce cas):

awk '! / "_ time", PIN, FULLNAME, OFFCODE, Acronyme, Name /' 201 [8-9] - [0-1] [0-2] - [0-3] [0-9] .csv >> histoire_utilisateur.csv


0 commentaires