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.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

When I run the command type type, this gives me a result to standard output.

So, now I try this command:

type type > abc.txt

It redirects the standard output to abc.txt. So, if this command executes on the same process (bash itself), then file descriptor 1 points to abc.txt. After that, whenever I use run a command, these results should go to file abc.txt, because file descriptor 1 points to abc.txt.

However, results always go to standard output. Does this mean that shell built-ins run under a forked process?

share|improve this question
    
I've proposed an edit to your question to make it easier to read. Hopefully, this is alright with you :D – cat Mar 1 at 22:57
1  
The basic answer is 'no'. Consider cd. If it executed in a child process it would have no effect. Indeed very early in the piece it did execute in a child process, leading to much bewilderment among the UNIX developers ... until they twigged that some commands had to be built-in and executed in the current shell process. – EJP Mar 2 at 9:26
up vote 10 down vote accepted

Output redirection

File descriptor 1 represents stdout, the standard output stream. When output redirection is used in type type > abc.txt, the shell opens the file abc.txt for writing and the file descriptor 1 is modified so that it points to the open file instead of the terminal device.

However, this redirection only applies to the current command being executed so this does not imply that the command executes in a forked process (or subshell).

Persistent redirection

If you wanted the redirection to persist, you could use the exec shell builtin to modify the file descriptors, e.g., to redirect standard output for successive commands, run the following command.

exec >abc.txt

Be careful running this as your shell session will be hard to use if all command output is being redirected to a file instead of your terminal device. You can restore the stdout file descriptor to the terminal output device by redirecting it to the same device pointed to by stderr (file descriptor 2):

exec >&2

Related resources

For more details, see:

share|improve this answer

You're right in that normally, to redirect a command, all the shell has to do is fork a child process, do the redirection in the child only (the parent file descriptors are not affected) and then execute the command in there (while the parent just waits for the child).

For builtin commands (or compound commands, or functions...), there is no fork nor exec. Yet after a redirected builtin command terminates, the file descriptors are back to how they were before.

To do that, the shell just saves a copy of the redirected file descriptor to another file descriptor before doing the redirection, marks that file descriptor with the O_CLOEXEC flag (in case the builtin ends up executing a command like eval or command would), and when the builtin returns, the shell restores the file descriptor.

You can see that if you run for instance:

strace sh -c 'echo test > /dev/null; :'

You'll see (only relevant entries included):

  • open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3: opens the file to redirect to.
  • fcntl(1, F_DUPFD, 10) = 10: duplicate the original stdout to the first available fd >= 10 (here 10).
  • fcntl(10, F_SETFD, FD_CLOEXEC) = 0: set the O_CLOEXEC flag on it
  • dup2(3, 1) = 1: make the redirected file stdout
  • close(3) = 0: no longer needed
  • write(1, "test\n", 5) = 5: echo runs and writes "test\n" on its stdout (now redirected to /dev/null).
  • dup2(10, 1) = 1: restore stdout
  • close(10) = 0: close fd 10 no longer needed.

Note that in the Bourne shell, redirecting a compound command did cause a fork (as in a=0; { a=1; echo "$a"; } >&2; echo "$a" would give you 1 then 0). To work around that, you actually had to do the above by hand:

Instead of

while cmd1; do cmd2; i=`expr "$i" + 1`; done > file

You had to do:

exec 3>&1 > file
while cmd1 3>&-; do cmd2 3>&-; i=`expr "$i" + 1 3>&-`; done
exec >&3 3>&-

(with a 3>&- for each command as that fd doesn't have the O_CLOEXEC flag).

And in early versions of the Bourne shell, you could not redirect builtins. On a Unix V7 on an emulated pdp11:

$ eval a=1 > /tmp/x
illegal io
$ read a < /etc/passwd
illegal io
share|improve this answer

As soon as the command completes the input files is closed, making it available for writing by other processes, file descriptor is released, and the child process terminated.

share|improve this answer

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.