2015-12-01 9 views
6

sagen, dass ich die folgende Struktur von Dateien und Verzeichnisse haben:Wie überspringe ich ein Verzeichnis in awk?

$ tree 
. 
├── a 
├── b 
└── dir 
    └── c 

1 directory, 3 files 

Das heißt, zwei Dateien a und b zusammen mit einem Verzeichnis dir, wo eine andere Datei c steht.

Ich möchte alle Dateien mit awk (GNU Awk 4.1.1, genau) verarbeiten, so mache ich so etwas wie diese:

$ gawk '{print FILENAME; nextfile}' * */* 
a 
b 
awk: cmd. line:1: warning: command line argument `dir' is a directory: skipped 
dir/c 

Alles ist in Ordnung, aber die * erweitert auch in das Verzeichnis dir und awk versucht zu verarbeiten es.

Also frage ich mich: gibt es eine native Art awk kann überprüfen, ob das angegebene Element eine Datei ist oder nicht, und wenn ja, überspringen Sie es? Das heißt, ohne dafür system() zu verwenden.

Ich machte es Arbeit der externen system in BEGINFILE Aufruf:

$ gawk 'BEGINFILE{print FILENAME; if (system(" [ ! -d " FILENAME " ]")) {print FILENAME, "is a dir, skipping"; nextfile}} ENDFILE{print FILENAME, FNR}' * */* 
a 
a 10 
a.wk 
a.wk 3 
b 
b 10 
dir 
dir is a dir, skipping 
dir/c 
dir/c 10 

Beachten Sie auch die Tatsache, dass if (system(" [ ! -d " FILENAME " ]")) {print FILENAME, "is a dir, skipping"; nextfile} Zähler intuitiv funktioniert: es 1 ist, wenn true zurückgeben sollte, aber es gibt den Exit-Code.

Ich las in A.5 Extensions in gawk Not in POSIX awk:

Und dann die verknüpfte Seite sagt:

4.11 Verzeichnisse auf der Befehlszeile

Gemäß dem POSIX-Standard müssen Dateien, die in der awk-Befehlszeile benannt sind, Textdateien sein. Es ist ein fataler Fehler, wenn sie es nicht sind. Die meisten Versionen von awk behandeln ein Verzeichnis in der Befehlszeile als schwerwiegender Fehler.

Standardmäßig erzeugt gawk eine Warnung für ein Verzeichnis im Befehl Zeile, ignoriert es aber ansonsten. Dies macht es einfacher Shell Platzhalter mit Ihrem awk-Programm verwenden:

$ gawk -f whizprog.awk *  Directories could kill this program 

Wenn eine der --posix oder --traditional Optionen gegeben, so kehrt glotzt in ein Verzeichnis auf der Kommandozeile als eine Behandlung fataler Fehler.

Siehe Extension Sample Readdir, für eine Möglichkeit zur Behandlung von Verzeichnissen als verwendbar Daten aus einem awk-Programm.

Und in der Tat ist es der Fall: der gleiche Befehl wie zuvor mit --posix fehlschlägt:

$ gawk --posix 'BEGINFILE{print FILENAME; if (system(" [ ! -d " FILENAME " ]")) {print FILENAME, "is a dir, skipping"; nextfile}} ENDFILE{print FILENAME, NR}' * */* 
gawk: cmd. line:1: fatal: cannot open file `dir' for reading (Is a directory) 

ich den 16.7.6 Reading Directories Abschnitt geprüft, die oben verbunden ist, und sie sprechen über readdir:

Die Erweiterung readdir fügt einen Eingabe-Parser für Verzeichnisse hinzu. Die Verwendung ist wie folgt:

@load „readdir“

Aber ich bin nicht sicher, weder, wie man es nennen, noch wie es von der Kommandozeile zu verwenden.

Antwort

2

Wenn Sie Ihr Skript von anderen Menschen irrtümlich sichern wollten ein Verzeichnis vorbei (oder irgendetwas anderes, das ist nicht eine lesbare Textdatei), um es, könnten Sie Dazu:

$ ls -F tmp 
bar dir/ foo 

$ cat tmp/foo 
line 1 

$ cat tmp/bar 
line 1 
line 2 

$ cat tmp/dir 
cat: tmp/dir: Is a directory 

$ cat tst.awk 
BEGIN { 
    for (i=1;i<ARGC;i++) { 
     if ((getline line < ARGV[i]) <= 0) { 
      print "Skipping:", ARGV[i], ERRNO 
      delete ARGV[i] 
     } 
     close(ARGV[i]) 
    } 
} 
{ print FILENAME, $0 } 

$ awk -f tst.awk tmp/* 
Skipping: tmp/dir Is a directory 
tmp/bar line 1 
tmp/bar line 2 
tmp/foo line 1 

$ awk --posix -f tst.awk tmp/* 
Skipping: tmp/dir 
tmp/bar line 1 
tmp/bar line 2 
tmp/foo line 1 

Per POSIX getline kehrt -1, wenn/falls es einen Datensatz aus einer Datei (zB unlesbare Datei oder Datei nicht existiert oder die Datei ist ein Verzeichnis) abrufen fehlschlägt versuchen, die Sie gerade brauchen GNU awk, um Ihnen zu sagen, welche dieser Fehler war es durch den Wert von ERRNO, wenn Sie sich interessieren.

+2

Niiiice! Also "getline" auf einem Verzeichnis nicht direkt fehlgeschlagen, aber kann behandelt werden. – fedorqui

+0

rechts. Als ich deine Frage zum ersten Mal gelesen habe, dachte ich, du würdest versuchen, awk nach Dateien/Verzeichnissen zu suchen (sorry - kurze Aufmerksamkeitsspanne!), Aber beim erneuten Lesen sieht es so aus, als würdest du nur verhindern wollen, dass jemand das Skript mit Nicht-Datei aufruft Argumente - es ist nichts falsch daran, dies zu tun und darüber ist, wie Sie es tun. Ich habe meine Antwort aktualisiert, um ein bisschen mehr Unterstützung dafür zu sein! –

+1

Ja, genau. Es dient lediglich dazu, Warnungen zu vermeiden oder sogar Codes zu beenden, da ein Verzeichnis in einer angeblich nur-Dateien-Liste expandiert wird. Sehr interessante Antwort, von der ich sehr viel gelernt habe, danke:) – fedorqui

4

Ich würde einfach vermeiden, Verzeichnisse an awk übergeben, da sogar POSIX sagt, dass alle Dateinamen Args Textdateien sein müssen.

Sie können find verwenden zum Durchlaufen des Verzeichnisses:

find PATH -type f -exec awk 'program' {} + 
+0

Ja! Ich denke, das ist der sauberste Weg, es zu tun. Ich frage mich trotzdem, ob 'awk' das irgendwie kann. Ich habe meine Frage bearbeitet, weil ich irrtümlich 'system()' benutzt habe, also funktioniert es jetzt so, aber ich mag es trotzdem nicht, einen externen Befehl dafür aufzurufen. – fedorqui

+0

@fedorqui Ich spielte auch etwas mit '@load readdir' (nett zu wissen, danke) .. Ich kam zum selben Ergebnis, was bedeutet, dass ich mit' system() 'überprüfe, ob Dateiname ein Verzeichnis ist. Ich sehe keinen anderen Weg. – hek2mgl

+0

Danke nochmal hek! Endlich akzeptierte ich Ed Mortons Antwort, weil sie es auf ungeschickte Weise tat. Auch wenn die Empfehlung es nicht generell ist. – fedorqui