Linux write error to file

How to Write Standard Error to a File While Using «Tee» With a Pipe

How do I write standard error to a file while using tee with a pipe?

I’m assuming you want to still see standard error and standard output on the terminal. You could go for Josh Kelley’s answer, but I find keeping a tail around in the background which outputs your log file very hackish and cludgy. Notice how you need to keep an extra file descriptor and do cleanup afterward by killing it and technically should be doing that in a trap ‘. ‘ EXIT .

There is a better way to do this, and you’ve already discovered it: tee .

Only, instead of just using it for your standard output, have a tee for standard output and one for standard error. How will you accomplish this? Process substitution and file redirection:

command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)

Let’s split it up and explain:

>(. ) (process substitution) creates a FIFO and lets tee listen on it. Then, it uses > (file redirection) to redirect the standard output of command to the FIFO that your first tee is listening on.

The same thing for the second:

We use process substitution again to make a tee process that reads from standard input and dumps it into stderr.log . tee outputs its input back on standard output, but since its input is our standard error, we want to redirect tee ‘s standard output to our standard error again. Then we use file redirection to redirect command ‘s standard error to the FIFO’s input ( tee ‘s standard input).

Process substitution is one of those really lovely things you get as a bonus of choosing Bash as your shell as opposed to sh (POSIX or Bourne).

In sh , you’d have to do things manually:

out="$/out.$$" err="$/err.$$"
mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log < "$out" &
tee -a stderr.log < "$err" >&2 &
command >"$out" 2>"$err"

Why would tee fail to write to stdout?

You aren’t misunderstanding tee , you’re misunderstanding what stdout is. In a pipe, like echo testing | tee | python3 -c ‘a=1’ , the tee command’s stdout is not the terminal, it’s the pipe going to the python command (and the echo command’s stdout is the pipe going to tee ).

So tee /dev/stdout sends two copies of its input (on stdin) to the exact same place: its stdout, whether that’s the terminal, or a pipe, or whatever.

If you want to send a copy of the input to tee someplace other than down the pipe, you need to send it somewhere other than stdout. Where that is depends on where you actually want to send it (i.e. why you want to copy it). If you specifically want to send it to the terminal, you could do this:

echo testing | tee /dev/tty | python3 -c 'a=1'

. while if you want to send it to the outer context’s stdout (which might or might not be a terminal), you can duplicate the outer context’s stdin to a different file descriptor (#3 is handy for this), and then have tee write a copy to that:

Читайте также:  Вход в линукс без пароля и логина

Yet another option is to redirect it to stderr (aka FD #2, which is also the terminal by default, but redirectable separately from stdout) with tee /dev/fd/2 .

Note that the various /dev entries I’m using here are supported by most unixish OSes, but they aren’t universal. Check to see what your specific OS provides.

Linux: tee usage misunderstanding

TL;DR — Try this one:

svnadmin dump MyRepo -r1:r2 2>&1 > dumpfile | tee log.txt

Explanation: Each command has three connected «standard streams»: Standard Input (STDIN, filehandle 0), Standard Output (STDOUT, 1) and Standard Error (STDERR, 2). Usually commands ouput their «data» on STDOUT and errors on STDERR.

A simple command like ls has all three of them connected to the console. Because both STDOUT and STDERR are connected to the console the output of the command is interleaved.

A «pipe» like ls | tee log.txt redirects STDOUT of the first command to STDIN of the second command. No more — no less. Therefore all other streams are still connected to the console. Should the ls part produce error messages they will be written to the console, not to the file! But your ls example did not output any errors so you didn’t notice.

After the pipe is setup the shell evaluates the other redirection operator of the first command — from left to right.

Therefore svnadmin dump > dumpfile | tee log.txt will redirect STDOUT of svnadmin to dumpfile leaving effectively no data for the tee command because that’s a redirection, not a copy.

svnadmin dump MyRepo 2>&1 > dumpfile | tee log.txt adds another redirection step. It reads «make filehandle 2 (STDERR) a copy of the filehandle 1 (STDOUT)» which is the pipe at this point. Writing to either STDOUT or STDERR will write to the pipe. But after that the > dumpfile redirection is applied and STDOUT is redirected to the file.

You can read all this (and much more) in the shell’s manual. For bash it is in the section REDIRECTION .

| vs for stdin and stdout with splice, pipe and tee command in C

The two methods are not equivalent:

On second case, tee returns -1 , and perror complains about an invalid argument.

You can check the nature of fd 0 using fstat and checking the st_mode value:

struct stat statbuf;
fstat(STDIN_FILENO, &statbuf);

if (statbuf.st_mode & S_IFREG)
printf(«regular file»);

if (statbuf.st_mode & S_IFIFO)
printf(«pipe»);

Tee doesn’t get the output from the pipe

If you redirect the output to exec > path/logfile.log then. well, the output will be redirected to the file, not to the pipe.

#!/bin/bash
dirname=/path
tempfile=myTempfileName
find "$dirname" -type f > "$tempfile"
sed 's_.*/__' "$tempfile" | sort | uniq -d |
while read fileName
do
grep "$fileName" "$tempfile"
done
> 2>&1 | tee -a path/logfile.log | tee 'path/scripts/tj_var1.txt'
# ^^ I guess log file should be appended.

I guess you could have only stdout in the tj_var1.txt file:

#!/bin/bash
dirname=/path
tempfile=myTempfileName
find "$dirname" -type f > "$tempfile"
sed 's_.*/__' "$tempfile" | sort | uniq -d |
while read fileName
do
grep "$fileName" "$tempfile"
done
> | tee 'path/scripts/tj_var1.txt'
> 2>&1 | tee -a path/logfile.log

It basically finds out the duplicate filenames and outputs it.

dirname=/path
find "$dirname" -type f -printf "%f\n" |
sort | uniq -d |
tee -a path/logfile.log | tee 'path/scripts/tj_var1.txt'

How to tee to stderr?

Note that this is dependant on OS support, not any built-in power in tee, so isn’t universal (but will work on MacOS, Linux, Solaris, FreeBSD, probably others).

Читайте также:  Kerio control linux аналог

On what occasion does ‘tee’ deletes the file it was writing on?

On what occasion does ‘tee’ deletes the file it was writing on?

tee does not delete nor truncate the file once it has started writing.

Is there a filesize limit or timeout in tee?

Is there any known behavior of tee that it deletes a file?

Note that file can be removed, but the process ( tee ) still will wrote the open file descriptor, but the file will not be accessible (see man 3 unlink ).

Источник

How to redirect stderr to a file [duplicate]

@terdon This is a more specific question, and it rightly shows up in google search for the more specific question, which is a good thing.

@nroose yes, and it will keep showing up, that won’t change. But any new answers should go to the more general question.

2 Answers 2

There are two main output streams in Linux (and other OSs), standard output (stdout) and standard error (stderr). Error messages, like the ones you show, are printed to standard error. The classic redirection operator ( command > file ) only redirects standard output, so standard error is still shown on the terminal. To redirect stderr as well, you have a few choices:

    Redirect stdout to one file and stderr to another file:

For more information on the various control and redirection operators, see here.

So hashdeep -rXvvl -j 30 -k checksums.txt /mnt/app/ >> result_hashdeep.txt 2> error_hashdeep.txt & or hashdeep -rXvvl -j 30 -k checksums.txt /mnt/app/ >> result_hashdeep.txt 2>&1 or hashdeep -rXvvl -j 30 -k checksums.txt /mnt/app/ &> result_mixed.txt

@AndréM.Faria yes. But the last two commands are equivalent, they will send both error and output to the same file.

As in the link you provided, I could use |& instead of 2>&1 they are equivalent, thanks for you time.

First thing to note is that there’s couple of ways depending on your purpose and shell, therefore this requires slight understanding of multiple aspects. Additionally, certain commands such as time and strace write output to stderr by default, and may or may not provide a method of redirection specific to that command

Basic theory behind redirection is that a process spawned by shell (assuming it is an external command and not shell built-in) is created via fork() and execve() syscalls, and before that happens another syscall dup2() performs necessary redirects before execve() happens. In that sense, redirections are inherited from the parent shell. The m&>n and m>n.txt inform the shell on how to perform open() and dup2() syscall (see also How input redirection works, What is the difference between redirection and pipe, and What does & exactly mean in output redirection )

Shell redirections

Most typical, is via 2> in Bourne-like shells, such as dash (which is symlinked to /bin/sh ) and bash ; first is the default and POSIX-compliant shell and the other is what most users use for interactive session. They differ in syntax and features, but luckily for us error stream redirection works the same (except the &> non standard one). In case of csh and its derivatives, the stderr redirection doesn’t quite work there.

Читайте также:  Rescue mode kali linux

Let’s come back to 2> part. Two key things to notice: > means redirection operator, where we open a file and 2 integer stands for stderr file descriptor; in fact this is exactly how POSIX standard for shell language defines redirection in section 2.7:

For simple > redirection, the 1 integer is implied for stdout , i.e. echo Hello World > /dev/null is just the same as echo Hello World 1>/dev/null . Note, that the integer or redirection operator cannot be quoted, otherwise shell doesn’t recognize them as such, and instead treats as literal string of text. As for spacing, it’s important that integer is right next to redirection operator, but file can either be next to redirection operator or not, i.e. command 2>/dev/null and command 2> /dev/null will work just fine.

The somewhat simplified syntax for typical command in shell would be

 command [arg1] [arg2] 2> /dev/null 

The trick here is that redirection can appear anywhere. That is both 2> command [arg1] and command 2> [arg1] are valid. Note that for bash shell, there there exists &> way to redirect both stdout and stderr streams at the same time, but again — it’s bash specific and if you’re striving for portability of scripts, it may not work. See also Ubuntu Wiki and What is the difference between &> and 2>&1.

Note: The > redirection operator truncates a file and overwrites it, if the file exists. The 2>> may be used for appending stderr to file.

If you may notice, > is meant for one single command. For scripts, we can redirect stderr stream of the whole script from outside as in myscript.sh 2> /dev/null or we can make use of exec built-in. The exec built-in has the power to rewire the stream for the whole shell session, so to speak, whether interactively or via script. Something like

#!/bin/sh exec 2> ./my_log_file.txt stat /etc/non_existing_file 

In this example, the log file should show stat: cannot stat ‘/etc/non_existing_file’: No such file or directory .

Yet another way is via functions. As kopciuszek noted in his answer, we can write function declaration with already attached redirection, that is

some_function() < command1 command2 >2> my_log_file.txt 

Commands writing to stderr exclusively

Commands such as time and strace write their output to stderr by default. In case of time command, the only viable alternative is to redirect output of whole command , that is

time echo foo 2>&1 > file.txt 

alternatively, synchronous list or subshell could be redirected if you want to separate the output ( as shown in related post ):

Other commands, such as strace or dialog provide means to redirect stderr. strace has -o option which allows specifying filename where output should be written. There is also an option for writing a textfile for each subprocess that strace sees. The dialog command writes the text user interface to stdout but output to stderr, so in order to save its output to variable ( because var=$(. ) and pipelines only receives stderr ) we need to swap the file descriptors

result=$(dialog --inputbox test 0 0 2>&1 1>/dev/tty); 

but additionally, there is —output-fd flag, which we also can utilize. There’s also the method of named pipes. I recommend reading the linked post about the dialog command for thorough description of what’s happening.

Источник

Оцените статью
Adblock
detector