Come applicare la stessa azione awk a file diversi?


8

Sono nuovo in awk e non so se è possibile scrivere uno script awk che faccia questo:

Ho centinaia di file di dati che devo ordinare. Per ognuno utilizzo il seguente one-liner:

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

e ottengo l'output di cui ho bisogno. Tuttavia, vorrei avere uno script per automatizzare questa azione, prendendo ogni file, applicando l'azione e scrivendo il corrispondente file ordinato.

Gradirei il tuo aiuto!

Risposte:


7

Se si modifica il awkcodice, può essere risolto con un singolo awkprocesso e nessun loop di shell:

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

Non una bellezza, solo insignificantemente più veloce.

Spiegazioni come richiesto nel commento.

FNR( f ile n umber or r ecord) è simile a NR( n umber o r ecord), ma mentre NRè un numero progressivo continuo di tutti i record di input, FNRviene reimpostato a 1 all'avvio dell'elaborazione di un nuovo file di input.

Un'alternativa gawk4.0 per il FNR==1è il BEGINFILEmodello speciale.

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

Grazie @manatwork! È stato fantastico. A differenza dell'ultima risposta, non capisco esattamente come funzioni questa linea singola, ma ha funzionato. Se hai tempo, ti ringrazierei se mi spiegassi cosa fa FNR==1. =)
Nacu,

12

È possibile applicare i file in un ciclo for:

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

O su una riga:

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

Dal momento che non si specifica quale shell, andare con il più standard basenameinvece utilizzando la sintassi specifica della shell ${file%%.tex}.


1
Quella "sintassi specifica della shell" è in POSIX ed è disponibile praticamente su ogni sistema unix che è ancora in garanzia e molti che non lo sono.
Gilles 'SO- smetti di essere malvagio' il

Grazie @Arcege !, Uso emacs come shell. Sebbene il tuo suggerimento sia abbastanza comprensibile, non so come usarlo. Per quanto ho capito e mi sono esercitato, si scrive uno script .awk che si esegue prima del file o della cartella a cui si desidera applicarlo. Ho ragione? L'ho fatto, tuttavia questo sembra un altro tipo di sceneggiatura che non so come usare.
Nacu,

Puoi eseguire una shell all'interno di emacs (<kbd> Mx </kbd> shell) ed eseguire i comandi sopra al suo interno al prompt. Oppure apri un terminale ed esegui il comando lì. Esistono due modi per specificare gli script (awk, shell, ecc.): Dalla riga di comando o in un file. Il tuo awkcomando nella pubblicazione utilizza il modulo da riga di comando; il mio comando "una riga" è anche un modulo della riga di comando.
Arcege,

0

Vecchia domanda ma dato che l'ultima volta che ho visto un personal computer single core è stato un decennio fa, puoi usare gnu parallel

Per risolvere l'espansione della shell e l'interpretazione delle citazioni

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

Utilizzare il glob corretto per selezionare i file di input. Qui sto usando {.} per estrarre l'estensione dal nome dell'output perché lo sto aggiungendo in seguito

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

dove si Xtrova il numero di processori che si desidera utilizzare, è comunque possibile utilizzare 1. Questo ti darebbe file[1-9]_sorted.texcome output

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.