The grep solution:
{ make all 2>&1 1>&3 | grep -v 'No rule to make target `all' >&2; } 3>&1
The construct 2>&1 1>&3
sends make's stdout to fd 3 and make's stderr to stdout. grep then reads from the previous command's stdout, removes the offending line and sends its stdout to stderr. Finally, fd 3 is returned to stdout.
2022-11-17, A response to @Pryftan's comment:
Ignoring the minor error that I used the wrong text.
Lets create a function that outputs some stuff
make() {
echo "this is stdout"
echo "this is stderr" >&2
printf 'oops, No rule to make target `%s`, not at all' "$1" >&2
}
Testing my solution:
$ { make foobar 2>&1 1>&3 | grep -v 'No rule to make target `all' >&2; } 3>&1
this is stdout
this is stderr
oops, No rule to make target `foobar`, not at all
$ { make all 2>&1 1>&3 | grep -v 'No rule to make target `all' >&2; } 3>&1
this is stdout
this is stderr
Looks good so far.
What about without the braces?
$ make all 2>&1 1>&3 | grep -v 'No rule to make target `all' >&2 3>&1
bash: 3: Bad file descriptor
In this case, we'd need to explicitly create fd 3
$ exec 3>&1; make all 2>&1 1>&3 | grep -v 'No rule to make target `all' >&2 3>&1
this is stdout
this is stderr
What is it about the braces? I think it's delaying evaluation of the contents, and that allows the trailing 3>&1
to be processed first. And that makes the inner 1>&3
valid.