gawkインプレースとstdout


10

gawk-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ことができるかを忘れます。私の最初の本能は、ファイルを2回「処理」する必要がないようにすることですが、それでも実際に高速であるとしても、まったく驚かないでしょう。それは私がプロファイリングタスクに私の根性を信頼できる理由を示すために行きます
エリックRenouf

1
はい、非常に高速であり、次の点に注意してください:1)一致がない場合、ファイルを1回だけ処理します(の部分-print -exec gawkはもう実行されません)。2)最初の一致で停止するため、最初の一致が最後の行では、ファイル全体を2回処理していません(1.X回に近い)。また、このgawk -i inplaceようsed -iに機能する場合は、とにかくファイルをインプレース編集します。つまり、編集するものが何もない場合でも、タイムスタンプ、inodeなどを更新します...
don_crissti

回答:


13

/dev/stderrまたはの/dev/fd/2代わりにを使用してください/proc/self/fd/2gawkハンドル/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では問題があります。上記の2つのバージョンは、stderrが通常のファイルや他のシーク可能なファイル、またはソケット(後者の場合は失敗します)に対して(ファイル記述子を浪費することは言うまでもありません)、同等ではありません。

つまり、元の標準出力に書き込む必要がある場合は、次のように別のfdに保存する必要があります。

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

gawkstdoutをインプレースでファイルにリダイレクトします。たとえば、次のようにしたいために必要です。

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

uname出力をに保存しますfile


2

ちょうど楽しみのために、唯一のPOSIXを使用して別のアプローチが特徴のfindshgrepおよび(特に)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 {} +

(上記のコマンドの改行はすべてオプションです。1行にまとめることができます。)

または、おそらくより読みやすい:

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

:)


(このコマンドはテストされていません。何か間違えた場合は編集を歓迎します。)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.