0

I have a script that do many action, and echo information. I want to have a "duplicate" of all what is printed to the terminal into a file, and I want to add some information into it as well.

Problem : i can't simply piping every echo into tee because some printing are done by function that are sourced from another source file, and I can't modify them.

So I searched and I found this redirect COPY of stdout to log file from within bash script itself

So based on that, i do the following script

#!/bin/bash

strReportingFilepath="./reporting.log"

echo "[1] stdout : This text will not appear in the reporting file"
echo "[2] stderr : Neither this one" >&2

# We dup stdour and stderr into the reporting file (reporting fiel emptied)
exec > >(tee -i "$strReportingFilepath") 2>&1


echo "[3] stdout : This text will be on both the terminal and the reporting file"
echo "[4] stderr : This one too :)" >&2
echo "[5] stdout : Last case : this text should only appear into the reporting file" >> "$strReportingFilepath"

exit 0

When I run this script, I have this on my terminal

(terminal info) # ./my_script.sh
[1] stdout : This text will not appear in the reporting file
[2] stderr : Neither this one
(terminal info) # [3] stdout : This text will be on both the terminal and the reporting file
[4] stderr : This one too :)

and this into my file

(terminal info) # cat reporting.log
[3] stdout : This text will be on both the terminal and the reporting file
[4] stderr : This one too :)

I have 2 problem :

First, for an unknow reason (I suspect that I use exec/named pipe/tee in a sloppy way), my script actually wait at the end. I have to press enter key in order to terminate the script.

Secondly, the [5] echo doesn't appear into the file.

I'm pretty sure that I mess something up with the exec line, but I can't understand what and I don't know what to do.

I added a "sync" call after the [5] echo, but it didn't work.


Well, I think I finally managed to do what I wanted.

What I want :

  1. Having everything printed onto the screen terminal (stdout, stderr, stdin) to be "logged" into a file
  2. Having the possibility to add text into this logging file without displaying it onto the terminal screen.

I stopped using tee, and I found a link talking about "script(8)". After tinkering around, I ended up with this script :

#!/bin/bash

strReportingFilepath="./reporting.log"

# Part that will do the script reporting
if [ -z "$SCRIPT" ]; then
    export SCRIPT="$0"

    # Creation of the string bash command 
    strCommand="$0"
    strSearch="'"
    strReplace="'\''"
    for strArgument in "$@"; do
        strCommand+=" '${strArgument//$strSearch/$strReplace}'"
    done
    
    # Execution of the script
    /usr/bin/script --return --quiet --flush --append "$strReportingFilepath" --command "$strCommand"
    # We get the return
    intScriptReturnValue="$?"
    
    # The line automaticly added by /usr/bin/script is something that I don't want
    strTemporaryFilepath="$(mktemp)"
    grep -v '^Script started on ' "$strReportingFilepath" > "$strTemporaryFilepath"
    mv "$strTemporaryFilepath" "$strReportingFilepath"
    
    exit "$intScriptReturnValue"
fi



# "Start" of the script that I want "supervised"

sync
echo "[0  ] directly" >> "$strReportingFilepath"

echo "[1. ] out"

sync
echo "[1.5] directly" >> "$strReportingFilepath"

echo "[2  ] out"
echo "[3. ] err" >&2

sync
echo "[3.5] directly" >> "$strReportingFilepath"

echo "[4  ] err" >&2
echo -n "[5  ] input : "
read

echo "[6  ] out"
echo "[7  ] err" >&2
echo "      End :)"

exit 5

Now, there are still some problem :

  1. I really don't know what I have done, so if you're thinking about using this code as it, think about it twice. I just know that for my case, it "seem to work" (yeepee !)

  2. Each time I want to write something directly into the logging file, I have to call "sync" before. Without it, the line aren't in order. Please note that "sync" doesn't garanty that it's instantaneous. In the doc, they say that command like "halt" do a "sleep" after a sync in order to be sure, so it's still possible to have race condition resulting in wrong line order.

  3. It seem that "script" doesn't work on some case, like when stdin is not a terminal. See "script(8)".

Well, sorry for this strange question, I would have bet that what I wanted to do was simple and common, turn out not really.

Tom's
  • 2,448
  • 10
  • 22

1 Answers1

1

You have a race condition; everything sent via tee is just getting buffered, and only gets written (to the terminal and disk) after the script has actually finished. Note: sync doesn't affect this, since it flushes the operating system's buffers, but tail's.

That's why you're getting a terminal prompt before line [3]:

(terminal info) # [3] stdout : This text will be on both the terminal and the reporting file

...your shell sends the prompt for the next command (after running the script), then tee flushes everything in its buffers, so you see lines [3] and [4] after the prompt. Pressing enter after this doesn't terminate the script (it ended a while ago), it just gets you a clean shell prompt that isn't mixed up with the output.

Something similar happens with the reporting.log file. Line [5] gets written into it, and then a moment later tee writes lines [3] and [4] into it, but since tee's output pointer is set to write to it starting at the beginning of the file, it overwrites line [5].

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
  • you're completly rigth, tee is buffered and write all at one at the end. Adding the append option allowed me to clearly see that. Thank you, I was able to do some other research with new keyword, but sadly I didn't solve my problem yet. – Tom's Feb 11 '22 at 09:35
  • @Tom's It's extremely hard to guarantee in-order delivery of data anytime the data's going by several different paths to the same destination. Using [`unbuffer`](https://linux.die.net/man/1/unbuffer) and/or options like `--line-buffered` (for programs that have it) can help quite a bit, but they're really just decreasing the probability of a problem, not entirely eliminating it. – Gordon Davisson Feb 11 '22 at 10:20
  • I edited my question to add my current "solution". If you have retex, I'm gladly taking them. Hope that can help other people. – Tom's Feb 11 '22 at 17:03