Wie wende ich dieselbe awk-Aktion auf verschiedene Dateien an?

8

Ich bin neu in awk und weiß nicht, ob es möglich ist, ein awk-Skript zu schreiben, das dies tut:

Ich habe Hunderte von Datendateien, die ich sortieren muss. Für jeden verwende ich den folgenden Einzeiler:

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

und ich bekomme die Ausgabe, die ich brauche. Ich hätte jedoch gerne ein Skript, um diese Aktion zu automatisieren, jede Datei zu übernehmen, die Aktion anzuwenden und die entsprechende sortierte Datei zu schreiben.

Ich würde Ihre Hilfe schätzen!

Nacu
quelle

Antworten:

7

Wenn Sie den awkCode ändern , kann durch einen einzelnen awkProzess und keine Shell-Schleife gelöst werden :

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

Keine Schönheit, nur unbedeutend schneller.

Erklärungen wie im Kommentar angefordert.

FNR( F ile n umber oder r ECORD) ähnelt NR( n umber oder r ECORD), aber während NReine kontinuierliche Sequenznummer aller Eingabedatensätzen ist, FNRwird auf 1 zurückgesetzt , wenn die Verarbeitung eines neuen Eingabedatei gestartet wird.

Eine gawk4.0-Alternative für das FNR==1ist das BEGINFILEspezielle Muster.

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
Mann bei der Arbeit
quelle
Danke @manatwork! Das war wunderbar. Im Gegensatz zur letzten Antwort verstehe ich nicht genau, wie dieser Einzeiler funktioniert, aber es hat funktioniert. Wenn Sie Zeit haben, würde ich Ihnen danken, wenn Sie mir erklären könnten, was zu FNR==1tun ist. =)
Nacu
12

Sie können die Dateien in einer for-Schleife anwenden:

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

Oder in einer Zeile:

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

Da Sie nicht angeben, welche Shell verwendet werden soll, verwenden Sie stattdessen den Standard, basenameindem Sie die Shell-spezifische Syntax verwenden ${file%%.tex}.

Arcege
quelle
1
Diese „Shell-spezifische Syntax“ ist in POSIX enthalten und auf praktisch jedem Unix-System verfügbar, für das noch eine Garantie besteht, und auf vielen anderen, für die dies nicht der Fall ist.
Gilles 'SO - hör auf böse zu sein'
Danke @Arcege!, Ich benutze Emacs als Shell. Obwohl Ihr Vorschlag ziemlich verständlich ist, weiß ich nicht, wie ich ihn verwenden soll. Soweit ich weiß und geübt wurde, schreibt man ein .awk- Skript, das Sie vor der Datei oder dem Ordner ausführen, auf die Sie es anwenden möchten. Habe ich recht? Ich habe das getan, aber dies scheint eine andere Art von Skript zu sein, die ich nicht verwenden kann.
Nacu
Sie können eine Shell in Emacs (<kbd> Mx </ kbd> shell) ausführen und die obigen Befehle an der Eingabeaufforderung ausführen. Oder öffnen Sie ein Terminal und führen Sie dort den Befehl aus. Es gibt zwei Möglichkeiten, Skripte anzugeben (awk, shell usw.): entweder in der Befehlszeile oder in einer Datei. Ihr awkBefehl in der Buchung verwendet das Befehlszeilenformular. Mein Befehl "eine Zeile" ist auch eine Befehlszeilenform.
Arcege
0

Alte Frage, aber da ich vor einem Jahrzehnt das letzte Mal einen Single-Core-PC gesehen habe, können Sie gnu parallel verwenden

Lösung der Shell-Erweiterung und Interpretation von Anführungszeichen

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

Verwenden Sie den richtigen Glob, um die Eingabedateien auszuwählen. Hier verwende ich {.} , um die Erweiterung aus dem Ausgabenamen zu entfernen, da ich sie anschließend anhänge

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

Wo Xist die Anzahl der Prozessoren, die Sie verwenden möchten, können Sie dennoch 1 verwenden. Dies würde Ihnen file[1-9]_sorted.texals Ausgaben geben

matrs
quelle