Bash – How to implement deferred stdout in child process without using temporary files

bashio-redirectionstdout

I have a program (let's name it middle) which I can't modify.
This program performs the following sequence of steps:

  • middle sends some text to stdout (echo Text1)
  • middle invokes shell script inner.sh passing some text string (4 KBytes or less) as argument (inner.sh "Deferred Message")
  • middle sends some text to stdout (echo Text2)

My task is to create two bash scripts outer.sh and inner.sh to arrange deferred message after all the other messages generated by middle process.
The resulting stdout must be the following:

Text1
Text2
Deferred Message

The requirements:

  • outer.sh should invoke middle (and middle will invoke inner.sh).
  • inner.sh must remember its argument (deferred message) somewhere
  • After middle is terminated, outer.sh must recall the deferred message and print it to stdout
  • There will be a lot of outer.sh processes running simultaneously. The deferred message must be stored in a place which is local to current instance of outer.sh process
  • Creation of temporary objects in file system is prohibited. (Is it possible to store the deferred message somewhere in memory?)

inner.sh

# Where should I save message "$1"?

outer.sh

middle "$@"
# How should I recall and print the deferred message?

Where should I save the deferred message in child process and how to read it back in parent process?

Best Answer

inner.sh could be:

printf '%s\n' "$1" >&3

and in outer.sh, you could do:

{ inner=$(middle "$@" 3>&1 >&4 4>&-); } 4>&1
printf '%s\n' "$inner"

The inner text is passed via a pipe (in the command substitution) and stored in a shell variable. That assumes middle doesn't close that fd 3 before invoking inner.sh (no reason it would do that though).

Explanation:

  • { ... } 4>&1. In that command group, initially, both fd 1 and 4 point to the original stdout. IOW, we've made a copy of outer.sh's stdout onto fd 4 to be able to recover it inside the command substitution
  • $(...). Inside that command substitution, stdout (fd 1) is a pipe. That's the point of command substitution. It wants to grab the output of the command. But here, we don't want the stdout of middle, we want what it (or more exactly its child inner.sh) writes on fd 3, so:
  • middle 3>&1 >&4 4>&-: for middle, we make fd 3 the cmdsubst pipe, so that what inner.sh writes there goes into $inner, and we restore middle's stdout onto the saved original stdout on fd 4. We close fd 4 after it has served it's purpose as middle doesn't need to do anything with it.
Related Question