поглазеть на месте и стандартный вывод

10

Можно ли использовать gawk«s -i inplaceварианта , а также печатать вещи stdout?

Например, если я хочу обновить файл, и если есть какие-либо изменения, напечатайте имя файла и измененные строки, чтобы stderrя мог сделать что-то вроде

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

но есть ли способ использовать stdoutвместо этого или более чистый способ печати этого блока в альтернативный поток?

Эрик Ренуф
источник
2
В этом конкретном случае вы можете захотеть запустить что-то подобное find -type f -name 'myfiles' -exec grep -q 'pattern' {} \; -print -exec gawk -i inplace '{do_your_sub_and_print_to_dev/stderr_too}' {} \;, вы будете использовать awk только для редактирования файлов, которые на самом деле содержат строки, соответствующие этому шаблону.
don_crissti
@don_crissti Я часто забываю, насколько быстрее это grepможет быть. Мой первый инстинкт - не обрабатывать файл дважды, но я не удивлюсь, если он действительно быстрее. Это просто показывает, почему я могу доверять своей интуиции в задачах профилирования
Эрик Ренуф
1
Да, это довольно быстро, и имейте в виду, что: 1) если совпадения нет, вы обрабатываете файл только один раз (часть с -print -exec gawkбольше не выполняется) и 2) он остановится на первом совпадении, поэтому, если первое совпадение не будет В последней строке вы все еще не обрабатываете весь файл дважды (это больше похоже на 1X раз). Кроме того, если он gawk -i inplaceработает как-то, sed -iон все равно будет редактировать файл на месте - т.е. обновлять метки времени, inode и т. Д., Даже если редактировать было нечего ...
don_crissti

Ответы:

13

Вы должны использовать /dev/stderrили /dev/fd/2вместо /proc/self/fd/2. gawkобрабатывает /dev/fd/xи /dev/stderrсам по себе (независимо от того, есть ли в системе эти файлы или нет).

Когда вы делаете:

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

gawkделает write(2, "x\n"), а когда вы делаете:

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

так как он не обрабатывает /proc/self/fd/xспециально, он делает:

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

Во-первых, /proc/self/fdэто специфично для Linux, а в Linux они проблематичны. Две вышеупомянутые версии не эквивалентны, когда stderr является обычным или другим доступным для поиска файлом или сокетом (для которого последний не сможет работать) (не говоря уже о том, что он использует файловый дескриптор).

При этом, если вам нужно записать в исходный стандартный вывод, вам нужно сохранить его в другом файле, например:

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

gawkперенаправляет стандартный вывод с файлом на место Это необходимо, потому что, например, вы хотите:

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

сохранить unameвывод в file.

Стефан Шазелас
источник
2

Просто для удовольствия, альтернативный подход , использующий только POSIX черты find, sh, grepи (самое главное) 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 {} +

(Все разрывы строк в приведенной выше команде являются необязательными; они могут быть сжаты до одной строки.)

Или, возможно, более разборчиво:

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

:)


(Эта команда не проверена; изменения приветствуются, если я сделал какие-то глупости.)

Wildcard
источник