wpatruj się w miejsce i stdout

10

Czy można skorzystać gawkz -i inplaceopcji i wydrukować coś stdout?

Na przykład, jeśli chciałbym zaktualizować plik i jeśli są jakieś zmiany, wydrukuj nazwę pliku i zmienione wiersze stderr, mógłbym zrobić coś takiego

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;}' {} +

ale czy istnieje stdoutzamiast tego sposób użycia lub czystszy sposób wydrukowania tego bloku do alternatywnego strumienia?

Eric Renouf
źródło
2
W tym konkretnym przypadku możesz chcieć uruchomić coś w find -type f -name 'myfiles' -exec grep -q 'pattern' {} \; -print -exec gawk -i inplace '{do_your_sub_and_print_to_dev/stderr_too}' {} \;ten sposób, będziesz używał awk tylko do edycji plików, które faktycznie zawierają linie pasujące do tego wzorca.
don_crissti
@don_crissti Często zapominam, jak szybko grepmoże być. Moim pierwszym instynktem jest uniknięcie konieczności dwukrotnego „przetwarzania” pliku, ale nie byłbym wcale zaskoczony, gdyby mimo to był on szybszy. To po prostu pokazuje, dlaczego mogę ufać mojej odwadze w zadaniach profilowania
Eric Renouf,
1
Tak, jest dość szybki i pamiętaj, że: 1) jeśli nie ma dopasowania, plik jest przetwarzany tylko raz (część z -print -exec gawknie jest już wykonywana) i 2) zatrzyma się przy 1. meczu, więc chyba, że ​​1. mecz jest w w ostatnim wierszu nadal nie przetwarzasz całego pliku dwa razy (jest to więcej niż 1.X razy). Ponadto, jeśli gawk -i inplacedziała tak, sed -ito i tak edytuje plik na miejscu - tj. Aktualizuje znaczniki czasu, i-węzeł itp., Nawet jeśli nie było nic do edycji ...
don_crissti

Odpowiedzi:

13

Powinieneś użyć /dev/stderrlub /dev/fd/2zamiast /proc/self/fd/2. gawkobsługuje /dev/fd/xi /dev/stderrsam (niezależnie od tego, czy system ma te pliki, czy nie).

Kiedy wykonujesz:

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

gawkrobi a write(2, "x\n"), kiedy to robisz:

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

ponieważ nie traktuje /proc/self/fd/xspecjalnie, robi:

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

Pierwszy /proc/self/fdjest specyficzny dla Linuksa, a na Linuksie są problematyczne. Dwie powyższe wersje nie są równoważne, gdy stderr znajduje się w zwykłym lub innym możliwym do przejrzenia pliku lub w gnieździe (dla którego ten ostatni zawiódłby) (nie wspominając o tym, że marnuje deskryptor pliku).

Biorąc to pod uwagę, jeśli chcesz napisać do oryginalnego standardowego interfejsu, musisz zapisać go w innym fd, takim jak:

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

gawkprzekierowuje standardowe wyjście z plikiem na miejsce. Jest to potrzebne, ponieważ na przykład chcesz:

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

do przechowywania danych unamewyjściowych w file.

Stéphane Chazelas
źródło
2

Tylko dla zabawy, alternatywnym podejściem używając tylko POSIX funkcje systemu find, sh, grepi (przede wszystkim) 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 {} +

(Wszystkie podziały wiersza w powyższym poleceniu są opcjonalne; można je skondensować do jednej linii).

Lub, prawdopodobnie bardziej czytelnie:

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 {} +

:)


(To polecenie jest nieprzetestowane; edycje są mile widziane, jeśli zrobiłem jakieś błędy.)

Dzika karta
źródło