If you want to globally redirect everything that happens to be written (like you do now), it's tricky, but can be hacked together.
I strongly recommend that, if it's possible, just do it by normal piping. just wrap everything you do in a subshell. In this case
echo "this is the message"
) | cat
or just write everything into a variable with "$()" syntax.
The next way is to use what you did, but write to a tmpfs or
/dev/shm if they are available. That's pretty straight forward, but you have to know what ram-based filesystems are in place (and set them up if possible).
Another way is to create a fifo with
mkfifo. In both cases, you need to clean up after yourself.
I have a very ugly hack, but I bet someone can improve it.
exec > >( tee >( ( tac && echo _ ) | tac | (read && cat > ./log) ) )
echo "finished writing"
echo "stdout now reopen"
sleep 1 #wait if the file is still being written asynchronously
How it works: first, you have a
tee so you can see what's going on. This in turn outputs to another process substitution. There, you have the trick
tac|tac which (because
tac needs entire input to start outputting) waits for the entire stream to finish before going on. The last piece is in a subshell that actually outputs this into a file. Of course, the final shell would, immeadiately upon instantiation, create the output file in the filesystem if that was the only line. So something that also waits for the input to finally come, has to be done first, to delay file creation. I do this by outputting a dummy line first with echo, and then reading and discarding it. The
read blocks until you close the file descriptor, signalling to
tac its time has come. Hence, the closing of the stdout file descriptor at the end. I also saved the original
stdout before opening the process substitution, in order to restore it at the end (to use
cat once more). There's a
sleep 5 in there, so I could check with
ls if the file really wasn't created too early. The final sleep is trickier... The subshell is asynchronous and if there is a lot of output, you are waiting for both
tacs to do their thing before the file is really there. So reasonably, you'll probably need to do something else to check if the thing really is finished. For instance,
&& touch sentinel at the end of the last subshell, and then
while [ ! -f sentinel ]; do sleep 1; done && rm sentinel before you finally use the file.
All in all, two process substitutions and in the inner one another two subshells and 2 pipes. It's one of the ugliest things I've ever written... but it should create the file only when you close the stdout, which means it's well controlled and can be done when your filesystems are ready.