1

Quite new to bash - I'm trying to store the output of my /usr/bin/time command into the TIME_INFO variable, which works with the below setup... however I would also like to be able to store the output of some of the other nested commands (such as /usr/local/bin/firejail or ./program) to other variables. Currently if there is a runtime exception in ./program it'll also go to the TIME_INFO variable.

TIME_INFO=$( /usr/bin/time --quiet -f "%e-%U-%S-%M-%x" 2>&1 \
timeout 5s \
/usr/local/bin/firejail --quiet --cgroup=/sys/fs/cgroup/memory/group1/tasks --profile=java.profile \
./program < test.in > test.out )

Is there any way to accomplish separating outputs of multiple nested commands?

Thanks in advance!

Gosre
  • 61
  • 9
  • This is certainly *easiest* with temporary files. Are you okay with their use? – Charles Duffy Oct 14 '17 at 15:15
  • Yeah, I'm only using the data to make a summary of information for each process that the script runs so that'd be fine. – Gosre Oct 14 '17 at 15:17
  • See [capture stdout and stderr in different variables](https://stackoverflow.com/questions/11027679/store-capture-stdout-and-stderr-in-different-variables-bash) for an idea of what a closely related operation looks like *without* temporary files in use. – Charles Duffy Oct 14 '17 at 15:17
  • If you aren't looking for the errors , doing `2>&1` inside the `$(...)` is probably not a good idea. – Mad Physicist Oct 14 '17 at 15:25
  • @MadPhysicist Sorry, I was a bit unclear - the time command outputs to stderr by default according to the man page. – Gosre Oct 14 '17 at 15:28

1 Answers1

2

One way to do this is to inject a shell in the call chain and make it responsible for modifying stderr for its subprocesses:

time_info=$( /usr/bin/time --quiet -f "%e-%U-%S-%M-%x" 2>&1 \
  sh -c '"$@" 2>"$0"' test.err \
    timeout 5s \
      /usr/local/bin/firejail \
          --quiet --cgroup=/sys/fs/cgroup/memory/group1/tasks --profile=java.profile \
        ./program < test.in > test.out )

# read your content back into a shell variable
error_text=$(<test.err)

The pertinent change here is sh -c '"$@" 2>"$0", which runs its arguments as a command, with stderr redirected to the filename passed in $0 -- which is populated from the string immediately following code passed with sh -c.

Note that I modified the case of the TIME_INFO variable per POSIX guidance specifying all-caps names for variables with meaning to the shell or OS, and reserving names with at least one lower-case character for other purposes.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Could you just leave out stderr entirely ? – Mad Physicist Oct 14 '17 at 15:26
  • @MadPhysicist, sure -- if you used `/dev/null` in place of `test.err`, it would have that effect. I'm not sure that's the OP's intent, though. (Can't discard stderr for the *entire* command because it's where the output of `time` is stored). – Charles Duffy Oct 14 '17 at 15:29
  • @MadPhysicist, ...that said, you make a point that since it's stderr from `time` that we want to keep, I should really be putting the shim earlier in the call tree. – Charles Duffy Oct 14 '17 at 15:30
  • @CharlesDuffy This works perfect, thank you! Could get a little messy if you wanted to get the output for each individual sub commannd though. Also cheers for the bit on the posix syntax. – Gosre Oct 15 '17 at 12:14