2

I can redirect the output of a script to a logfile from inside the script with exec:

#!/bin/bash
exec > stdout.log 2>&1
echo hello world

Is it possible to redirect the output to less instead of a file? I tried

#!/bin/bash
exec > >(less) 2>&1

# output some text
for (( i=1; i <= 500; i++ )); do echo "hello world $i"; done

but this fails in a strange way ... the less prompt is visible but you end back at the terminal.

I'd like to set this up a the start of the script (so it can be conditional, depending on arguments, tty, etc.).

1

2 Answers 2

1

You should have your script wait for the less child process, otherwise your script will terminate before it, and less will suddenly find itself outside the foreground process group, not able to read commands from the terminal or restore the terminal settings anymore.

Also, in order to prevent less for waiting forever for the end of its input, your script should close the pipe to it.

Putting all that together:

exec > >(less) 2>&1
trap 'exec >&- 2>&-; wait' EXIT
# >&- 2>&- => close stdout and stderr => cause EOF on less' stdin

seq 1 50000
# the rest of your script

But this is not very nice, not portable to most other shells, and relying on undocumented (and unreliable) behaviour of bash: the wait won't work fine if you have more than one exec > >(...) in your script, and it will also wait on other background processes started with &.


A better idea would be to have your script call itself, using an environment variable to avoid infinite recursion:

if [ ! "$CALLED_MYSELF" ]; then
    set -o pipefail # supported in bash, but not in all the shells  
    CALLED_MYSELF=1 "$0" "$@" 2>&1 | less
    exit
fi

seq 1 50000
# the rest of your script
1
  • Thank you for explaining what's happening!
    – laktak
    Sep 25, 2020 at 8:39
1

Try just this (without the exec line):

for (( i=1; i <= 500; i++ )); do echo "hello world $i"; done | less

(update)

If you want to include the whole script, you can wrap the script with { ... }

#!/bin/bash
{
# output some text
for (( i=1; i <= 500; i++)) do echo "hello world $i"; done

# whatever output you want...

} | less
5
  • Thank you but you this doesn't answer the question. I want to redirect the whole output of the script as shown in the first example, not just a line or part of it.
    – laktak
    Sep 24, 2020 at 15:08
  • maybe run the script using the pipe?
    – SimonS
    Sep 24, 2020 at 15:09
  • i.e. ./script | less
    – SimonS
    Sep 24, 2020 at 15:10
  • Thanks, while this would work I'd still prefer to set it up a the start of the script.
    – laktak
    Sep 24, 2020 at 15:28
  • 1
    I agree with @SimonS for solutions. I think he answered the question. In shell scripting, there is always more than one way to do it. If you don’t want to accept his suggestions, then you should research it yourself: less howto, man less, bash howto, and man bash to name a few.
    – Steve O
    Sep 24, 2020 at 18:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.