gawk inplace and stdout

You should use /dev/stderr or /dev/fd/2 instead of /proc/self/fd/2. gawk handles /dev/fd/x and /dev/stderr by itself (regardless of whether the system has those files or not).

When you do a:

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

gawk does a write(2, "x\n"), while when you do:

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

since it doesn't treat /proc/self/fd/x specially, it does a:

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

First /proc/self/fd is Linux specific and on Linux they are problematic. The two versions above are not equivalent when stderr is to a regular or other seekable file or to a socket (for which the latter would fail) (not to mention that it wastes a file descriptor).

That being said, if you need to write to the original stdout, you need to save it away in another fd like:

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

gawk does redirect stdout with in-place to the file. It's needed because for instance, you'd want:

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

to store the uname output into the file.


Just for fun, an alternate approach using only POSIX features of find, sh, grep and (most notably) 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 {} +

(All line breaks in the above command are optional; it can be condensed to a one-liner.)

Or, arguably more legibly:

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

:)


(This command is untested; edits welcome if I made any goofs.)

Tags:

Awk

Gawk