Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to redirect output of an entire shell script within the script itself?

Tags:

bash

shell

sh

People also ask

Can we redirect the output of a command to a file and display at the same time?

Redirecting output to Multiple files and screen: If you want to redirect the screen output to multiple files, the only thing you have to do is add the file names at the end of the tee command.

What character is used to redirect the output of a script to a file?

The '>' symbol is used for output (STDOUT) redirection. Here the output of command ls -al is re-directed to file “listings” instead of your screen.


Addressing the question as updated.

#...part of script without redirection...

{
    #...part of script with redirection...
} > file1 2>file2 # ...and others as appropriate...

#...residue of script without redirection...

The braces '{ ... }' provide a unit of I/O redirection. The braces must appear where a command could appear - simplistically, at the start of a line or after a semi-colon. (Yes, that can be made more precise; if you want to quibble, let me know.)

You are right that you can preserve the original stdout and stderr with the redirections you showed, but it is usually simpler for the people who have to maintain the script later to understand what's going on if you scope the redirected code as shown above.

The relevant sections of the Bash manual are Grouping Commands and I/O Redirection. The relevant sections of the POSIX shell specification are Compound Commands and I/O Redirection. Bash has some extra notations, but is otherwise similar to the POSIX shell specification.


Typically we would place one of these at or near the top of the script. Scripts that parse their command lines would do the redirection after parsing.

Send stdout to a file

exec > file

with stderr

exec > file                                                                      
exec 2>&1

append both stdout and stderr to file

exec >> file
exec 2>&1

As Jonathan Leffler mentioned in his comment:

exec has two separate jobs. The first one is to replace the currently executing shell (script) with a new program. The other is changing the I/O redirections in the current shell. This is distinguished by having no argument to exec.


You can make the whole script a function like this:

main_function() {
  do_things_here
}

then at the end of the script have this:

if [ -z $TERM ]; then
  # if not run via terminal, log everything into a log file
  main_function 2>&1 >> /var/log/my_uber_script.log
else
  # run via terminal, only output to screen
  main_function
fi

Alternatively, you may log everything into logfile each run and still output it to stdout by simply doing:

# log everything, but also output to stdout
main_function 2>&1 | tee -a /var/log/my_uber_script.log

For saving the original stdout and stderr you can use:

exec [fd number]<&1 
exec [fd number]<&2

For example, the following code will print "walla1" and "walla2" to the log file (a.txt), "walla3" to stdout, "walla4" to stderr.

#!/bin/bash

exec 5<&1
exec 6<&2

exec 1> ~/a.txt 2>&1

echo "walla1"
echo "walla2" >&2
echo "walla3" >&5
echo "walla4" >&6