nowox
nowox

Reputation: 29096

How to capture stdout, stderr and $? temporarily in bash

I have to deal with a compiler that sometime outputs a dummy error. I don't want my build process to be stopped in that case. Instead I simply want to ignore the error and rerun the compiler:

%o: %c
    $(prefix) dumb_compiler -c $^ -o $@ $(suffix)

My goal is to define something as $prefix and $suffix to ignore the dummy error. Instead of dumb_compiler let's simply consider this dummy program:

#!/usr/bin/perl
my $i = int rand 10;
die "Error -42\n" if $i > 5;
die "Error -12\n" if $i % 2;
print "$i\n";

When Error -42 occurs, I want to discard stdout, stderr and run the program again as if nothing happened. In any other case, I want print what the program would have printed.

I would like to write the following in bash as a oneliner to put it in a Makefile recipe:

while True:
    (_exit, _stdout, _stderr) = exec(program)
    if _exit == 0 and "Error -42" not in _stderr: 
        print(stdout=_stdout, stderr=_stderr)
        exit(_exit) 

I currently wrote this, but it doesn't work and it doesn't look like a oneliner

#!/usr/bin/bash
exit_status=0
run=1
exec 3>&1
exec 4>&2
while [ $run -eq 1 ];
do
    run=0 
    ./program 2>&4 1>&3
    $exit_status=$?
    if [ $exit_status -ne 0 ]
    then
        &4 > grep -q "Error -42" 
        if [ $? -ne 0 ]:
        then
            run=1
        fi
    fi
done
3>&1
4>&2
3>&-
4>&-
exit $exit_status

My question is how can I properly hold the stdout, stderr and exit status from my program execution and flush it out once I finished?

Still I can try to make my program work, but I think my approach is wrong, that is why I prefer to ask SO.

Upvotes: 2

Views: 62

Answers (1)

chepner
chepner

Reputation: 531095

Just use files. Redirections to other file handles don't buffer anything (in memory or on disk); they aren't the solution here.

while true; do
   ./program > /tmp/output 2> /tmp/error && break
   exit_status=$?
   grep -q "Error -42" /tmp/error || break
done
cat /tmp/output
cat /tmp/error >&2
exit $exit_status

The one minor drawback to this is that standard output and standard error won't be interleaved, but that shouldn't be a big loss. They are only "properly" interleaved when they both write to the same file handle; as soon as you separate them (as is necessary if you want to look for Error -42 in standard error only), you've lost the ability to retain the relative order in which the original program produced them.

Upvotes: 3

Related Questions