0

My script runs some function/external program. I need to print its output normally. Hovever, I also need to save stdout and stderr separately to two variables/files. Let's assume files beacuse that would probably be easier in a shell.

Here is a template of my script:

#!/bin/sh

print_things()
{
    echo 'Some normal output'
    echo 'Now errors' 1>&2
    echo 'Normal output again'
    echo 'And more errors' 1>&2
}

print_things | <do magic here>
<possibly a few more lines of magic>

# there will be the rest of the script

exit 0

I would like a solution that preserving division into stderr and stdout but it would still be acceptable if they get merged at some point, as long as they are saved separately. The output has to preserve order of lines so it has to look like that:

Now errors
And more errors
Some normal output
Normal output again

After execution of the "magic" there should be two files in the folder. One that contains the whole stdout and one that contains stderr.

Bonus points for preserving the exit status without saving it to a file.

I'm looking for a posix compliant solution.

Piotr Siupa
  • 3,929
  • 2
  • 29
  • 65
  • This has been asked and answered many times over. The only part that's tricky (or, rather, impossible) is routing the two descriptors separately but still keeping a firm guarantee that they're in-order. We, again, have in-depth answers already in the knowledge base describing exactly why the ordering guarentees POSIX provides are no longer applicable when your descriptors aren't simply `fdup()`s of each other. – Charles Duffy Jun 06 '18 at 19:54
  • 1
    ...now, what you *can* do is keep a syscall-level log of execution, and reconstruct the output with original ordering from that log, with tooling such as `sysdig` doing the recording. – Charles Duffy Jun 06 '18 at 19:55
  • @CharlesDuffy This is not a duplicate of that questions. I'm explicitly stated in three places that I'm looking for a Bourne shell solution. The other question are about bash. – Piotr Siupa Jun 06 '18 at 20:02
  • 1
    If it can't be done in bash, it can't be done in POSIX sh (which is not Bourne -- Bourne is a 1970s-era shell, whereas POSIX sh is a standard from the early 1990s; I haven't seen a Bourne shell outside the [Heirloom project](http://heirloom.sourceforge.net/sh.html) since Sun discontinued theirs in the mid-2000s) either. And "it can't be done" *is indeed* the answer. If you had an answer you considered adequate that worked for bash (and satisfied all your criteria), translating it to POSIX sh would be child's play. There exists no such answer, so its translation can't be done. – Charles Duffy Jun 06 '18 at 20:03
  • ...well, can't be done without syscall-tracing tools -- you'll note one of the duplicates I linked demonstrates their use. – Charles Duffy Jun 06 '18 at 20:04
  • @CharlesDuffy I honestly though that Bourne shell is a full name of sh. – Piotr Siupa Jun 06 '18 at 20:11
  • 1
    ...yeah, the GNU "Bourne Again" name, while clever, has a bit of a history of misleading folks. bash is actually a closer descendent of ksh than of Bourne (for that matter, early ksh substantially influenced the POSIX sh standard). `$(( 1 + 1 ))`, for example, is something ksh pioneered and the POSIX sh standard adopted, which Bourne never had. – Charles Duffy Jun 06 '18 at 20:12
  • @CharlesDuffy You remark about recording times gave me an idea. I could use `date` or a similar command to record time of each line in both files. I wonder if I could do it precise enough to be able to reconstruct original order from that. I need to test that. – Piotr Siupa Jun 06 '18 at 20:13
  • ...the problem is that afaik some implementations of `date` can't print nanosecond. This is becoming off-topic however. – Piotr Siupa Jun 06 '18 at 20:16
  • 1
    If you want to try to use `date`, you'll want to actually retrieve its output and then incorporate it into a single write. That is, `echo "$(date +%s.%N) foobar"` is writing `foobar` at the same time as it writes the output of `date`. There's going to be several milliseconds of latency involved in actually forking off a subshell to run `date` in, collecting its output, etc., though, so the timestamp you write is going to be based on when `date` finally finished spinning up, not when its output was read, the `wait()` for its completion finished, and the `echo` finally did *its* write. – Charles Duffy Jun 06 '18 at 20:16

0 Answers0