andrej
andrej

Reputation: 587

redirect output to terminal

I am trying to understand redirecting the output.

I have the code:

#!/bin/bash
function function1 () {
    log=${1}.log

    exec 3>&1
    exec 1>>$log
    exec 2>&1

    echo checking $log >&3
    echo txt to $log
}

function1 log1
function1 log2

exit 0

the output that I get is:

checking log1.log

and file log1.log with content

txt to log1.log
checking log2.log

and file log2.log with content

txt to log2.log

what I really want is file log1.log with content

txt to log1.log

and file log2.log with content

txt to log2.log

and output to terminal to be.

checking log1.log
checking log2.log

How can I do that, please ? I know I can use function1 log1 > log1.log 2>&1, but than I can not redirect echo back to terminal in function1, I can but it is similar result.

Upvotes: 2

Views: 1427

Answers (3)

meuh
meuh

Reputation: 12255

If you want to set stdout and stderr and then restore them in the function do:

function function1 () {
    log=${1}.log
    exec 4>&2 3>&1 1>>$log 2>&1 # save 1 and 2 to 3 and 4
    echo checking $log >&3
    echo txt to $log
    exec 1>&3 2>&4 3>&- 4>&- # restore 1 and 2
}

The 3>&- closes 3, just for completeness.

Upvotes: 4

123
123

Reputation: 11216

No idea why you would want to do it this way and the other answer is a much better way unless you are not disclosing all information, but the problem is with your understanding of how file descriptors work.

First time

exec 3>&1     ## Assign 3 to where 1 is currently pointing which is /dev/tty
exec 1>>$log  ## assigns 1 to point to logfile
exec 2>&1     ## Assigns stderr to the 1 which currently points at the logfile

Second time

exec 3>&1     ## Assign 3 to where 1 is currently pointing which is the old logfile
exec 1>>$log  ## assigns 1 to point to new logfile
exec 2>&1     ## Assigns stderr to the 1 which currently points at the new logfile

As you can see the file descriptors remember where they are pointing and simply point to whatever you tell them to.

If you want to do it this way, instead of redirecting to 1, just redirect straight to /dev/tty, as unlike 1 this will never change(hopefully!).

#!/bin/bash
function function1 () {
    log=${1}.log

    exec 3>/dev/tty
    exec 1>>$log
    exec 2>&1


    echo checking $log >&3
    echo txt to $log

    exec 1>/dev/tty # Probably want this line as well so your not writing to 
                    #  the logfile for the rest of the script
} 

function1 log1
function1 log2

exit 0

Upvotes: 2

Phil Miller
Phil Miller

Reputation: 38118

This should do it

#!/bin/bash
function function1 () {
    log=${1}.log

    echo checking $log
    echo txt to $log >$log
}

function1 log1
function1 log2

exit 0

There's no reason to store changes to any particular file descriptor, when you're only using the changed destination once.

If you do want to use that approach, say because you're redirecting a lot of commands, you have at least 2 options:

  • run the redirection and associated commands in a subshell, so that when it exits, you're still dealing with the original streams
  • treat redirection like registers to be saved in a function call - push them to a temporary (as in your &3 above) and then restore them after the call.

Upvotes: 1

Related Questions