开发者

In the Unix/Linux shell programming:the difference between > and >&

开发者 https://www.devze.com 2023-04-09 14:30 出处:网络
int main(void) { char buf[] = \"standard err, output.\\n\"; printf(\"standard output.\\n\"); if (write(STDERR_FILENO,buf, 22) != 22)
int main(void)
{

  char buf[] = "standard err, output.\n";

  printf("standard output.\n");

  if (write(STDERR_FILENO,buf, 22) != 22)
      printf("write err!\n");

  exit(0);
}

Compile using:

gcc -Wall text.c

Then running in the shell:

  1. ./a.out > outfile 2 >& 1

    Result:outfile´s content are:

    standard err, output.                
    standard output.
    
  2. ./a.out 2 >& 1 >outfile

    Result:

    This first prints to the terminal: standard err, output.

    and the content of outfile are: standard output.

Questions:

  • I want to ask the difference between 开发者_如何学编程2 >& fd and 2 > file.

    Are they all equal to the function dup()?

  • Another question: why are the contents of outfile:

     standard err, output. 
     standard output.
    

    I expected the content of outfile to be:

     standard output. 
     standard err, output 
    


Actually, in bash, >& is quite similar to dup2. That is, the file descriptor to which it is applied will refer to the same file as the descriptor to the right. So:

$ ./a.out > outfile 2>& 1

It will redirect stdout(1) to the file outfile and, after that, will dup2 stderr(2) to refer to the same file as stdout(1). That is, both stdout and stderr are being redirected to the file.

$ ./a.out 2>& 1 >outfile

It will redirect stderr(2) to refer to the same file as stdout(1), that is, the console, and after that, will redirect stdout(1) to refer to the file outfile. That is, stderr will output to the console and stdout to the file.

And that's exactly what you are getting.


Paradigm Mixing


While there are reasons to do all of these things deliberately, as a learning experience it is probably going to be confusing to mix operations over what I might call "domain boundaries".

Buffered vs non-buffered I/O

The printf() is buffered, the write() is a direct system call. The write happens immediately no matter what, the printf will be (usually) buffered line-by-line when the output is a terminal and block-by-block when the output is a real file. In the file-output case (redirection) your actual printf output will happen only when you return from main() or in some other fashion call exit(3), unless you printf a whole bunch of stuff.

Historic csh redirection vs bash redirection

The now-forgotten (but typically still in a default install) csh that Bill Joy wrote at UCB while a grad student had a few nice features that have been imported into kitchen-sink shells that OR-together every shell feature ever thought of. Yes, I'm talking about bash here. So, in csh, the way to redirect both standard output and standard error was simply to say cmd >& file which was really more civilized that the bag-of-tools approach that the "official" Bourne shell provided. But the Bourne syntax had its good points elsewhere and in any case survived as the dominant paradigm.

But the bash "native" redirection features are somewhat complex and I wouldn't try to summarize them in a SO answer, although others seem to have made a good start. In any case you are using real bash redirection in one test and the legacy-csh syntax that bash also supports in another, and with a program that itself mixes paradigms. The main issue from the shell's point of view is that the order of redirection is quite important in the bash-style syntax while the csh-style syntax simply specifies the end result.


There are several loosely related issues here.

Style comment: I recommend using 2>&1 without spaces. I wasn't even aware that the spaced-out version works (I suspect it didn't in Bourne shell in the mid-80s) and the compressed version is the orthodox way of writing it.

The file-descriptor I/O redirection notations are not all available in the C shell and derivatives; they are avialable in Bourne shell and its derivatives (Korn shell, POSIX shell, Bash, ...).

The difference between >file or 2>file and 2>&1 is what the shell has to do. The first two arrange for output written to a file descriptor (1 in the first case, aka standard output; 2 in the second case, aka standard error) to go to the named file. This means that anything written by the program to standard output goes to file instead. The third notation arranges for 2 (standard error) to go to the same file descriptor as 1 (standard output); anything written to standard error goes to the same file as standard output. It is trivially implemented using dup2(). However, the standard error stream in the program will have its own buffer and the standard output stream in the program will have its own buffer, so the interleaving of the output is not completely determinate if the output goes to a file.

You run the command two different ways, and (not surprisingly) get two different results.

  1. ./a.out > outfile 2>&1

    I/O redirections are processed left to right. The first one sends standard output to outfile. The second sends standard error to the same place as standard output, so it goes to outfile too.

  2. ./a.out 2>&1 >outfile

    The first redirection sends standard error to the place where standard output is going, which is currently the terminal. The second redirection then sends standard output to the file (but leaves standard error going to the terminal).

The program uses the printf() function and the write() system call. When the printf() function is used, it buffers its output. If the output is going to a terminal, then it is normally 'line buffered', so output appears when a newline is added to the buffer. However, when the output is going to a file, it is 'fully buffered' and output does not appear until the file stream is flushed or closed or the buffer fills. Note that stderr is not fully buffered, so output written to it appears immediately.

If you run your program without any I/O redirection, you will see:

standard output. 
standard err, output

By contrast, the write() system call immediately transfers data to the output file descriptor. In the example, you write to standard error, and what you write will appear immediately. The same would have happened if you had used fprintf(stderr, ...). However, suppose you modified the program to write to STDOUT_FILENO; then when the output is to a file, the output would appear in the order:

standard err, output
standard output. 

because the write() is unbuffered while the printf() is buffered.


The 2>&1 part makes the shell do something like that:

dup2(1, 2);

This makes fd 2 a "copy" of fd 1.

The 2> file is interpreted as

fd = open(file, ...);
dup2(fd, 2);

which opens a file and puts the filedescriptor into slot 2.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号