Home > front end >  How to redirect stdout and stderr for both commands, when one command is argument of the other
How to redirect stdout and stderr for both commands, when one command is argument of the other

Time:12-11

The problem

Some programs ('called-command') receive as part of their parameters, another program with its own parameters ('indirect-command'). Such is the case of the commands time, timeout, stdbuf, perf, and many others.

I am trying to capture stdout and stderr of the 'called' as well as the 'indirect' commands in independent files.

Conceptually speaking, I want something like this, but of course, this doesn't work as I am typing it:

/usr/bin/time -p -- { \
    timeout -k 10 7 { \
        stdbuf -oL { \
            ./main >main_out.log 2>main_err.log; \
        } \
    } 2>timeout.log; \
} 2>time.log

In other words:

  • stderr of the command time into file time.log
  • stderr of the command timeout into file timeout.log
  • let the stdout and stderr of the command stdbuf be part of timeout's out/err.
  • stdout and stderr of my program main into file main_out.log and main_err.log respectively.

What I have checked/tried

I am aware that time has the option -o OUTPUT_FILE, but that is a particular feature of time, and I would like to know a generic way in which to capture outputs/errors of commands that are parameters of other commands.

I am suspecting that perhaps this cannot be generically done, because the 'called-command' actually just receives a number of strings. It is up to the 'called-command' to interpret those strings as its own options and from some point recognize the here called ('indirect-command'). Therefore, it is not forced to treat the characters < or > in any special manner...

  • In general my internet searches land to examples related to pipes, &&, ||, or blocks such as while loops. After trying to adapt what they present, I don't think they apply here.
  • Surrounding the "child-arguments" in quotations marks (of different flavors,) fails because the "parent-command" tries to lookup the whole string as a command name. (Along the lines of 'timeout -k 10 7...' does not exist)
  • I tried parenthesis and curly brackets as suggested here How to redirect the output of the time command to a file in Linux?, but after fidgeting for a good while, I still cannot make it work.
  • tee was mentioned in the comments, but I cannot see how to use it in this case. Also, I would prefer not to pollute the results of time by adding new forks.

Thanks in advance.

CodePudding user response:

I think what you are trying to do can be done with tee and exec working together. Try the following example:

/usr/bin/time -p -- { \
    exec 2>timeout.log; \
    timeout -k 10 7 { \
        exec > >(tee -a timeout.log) 2> >(tee -a timeout.log >&2); \
        stdbuf -oL { \
            exec >main_out.log 2>main_err.log; \
            ./main; \
        } \
    } \
} 2>time.log

CodePudding user response:

I wonder if this can meet your requirements :

/usr/bin/time -p --\
    bash -c '"$@" 2>timeout.log'                _ timeout -k 10 7 \
    bash -c '"$@"'                              _ stdbuf -oL\
    bash -c '"$@" >main_out.log 2>main_err.log' _ ./main \
    2>time.log

Yes, the idea is to spawn one bash instance per "nesting-level".

I have updated main to ./main, it should work now.

The underscore is a placeholder for $0 of each bash process.

  • Related