Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

This code snippet is from Advanced Bash Scripting Guide.

# Redirecting only stderr to a pipe.

exec 3>&1                              # Save current "value" of stdout.
ls -l 2>&1 >&3 3>&- | grep bad 3>&-    # Close fd 3 for 'grep' (but not 'ls').
#              ^^^^   ^^^^
exec 3>&-                              # Now close it for the remainder of the script.

# Thanks, S.C.

The comments explains the code closes fd 3 only for 'grep'. but the closes fd 3 twice. Why we need to do like this? Is it wrong to close fd 3 only one time for 'grep' like this?

ls -l 2>&1 >&3 | grep bad 3>&-    
share|improve this question
add comment

3 Answers 3

Neither ls nor grep need anything open on their fd 3 (they don't use that fd), so it's better practice to close it (release the resources you don't need). We only use that fd 3 to be able to restore ls stdout to the original one (before executing ls).

Remember that in a pipe line, all commands run concurrently in their own process. Redirections apply for each separately, so closing the fd 3 for one cannot close it for the other one.

It won't make much difference in practice in this case to close or not to close other than you might reach a limit on the number of file descriptors sooner if you don't.

In other cases (commands themselves starting other processes for instance), that may hold resources and cause problems. Think for instance if stdout is a pipe for instance, a background process may end up inheriting that fd, preventing any reader from that pipe to see EOF until that process has returned.

A better way to write it would be:

{ ls -l 2>&1 >&3 3>&- | grep bad 3>&-; } 3>&1

That way, the fd 3 is only redirected temporarily for the duration of that command group (and restored afterwards or closed).

See the difference:

$ { ls -l /proc/self/fd 2>&1 >&3 3>&- | grep bad 3>&-; } 3>&1
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:29 0 -> /dev/pts/4
lrwx------ 1 stephane stephane 64 Apr  2 09:29 1 -> /dev/pts/4
l-wx------ 1 stephane stephane 64 Apr  2 09:29 2 -> pipe:[575886]
lr-x------ 1 stephane stephane 64 Apr  2 09:29 3 -> /proc/20918/fd/
$ { ls -l /proc/self/fd 2>&1 >&3  | grep bad 3>&-; } 3>&1
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:29 0 -> /dev/pts/4
lrwx------ 1 stephane stephane 64 Apr  2 09:29 1 -> /dev/pts/4
l-wx------ 1 stephane stephane 64 Apr  2 09:29 2 -> pipe:[575900]
lrwx------ 1 stephane stephane 64 Apr  2 09:29 3 -> /dev/pts/4
lr-x------ 1 stephane stephane 64 Apr  2 09:29 4 -> /proc/20926/fd/

In the second invocation, ls had its fd 3 also open to the terminal (which was open on stdout at the time I ran the pipeline) for no good reason.

Note that in ksh93, with exec, you don't need to close those fds as fds other than 0, 1, 2 are automatically closed upon executing a command.

$ ksh93 -c 'exec 3>&1; ls -l /dev/fd/'
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:34 0 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 1 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 2 -> /dev/pts/16
lr-x------ 1 stephane stephane 64 Apr  2 09:34 3 -> /proc/21105/fd
$ ksh93 -c 'exec 3>&1; ls -l /dev/fd/ 3>&3'
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:34 0 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 1 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 2 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 3 -> /dev/pts/16
lr-x------ 1 stephane stephane 64 Apr  2 09:34 4 -> /proc/21108/fd

I don't know why it says (but not 'ls') above, sounds like a mistake (possibly mine ;-).

share|improve this answer
3  
Hey so what does #Thanks, S.C mean there? –  goldilocks Apr 2 at 7:44
    
Do I understand correctly, that pipe is created before fd3 is closed by 3>&- (for ls part). Is it described somewhere in which order descriptors and/or pipes are created? –  vyegorov May 22 at 19:33
1  
@vyegorov, the pipe has to be created before the fork (otherwise, the two commands couldn't both access it), so before the redictions. The order is pipe, fork, and then redirection applied in each part of the pipe (in the different process), and then the commands are executed (after the forks, the difference processes work independently, so one might still be doing redirections while the other one has alread executed its command). –  Stéphane Chazelas May 22 at 19:45
add comment

Close fd3 twice because you fork two subshell in script, each subshell is inherited and copy parent's file descriptor. Close fd3 in each subshell doesn't affect others (and the parent, too).

So the comment line is very unclear and causes misleading.

If you want to redirect only stder to pipe, you can use process substitution:

ls -l 2> >(grep bad)

or swap stderr and stdout:

ls -l 3>&1 1>&2 2>&3 | grep bad
share|improve this answer
add comment

All redirections in the |pipeline affect the specific processes they're applied to. Unless you specifically group the processes therein, two redirections like >&3 3>&- will close &1, not save it. You can only have one direction for the file unless some process duplicates it for you. This is what you want to do, I think:

ls -l 2>&- | tee /dev/stderr | grep bad

That'll get ls output to grep but close ls stderr and print all of ls output to the terminal. If you then wanted to grep it again you could:

ls -l 2>&- | { tee /dev/stderr | grep bad ; } 2>&1 | tee /dev/stderr | grep good

But if you want only stderr in the pipe that's easy:

{ ls -l | : ; } 2>&1 | grep bad

And to swap you can just follow it after grep :

{ { ls -l >&3 ; } 2>&1 | grep bad ; } 3>&1 
share|improve this answer
    
Could you please not italicize text in code tags? It makes it unneccesarily difficult to read for no benefit at all. –  jasonwryan Apr 2 at 7:11
    
@jasonwryan Which codeblock is italicized? Or do mean the inline stuff? I'm kinda flattered you're reading it at all, though. –  mikeserv Apr 2 at 7:13
    
Edited my comment: the code tags. The combination of italics and the grey background render it almost illegible for me. –  jasonwryan Apr 2 at 7:14
    
@jasonwryan Oh - you get a grey background? Mine's all white all of the time - which is why I do it in the first place! I can bold it, but otherwise it just disappears for me... –  mikeserv Apr 2 at 7:16
    
This is how it looks to me: the bolding is a bit gratuitious as well, but thanks for changing it. –  jasonwryan Apr 2 at 7:21
show 4 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.