gawk inplace und stdout

9

Ist es möglich , zu verwenden , gawks‘ -i inplaceOption und auch drucken Dinge stdout?

Wenn ich beispielsweise eine Datei aktualisieren wollte und Änderungen vorgenommen wurden, den Namen der Datei und die geänderten Zeilen zu drucken, stderrkönnte ich so etwas tun

find -type f -name 'myfiles' -exec gawk -i inplace '{if(gsub(/pat/, "repl")) { print FILENAME > "/proc/self/fd/2" ; print > "/proc/self/fd/2"; } print;}' {} +

Aber gibt es eine Möglichkeit, stdoutdiesen Block zu verwenden , oder eine sauberere Möglichkeit, diesen Block in den alternativen Stream zu drucken?

Eric Renouf
quelle
2
In diesem speziellen Fall möchten Sie möglicherweise so etwas ausführen. find -type f -name 'myfiles' -exec grep -q 'pattern' {} \; -print -exec gawk -i inplace '{do_your_sub_and_print_to_dev/stderr_too}' {} \;Sie verwenden awk nur, um die Dateien zu bearbeiten, die tatsächlich Linien enthalten, die diesem Muster entsprechen.
don_crissti
@don_crissti Ich vergesse oft, wie viel schneller sein grepkann. Mein erster Instinkt ist, zu vermeiden, dass die Datei zweimal "verarbeitet" werden muss, aber ich wäre überhaupt nicht überrascht, wenn sie tatsächlich schneller ist. Es zeigt nur, warum ich bei Profilierungsaufgaben meinem Bauch vertrauen kann
Eric Renouf
1
Ja, es ist ziemlich schnell und bedenken Sie Folgendes: 1) Wenn keine Übereinstimmung vorliegt, verarbeiten Sie die Datei nur einmal (der Teil mit -print -exec gawkwird nicht mehr ausgeführt) und 2) sie stoppt bei der ersten Übereinstimmung, es sei denn, die erste Übereinstimmung ist auf der In der letzten Zeile verarbeiten Sie immer noch nicht die gesamte Datei zweimal (es ist eher 1.X-mal). Wenn es so gawk -i inplacefunktioniert sed -i, wird die Datei trotzdem an Ort und Stelle bearbeitet - dh Zeitstempel, Inode usw. werden aktualisiert, auch wenn nichts zu bearbeiten war ...
don_crissti

Antworten:

13

Sie sollten /dev/stderroder /dev/fd/2anstelle von verwenden /proc/self/fd/2. gawkHandles /dev/fd/xund /dev/stderrfür sich (unabhängig davon, ob das System diese Dateien hat oder nicht).

Wenn Sie eine:

print "x" > "/dev/fd/2"

gawktut eine write(2, "x\n"), während, wenn Sie tun:

print "x" > "/proc/self/fd/2"

da es nicht /proc/self/fd/xspeziell behandelt , tut es ein:

fd = open("/proc/self/fd/2", O_WRONLY|O_CREAT|O_TRUNC);
write(fd, "x\n");

Erstens /proc/self/fdist Linux spezifisch und unter Linux sind sie problematisch. Die beiden oben genannten Versionen sind nicht gleichwertig, wenn stderr einer regulären oder anderen durchsuchbaren Datei oder einem Socket (für den letzterer fehlschlagen würde) entspricht (ganz zu schweigen davon, dass ein Dateideskriptor verschwendet wird).

Davon abgesehen, wenn Sie auf das ursprüngliche Standard schreiben müssen, müssen Sie es in einem anderen Feld speichern, wie:

gawk -i inplace '{
   print "goes into the-file"
   print "to stdout" > "/dev/fd/3"}' the-file 3>&1

gawkleitet stdout mit in-place in die Datei um. Es wird benötigt, weil Sie zum Beispiel möchten:

awk -i inplace '{system("uname")}' file

um die unameAusgabe in der zu speichern file.

Stéphane Chazelas
quelle
1

Just for fun, ein alternativer Ansatz nur POSIX mit Funktionen von find, sh, grepund (vor allem) ex:

find . -type f -name 'myfiles' -exec sh -c '
  for f do grep -q "pat" "$f" &&
    { printf "%s\n" "$f";
      printf "%s\n" "%s/pat/repl/g" x | ex "$f";
  }; done' find-sh {} +

(Alle Zeilenumbrüche im obigen Befehl sind optional; sie können zu einem Einzeiler zusammengefasst werden.)

Oder wohl leserlicher:

find . -type f -name 'myfiles' -exec sh -c '
  for f
  do
    if grep -q "pat" "$f"; then
      printf "%s\n" "$f"
      printf "%s\n" "%s/pat/repl/g" x | ex "$f"
    fi
  done' find-sh {} +

:) :)


(Dieser Befehl ist nicht getestet. Änderungen sind willkommen, wenn ich irgendwelche Fehler gemacht habe.)

Platzhalter
quelle