1

If I wish to redirect all stdout to a file, I would run

my_prog 1> out

If I wish to do the same for stderr, I would run

my_prog 2> err

However, I know that in shell there are other file descriptors as well. For example, the top answer in this question uses a third file descriptor, to which one can send data from the command line via

echo "Some console message" 1>&3

Is there a way for a C program to write to this file descriptor? Does writing to it while no other program is reading it, send its output to the Terminal as well?

3

I am not really familiar with C so there may be better ways to to this but you can simply use the syscall (man 2 write):

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

Where the written data is sent depends on what the file desciptor has opened. The data reaches the terminal only if the file descriptor belongs to a terminal.

If you write to a pipeline or socket and the other side doesn't read the data then the writing application (i.e. the write() call) will block when the buffers are full.

You can see the file descriptors in /proc/$PID/fd

  • 1
    No directory /proc on a Mac. – Alex Aug 11 '17 at 20:20
6

To clarify a few more things. In:

echo foo >&3

echo is not writing "foo\n" to its file descriptor 3. echo is always writing to stdout, to its file descriptor 1. It's calling the write() system call with a 1 integer as its first argument, a pointer to an area in memory that starts with foo\n as the second argument and 4 (the length of foo\n) as the third.

In C, you'd write it write(1, "foo\n", 4). In the code above, the shell redirects the fd 1 to the same open file description as open on fd 3 before calling echo (by way of the dup2() system call). So, even though it's functionally equivalent, it's not the same as doing write(3, "foo\n", 4). In effect, it's something like (simplified):

if (pid = fork())
  waitpid(pid, ...);
else {
  dup2(3, 1);
  execlp("echo", "foo", 0);
} 

And echo does a write(1, "foo\n", 4)

Except that in virtually all shells, echo is builtin, so there's no fork or exec. Instead, the shell does:

saved_stdout = dup(1);
dup2(3, 1);
builtin_echo("foo");
dup2(saved_stdout, 1); close(saved_stdout); /* restore stdout */

(where builtin_echo() is a function that does the write(1, "foo\n", 4) in the same process).

For a command that does write(3, "foo\n", 4), you can have a look at ksh/zsh's print -u3 foo builtin command.

Now, every process is free to use file descriptors as they please. Except that 0, 1 and 2 are by convention reserved for stdin, stdout and stderr. Other fds are generally not special, but in shells (which are just one type of application), fds 0 to <some-value> where some value is at least 9 are reserved for usage by the user (of the shell). The shell will not mingle with them for its own internal soup. For instance my saved_stdout = dup(1) above was an approximation. In effect, the shell will make sure saved_stdout will be a value greater than <some-value>.

Now, since there's no convention attached to fds outside 0,1,2, you can't expect anything to be open on fd 3. Most likely it will be closed. Or if it's not, it's likely the caller of your script will just have forgotten to close it (or to add the O_CLOEXC flag to it) as there would be no reason it left it open for you as nobody expects anything to be open on fd 3.

You would use the fd 3 if you knew it had been open onto something, generally by you in the same script beforehand, like in:

 {
   var=$(cmd 2>&1 >&3)
 } 3>&1

Where fd 3 had been defined as a dup() of fd 1 before we use it for cmd to dup() it back onto fd 1.

2

Assuming the descriptor is open, C can indeed write (or more likely use some higher-level call that will eventually boil down to a write(2)) to that descriptor. This here be write2three.c

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
    int fd;
    if ((fd = dup(3)) == -1) err(1, "dup failed");
    dprintf(fd, "hello\n");
    exit(EXIT_SUCCESS);
}

E.g.

$ make write2three  
cc     write2three.c   -o write2three
$ ./write2three
write2three: dup failed: Bad file descriptor
$ rm out
$ ./write2three 3>out
$ cat out
hello
$ 

You may need to clean up in the shell, afterwards.

$ exec 3>&-
$ 
  • +1: This answers the question of how to make writing to a different file descriptor work together with a variant of printf. – Alex Aug 12 '17 at 15:23
0

Is it possible to write to other file descriptors in C?

Yes.

Most processes inherit 3 open file descriptors from the parent that executed them.

stdin, stdout and stderr. These are file descriptors 0 - 2 respectively.

I believe fd 0 (stdin) is opened in read only mode, so your c program can read this file descriptor but not write to it.

When you program opens another file, it gets the next file descriptor in incremental order.

  • But then writing to the file descriptor this way would write to the file, instead of writing to the process expecting input in the form echo "Some console message" 1>&3 – Alex Aug 11 '17 at 20:15
  • 1
    Your example is not c, that is a shell command. stdin is not the 'file', it is usually attached to the controlling terminal. – datUser Aug 11 '17 at 20:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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