Comment appliquer la même action awk à différents fichiers?

8

Je suis nouveau dans awk et je ne sais pas s'il est possible d'écrire un script awk qui fait ceci:

J'ai des centaines de fichiers de données que je dois trier. Pour chacun, j'utilise le one-liner suivant:

awk 'ORS=NR%3?" ":"\n" ' file1.tex >  file1_sorted.tex
awk 'ORS=NR%3?" ":"\n" ' file2.tex >  file2_sorted.tex
...

et j'obtiens la sortie dont j'ai besoin. Cependant, j'aimerais avoir un script pour automatiser cette action, en prenant chaque fichier, en appliquant l'action et en écrivant le fichier trié correspondant.

J'apprécierais votre aide!

Nacu
la source

Réponses:

7

Si vous modifiez le awkcode, peut être résolu par un seul awkprocessus et sans boucle shell:

awk 'FNR==1{if(o)close(o);o=FILENAME;sub(/\.tex/,"_sorted.tex",o)}{ORS=FNR%3?" ":"\n";print>o}' *.tex

Pas une beauté, juste insignifiamment plus rapide.

Explications demandées en commentaire.

FNR( f ile n umber ou r ecord) est similaire à NR( n umber or r ecord), mais bien qu'il s'agisse d' NRun numéro de séquence continu de tous les enregistrements d'entrée, il FNRest réinitialisé à 1 lorsque le traitement d'un nouveau fichier d'entrée est démarré.

Une gawkalternative 4.0 uniquement pour le FNR==1est le BEGINFILEmodèle spécial.

awk '
FNR==1{   # first record of an input file?
  if(o)close(o);   # was previous output file? close it
  o=FILENAME;sub(/\.tex/,"_sorted.tex",o)   # new output file name
}
{
  ORS=FNR%3?" ":"\n";   # set ORS based on FNR (not NR as in the original code)
  print>o   # print to the current output file
}
' *.tex
homme au travail
la source
Merci @manatwork! C'était génial. Contrairement à la dernière réponse, je ne sais pas exactement comment fonctionne ce one-liner, mais il l'a fait. Si vous avez le temps, je vous remercie si vous pouviez m'expliquer ce que cela FNR==1fait. =)
Nacu
12

Vous pouvez appliquer les fichiers dans une boucle for:

for file in *.tex;
do
    awk 'ORS=NR%3?" ":"\n"' "$file" > "$(basename "$file")_sorted.tex"
done

Ou sur une seule ligne:

for file in *.tex; do awk 'ORS=NR%3?" ":"\n"' $file > "$(basename "$file" .tex)_sorted.tex"; done

Puisque vous ne spécifiez pas quel shell, optez plutôt pour le plus standard en basenameutilisant la syntaxe spécifique au shell ${file%%.tex}.

Arcege
la source
1
Cette «syntaxe spécifique au shell» est dans POSIX et est disponible sur pratiquement tous les systèmes Unix encore sous garantie, et beaucoup ne le sont pas.
Gilles 'SO- arrête d'être méchant'
Merci @Arcege !, J'utilise emacs comme shell. Bien que votre suggestion soit assez compréhensible, je ne sais pas comment l'utiliser. Pour autant que je sache et que j'ai été pratiqué, on écrit un script .awk que vous exécutez avant le fichier ou le dossier auquel vous souhaitez l'appliquer. Ai-je raison? Je l'ai fait, mais cela semble être un autre type de script que je ne sais pas utiliser.
Nacu
Vous pouvez exécuter un shell dans emacs (<kbd> Mx </kbd> shell) et exécuter les commandes ci-dessus à l'intérieur à l'invite. Ou ouvrez un terminal et exécutez-y la commande. Il existe deux façons de spécifier (awk, shell, etc.) des scripts: soit sur la ligne de commande, soit dans un fichier. Votre awkcommande dans la publication utilise le formulaire de ligne de commande; ma commande "une ligne" est également un formulaire en ligne de commande.
Arcege
0

Vieille question mais étant donné que la dernière fois que j'ai vu un ordinateur personnel monocœur, c'était il y a une décennie, vous pouvez utiliser gnu parallel

Pour résoudre l'expansion du shell et l'interprétation des citations

my_awk='ORS=NR%3?" ":"\n"' 

Utilisez le bon glob pour sélectionner les fichiers d'entrée. Ici, j'utilise {.} pour retirer l'extension du nom de sortie car je l'ajoute ensuite

parallel -jX "awk '$my_awk' {} > {.}_sorted.tex" ::: *.tex

Xest le nombre de processeurs que vous souhaitez utiliser, vous pouvez toujours en utiliser 1. Cela vous donnerait file[1-9]_sorted.texcomme sorties

matrs
la source