Skip to main content

Bash - Echo on the console and in a logfile in the same time

Tired of silent Bash scripts? Learn the simple, effective method to simultaneously echo command output to the console and write it to a log file line-by-line.

· 3 min read
View raw
Christophe
Markdown, WSL and Docker lover ~ Insatiable curious.
Cet article existe aussi en français — lire la version française.

This article is over a year old but was reviewed on July 30, 2026 — the content is still accurate.

Bash - Echo on the console and in a logfile in the same time

⚡
TL;DR

This article shows how to make a long-running Bash command output to both the console and a log file simultaneously (rather than only one or the other), using eval to run the command and echoing each line to the screen while also appending it to the logfile in real time — instead of buffering everything and dumping it at the end.

In my previous article Bash - Script to add logging features to your script, I've shared a way to write information in a logfile.

By running ls -alh /tmp you will get the list of all files in the /tmp folder and display the list on your console. By running ls -alh /tmp >> application.log you won't see the list in your console since everything will be written in the application.log file.

How can we display the output of a command like ls f.i. both on the console and in a logfile?

Result​

>user@machine: ~/yourproject

Same lines, twice: once printed live on the console, once appended to /tmp/test.log — proof the two outputs really happen at the same time, not one after the other.

For the illustration above, the script listed a small demo folder instead of /tmp for readability; in practice you'd point it at whatever long-running command you need to trace.

Why it works​

The command runs through eval, and its output is piped into a while read loop. For every single line that comes out, the loop does two things immediately, before reading the next line: echo it to the console, then append it to the logfile. Nothing is buffered and dumped at the end — that's what keeps the two outputs in lockstep.

For example, let's get the list of all files below /tmp and this recursively. For this, our command will be ls -alhR /tmp.

This command will probably take time to run and this is the objective: run a long command and make sure our user will see the output on the screen so they know that the script is doing something, and also redirect the output to a log file for further analysis in case of f.i. problems (or just because the script is fired in a cron).

If "the script is doing something" is all your user needs to know, a progression bar is often nicer than a wall of scrolling output.

#!/usr/bin/env bash
set -euo pipefail
declare -r command="ls -alhR /tmp"
declare -r LOGFILE="/tmp/test.log"
touch "${LOGFILE}"
function log::write() {
printf '[%s] %s%s\n' "$(date +"%Y-%m-%dT%H:%M:%S%z")" "${line}" >>"${LOGFILE}"
return 0
}
eval "${command}" |
while IFS= read -r line; do
echo "${line}" # Output on the console (stdout)
log::write "${line}" # And append in the log file
done

This script runs a directory listing command and processes its output line by line, printing each line to the console while simultaneously writing it to a log file with a timestamp. It sets up safety flags at the start to catch errors early, defines a logging function, and uses a pipeline to feed the command output through a loop.

The main part is the eval function.

The command (ls -alhR /tmp) will be fired and, for every single line, we'll echo the line on the screen and, also, redirect it in a text file.

The example below does almost the same thing i.e., it executes a command and displays it both in a file and on the console but ... as you can see, the command will be run and everything will be redirected to the file so, for several seconds, the user will have no screen output and might think the script is stuck. Then, once the command has successfully finished, and only then, the log file will be displayed, in a block, on the console.

#!/usr/bin/env bash
set -euo pipefail
declare -r command="ls -alhR /tmp"
declare -r LOGFILE="/tmp/test.log"
eval "${command}" > "${LOGFILE}"
cat "${LOGFILE}"

This bash script runs a directory listing command on /tmp, saves the output to a log file, and then displays that log file's contents. It uses strict error handling to ensure the script stops if anything goes wrong.

Don't do things like that!


Was this article helpful?
Did you try the steps in this article?