<!--
  canonical-url: https://www.avonture.be/blog/bash-console-log-together
  published:     2024-05-03
  This is a static plain-Markdown mirror generated at build time.
  Visit the canonical URL above for the fully rendered page, with images and interactive components.
-->

# 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.

---
![Bash - Echo on the console and in a logfile in the same time](/img/v2/bash.webp)

**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](/blog/bash-logging), 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

```bash
$ ./script.sh

total 8.0K
drwxr-xr-x 2 node node 4.0K Aug  9 18:04 .
drwxr-xr-x 3 node node 4.0K Aug  9 18:04 ..
-rw-r--r-- 1 node node    0 Aug  9 18:04 archive.zip
-rw-r--r-- 1 node node    0 Aug  9 18:04 notes.txt
-rw-r--r-- 1 node node    0 Aug  9 18:04 report.csv

$ cat /tmp/test.log

[2026-08-09T18:04:09+0000] total 8.0K
[2026-08-09T18:04:09+0000] drwxr-xr-x 2 node node 4.0K Aug  9 18:04 .
[2026-08-09T18:04:09+0000] drwxr-xr-x 3 node node 4.0K Aug  9 18:04 ..
[2026-08-09T18:04:09+0000] -rw-r--r-- 1 node node    0 Aug  9 18:04 archive.zip
[2026-08-09T18:04:09+0000] -rw-r--r-- 1 node node    0 Aug  9 18:04 notes.txt
[2026-08-09T18:04:09+0000] -rw-r--r-- 1 node node    0 Aug  9 18:04 report.csv
```

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](/blog/bash-progression-bar) is often nicer than a wall of scrolling output.*

**script.sh**

```bash
#!/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
```

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.

**script.sh**

```bash
#!/usr/bin/env bash
set -euo pipefail

declare -r command="ls -alhR /tmp"
declare -r LOGFILE="/tmp/test.log"

eval "${command}" > "${LOGFILE}"
cat "${LOGFILE}"
```

Don't do things like that!
