[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: SHELL: duping stdout and stderr to another file



Ulisses Alonso <ulisses@pusa.eleinf.uv.es> writes:

> Hi all
> 
> I would like to know _how to copy_ (not start a new shell, eg: script)
> stdout and stderr _from a shell script_ to a file. Also It is interesting 
> for me if there is a way to stop copying stdout and stderr...
> 
> Thanks in advance,
> 
> 
> 			Ulisses

Well, I'm not entirely certain what you mean, so I'll just say several 
things that might answer your question and hope that one of them
tells you what you want to know.

Suppose I have a simple shell script:

#!/bin/sh
echo "Guvf vf n frperg zrffntr." | tr a-zA-Z n-za-mN-ZA-M

And I call this script foo.
I make it executable so that I can call it like this:

cush:~$ ./foo
This is a secret message.

Now, I decide I want to redirect this into a file.  I do:

cush:~$ ./foo > sekritfile

Ok.  So I can redirect all of foo's stdout into a file.  But suppose I 
made a mistake in foo (say, I added an extra argument to the `tr'
command).  Then I get:

cush:~$ ./foo > sekritfile
tr: too many arguments
Try `tr --help' for more information.

Well, I want to redirect both foo's stdout and stderr into a file.
Ok; I do this by saying "redirect stdout to sekritfile and redirect
stderr to the same place as stdout":

cush:~$ ./foo > sekritfile 2>&1

All well and good.  I can even do fancy things like redirecting stdout 
and stderr to two different files:

cush:~$ ./foo > sekritfile 2> errfile

Ok.  So far so good.  Now suppose I don't want to have to specify
where the stuff in foo goes on the commandline; suppose I want the
shell script to just redirect everything all the time.  I can do this
by using the shell builtin command 'exec' in a slightly unusual way:

#!/bin/sh
exec > sekritfile 2> errfile
echo "Guvf vf n frperg zrffntr." | tr a-zA-Z n-za-mN-ZA-M

So now what happens is that when I do:

cush:~$ ./foo

Is that the output of the tr command is redicted to 'sekritfile' and 
the stderr of the tr command (and any following commands) is
redirected to 'errfile'.  I could redirect them both to the same place 
using:
exec > sekritfile 2>&1

So far so good.  But what if I want to redirect something and then
undo the redirection?  Say, if I had:

#!/bin/sh
echo "Guvf vf n frperg zrffntr." | tr a-zA-Z n-za-mN-ZA-M
echo "Done decoding."

And I wanted the first line redirected, and the second not.  Then, I
just need to save the original destinations of stdout and stderr, like 
this:

#!/bin/sh
exec 3>&1 4>&2 > sekritfile 2>&1
echo "Guvf vf n frperg zrffntr." | tr a-zA-Z n-za-mN-ZA-M
exec >&3 2>&4
echo "Done decoding."

Then, only the first echo command gets redirected to a file.

cush:~$ ./foo
Done decoding
cush:~$ cat sekritfile
This is a secret message.

But notice:

cush:~$ ./foo > msgfile
cush:~$ cat msgfile
Done decoding.
cush:~$ cat sekritfile
This is a secret message.

Now, suppose that instead of redirecting output, I want to copy it.
(That is, I still want to see the output on the screen).  This means
the output has to go to two places - the file and the screen.  There
are a few ways to do this - I'm only going to show one involving the
'tail' command.

The problem is that the shell won't split output by itself, and won't
do overall redirections (like the exec commands above) into pipes.
However, we can do this:

#!/bin/sh
cat /dev/null > sekritfile   # create it so that tail doesn't complain
tail -f sekritfile &
tailpid=$!
exec >> sekritfile
exec 2>&1
...
... more commands here
...
sleep 2
kill -1 $tailpid

This causes the shell to dump stdout and stderr into the file
sekritfile and at the same time starts up a process (the tail -f) that 
reads the file and displays its contents.  The last kill command is
necessary to stop the 'tail' process when the script is done.  If you
want to do this and only want to redirect portions of the script, you
can do:

#!/bin/sh
cat /dev/null > sekritfile
tail -f sekritfile &
tailpid=$!
exec 3>&1 4>&2
exec >> sekritfile
exec 2>&1
...
... these commands get saved to the file
...
exec 5>&1 6>&2 1>&3 2>&4
...
... these commands don't go to the file
...
exec 1>&5 2>&6
...
... these commands go to the file
...
sleep 2
kill -1 $tailpid

I hope this answers your question.


--  
Unsubscribe?  mail -s unsubscribe debian-user-request@lists.debian.org < /dev/null


Reply to: