The problem with using /dev/tty
is it assumes that stdout was originally attached to a tty - this is not necessarily so as demonstrated by GitHub Actions. It would also be a problem if you were redirecting stdout of the script elsewhere (a log file for instance).
What you want to pass to tee is what was stdout before the process substitution (the $(...)
bit) - process substitution takes stdout in order to capture the output. Bash and other shells have the ability to manipulate file descriptors for your use case:
exec 3>&1 # Open FD 3 as a duplicate of stdout (fd 1)
# Run ./build.sh but make sure it does not have FD 3 open and tee to FD 3
VARIABLE=$(./build.sh 3>&- | tee /dev/fd/3)
exec 3>&- # Close FD 3
Doing it this way allows the stdout of the script to be manipulated outside the script (redirecting to a logger, or /dev/null, etc), preserving the correct behaviour WRT stdout.
As pointed out by @SOUser in the comments, there is an issue when writing the output of a script to a file. The problem is that tee /dev/fd/3
truncates the file when it opens it, discarding anything written to it already. Attempting to fix this with tee -a /dev/fd/3
causes even stranger behaviour - the line to be captured gets lost entirely for some reason.
A solution to this is to write to a pipe and not a file, so instead of ./script > test.log
, use ./script | cat > test.log
. When done this way, tee
cannot truncate the file because it is not attached to it - it is attached to a pipe to cat
which cannot be truncated.
printf '%s\n' "$VARIABLE"
just after?set -e -o pipefail
so if the build fails I will not get any outputs