Those codes modify files in place (well, would be for the perl one if -p
was used in place of -n
; with -n
, that results in files being emptied) and at the same time print to stderr (via w /dev/fd/2
or print STDERR
) what was modified.
However find . | xargs
is wrong as the output of find
is not compatible with the input format expected by xargs
. Also, in perl
, $.
which contains the current input line number is not reset between files unless you close the ARGV
handle at the end of each file so it should be:
find . -name '*.myfile' -exec perl -i -pe '
chomp($was = $_);
print STDERR "$ARGV($.): $was : $_" if s/abc/def/;
close ARGV if eof' -- {} +
For sed
, not sure macos supports /dev/fd/2
to mean stderr, but even on Linux-based systems (macos is not Linux btw), using w /dev/fd/2
would be wrong unless stderr goes to specific types of files such as pipes or terminal devices. -printf
is specific to the GNU implementation of find
and is not found in any other implementation including macos'.
Also on macos' sed
like on FreeBSD's on which it is based, you need -i ''
instead of -i
for in-place editing. sed
has =
to report the line number but you can't make any manipulation on it like embed it in a string. It's also not reset between files in POSIX-compliant sed
implementations (in the case of GNU sed
, it's reset when using the -i
or -s
non-standard options).
Note that it will edit (and replace) every .myfile
file even those that don't contain abc
. You could work around that with:
find . -name '*.myfile' -type f -exec grep -l --null abc {} + |
xargs -0 perl -i -pe '
chomp($was = $_);
print STDERR "$ARGV($.): $was : $_" if s/abc/def/;
close ARGV if eof' --
The grep
on macos has an API based on some old version of GNU grep
(as it used to have GNU grep
or at least FreeBSD on which it is based used to) so you'll find some GNU options in there including that --null
(though not the -Z
short variant). Its xargs
also has GNU's -0
option but unfortunately not the -r
option (yet), so you'll get an error by perl
if find
doesn't find any file.
If you don't want to modify the files in place and just print the matches and how they would be substituted, then that becomes:
find . -name '*.myfile' -type f -exec perl -nle '
$was = $_;
print "$ARGV($.): $was : $_" if s/abc/def/;
close ARGV if eof' -- {} +
No -i
nplace, -n
to n
ot p
rint the line at the end of each cycle; -l
does the chomp
on $_
automatically (and print
is told to add a \n
on output). We print on stdout instead of stderr (redirect the whole command to stderr with >&2
is you want it to go to stderr instead; at the prompt of an interactive shell in a terminal, both stdout and stderr should go to the terminal so it shouldn't make a difference).