When you run a script, progress messages appear in the terminal:
Processing: SRR1553607_1
Processing: SRR1553607_2
All samples processed.
But once you close the terminal — or scroll past it — that output is gone. There is no record of what ran, when it ran, or which files were processed.
For a two-file test this seems unimportant. In a real project with 50 samples, running overnight on a remote server, it matters a great deal. A log file gives you a permanent record that you can come back to at any time.
Save this as fastqc_with_log.sh, make it executable, and run it:
Terminal window
chmod+xfastqc_with_log.sh
./fastqc_with_log.sh
After it finishes, check the log:
Terminal window
catrun_log.txt
Output:
FastQC run started
Processing SRR1553607_1
Processing SRR1553607_2
FastQC run finished
Notice the last echo in the script — "Done. Log saved to: $LOG_FILE" — uses neither > nor >>. It prints to the terminal, not the log file. You can mix both: some messages go to the terminal, others go to the log.
With > and >>, output goes to the file but not to the terminal. You either see it or save it — not both. This is fine for echo messages you have already written, but sometimes you want to watch output appear on screen as it is saved.
That is what tee does. It takes output and sends it to two places simultaneously: the terminal and a file.
tee works with the pipe symbol |. The pipe connects two commands: it takes the output of the command on the left and passes it as input to the command on the right.
Terminal window
echo"Run started"|teerun_log.txt
This prints Run started to the terminal and writes it to run_log.txt at the same time.
Like >, tee on its own overwrites the file if it already exists. To append instead, add the -a flag:
Terminal window
echo"Run started"|teerun_log.txt# overwrites — use at the start
echo"Processing sample"|tee-arun_log.txt# appends — use throughout
Both are valid. In longer pipelines that run for hours — perhaps on a remote server — tee is particularly useful because you can watch progress in real time while the log is being written simultaneously.
The >> and tee approaches above work well for messages you write yourself with echo. But they have a gap: error messages.
When a command fails — for example, if FastQC cannot find a file — it prints the error to a separate stream called stderr. Everything we have used so far only captures stdout (standard output — normal messages and results). Errors on stderr bypass your log entirely and appear on the terminal unrecorded.
exec is a special command. When used for redirection at the top of a script — without any other command — it sets up that redirection for every command that follows. You write it once and the entire script is covered. No >> or | tee needed on individual lines.
Place this near the top of your script, just after your variable declarations. Then write the rest of the script completely normally.
Breaking it down piece by piece:
Part
What it does
exec
Apply this redirection to all commands from here onwards
>
Redirect stdout (stream 1)
>(tee logfile.txt)
Instead of redirecting into a plain file, send stdout into a tee process — tee then writes to both the terminal and logfile.txt
2>&1
Redirect stderr (stream 2) to the same destination as stdout — so errors are captured too
The >(...) syntax is called process substitution — it lets you use a running command (like tee) as if it were a file. You do not need to fully understand it right now; treat the whole line as a pattern you place at the top of any script you want fully logged.
# Everything below is automatically captured — no >> or | tee needed
DATA_DIR="Training/short_reads/paired"
OUTPUT_DIR="fastqc_results"
mkdir-p$OUTPUT_DIR
echo"FastQC run started"
forFILEin$DATA_DIR/*.fastq
do
SAMPLE=$(basename$FILE.fastq)
echo"Processing: $SAMPLE"
fastqc$FILE--outdir$OUTPUT_DIR
done
echo"All done."
Every echo, every FastQC status message, and any error messages all go to the terminal and to full_run.log — with nothing extra on any individual line.
Depending on your needs, you can adjust what gets captured and where it goes:
Everything to terminal and log — the most common choice:
Terminal window
exec>>(tee run.log)2>&1
Everything to a log file only — no terminal output:
Useful for scripts running overnight on a server where nobody is watching.
Terminal window
exec>run.log2>&1
Errors only, saved to a separate file:
Normal output still prints to the terminal. Only errors are saved. Useful when you want to check for problems after a run without reading through all the normal output.
Terminal window
exec2>errors.log
Normal output and errors in separate files:
Lets you review them independently — one file for what ran, one file for what went wrong.
Terminal window
exec>stdout.log2>errors.log
To append rather than overwrite: use tee -a so each run adds to the existing log rather than replacing it:
Any script where you want complete, automatic logging
For most real bioinformatics scripts — especially anything running on a cluster or overnight — exec > >(tee log) 2>&1 is the approach to reach for. It is the most complete, requires the least effort, and captures the errors that matter most.