1

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.

onlycparra
  • 607
  • 4
  • 22
  • You want to take a look at the `tee` utility. – arkascha Dec 11 '22 at 10:09
  • I am familiar with `tee`, I perhaps lack the imagination on how to use it in this case. I am comfortable with it when I am dealing with pipes and `&&`/`||` logic. But here I don't see how to use it. – onlycparra Dec 11 '22 at 10:13
  • You can't run a command in single quotes directly like `'command args'` but you can run `sh -c 'command args'`. Of course, the explicit shell adds a new process, which you wanted to avoid. – tripleee Dec 11 '22 at 10:35
  • Please [edit] your question to provide a [mcve] including a command you will run (and that we can also run for testing purposes) with the stderr/stdout not redirected so we can see where the issues are with that. – Ed Morton Dec 11 '22 at 12:33

2 Answers2

1

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
mirage
  • 632
  • 10
  • 21
0

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.

Philippe
  • 20,025
  • 2
  • 23
  • 32
  • Could you please tell me what is that underscore notation? When I run it I get `_: line 1: main: command not found`. It looks like your idea is to spawn one bash instance per "nesting-level", correct? – onlycparra Dec 11 '22 at 14:24