UP | HOME

Redirection
Processes
COP-3402

Table of Contents

Recall: redirection in bash

We can use bash to change (redirect) where stdio goes to and comes from.

cat > outfile.txt
grep define < /usr/include/stdio.h
How does bash implement this?

What's the role of standard I/O in making this possible?

Recall: file I/O

  • open() - open a file for I/O
  • read() - read bytes from an open file
  • write() - write bytes from an open file
After we open a file, how do we reference that open file?

File descriptors

  • Index into kernel table of open files
  • Returned by open
  • Used in calls to read/write

Writing to standard out

write_stdout.c

#include <fcntl.h>     // O_RDONLY, open
#include <unistd.h>    // read, write
#include <stdlib.h>    // exit
#include <stdio.h>     // perror, fprintf
#include <string.h>    // strlen

int main(int argc, char **argv) {
        char *msg1 = "hello, world!\n";
        ssize_t msg1c = strlen(msg1);

        if (-1 == write(STDOUT_FILENO, msg1, msg1c)) {
                perror("write");
                exit(EXIT_FAILURE);
        }
}

What's the difference between stdout and STDOUT_FILENO?

printf vs. write?

Why don't we have to open STDOUT_FILENO?

Duplicate a file descriptor

man 2 dup  # LPI chapter 5

Copies the file descriptor, so that we have two.

Both can write to the same open file.

duplicate_stdout.c

#include <fcntl.h>     // O_RDONLY, open
#include <unistd.h>    // dup, dup2, read, write
#include <stdlib.h>    // exit
#include <stdio.h>     // perror, fprintf
#include <string.h>    // strlen

int main(int argc, char **argv) {
        char *msg1 = "hello, world!\n";
        ssize_t msg1c = strlen(msg1);

        if (-1 == write(STDOUT_FILENO, msg1, msg1c)) {
                perror("write");
                exit(EXIT_FAILURE);
        }

  int dupfd = dup(STDOUT_FILENO);
  if (-1 == dupfd) {
    perror("dup");
    exit(EXIT_FAILURE);
  }

  sleep(2);

  char *msg2 = "goodbye!\n";
        ssize_t msg2c = strlen(msg2);

        if (-1 == write(dupfd, msg2, msg2c)) {
                perror("write");
                exit(EXIT_FAILURE);
        }
}

(Optional) duplicate_fd.c

#include <fcntl.h>     // O_RDONLY, open
#include <unistd.h>    // dup, dup2, read, write
#include <stdlib.h>    // exit
#include <stdio.h>     // perror, fprintf
#include <string.h>    // strlen

int main(int argc, char **argv) {
  char *path = "dupfd.out";
  int fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 0644);
  if (-1 == fd) {
    perror("open");
    exit(1);
  }

        char *msg1 = "hello, world!\n";
        ssize_t msg1c = strlen(msg1);

        if (-1 == write(fd, msg1, msg1c)) {
                perror("write");
                exit(EXIT_FAILURE);
        }

  int dupfd = dup(fd);
  if (-1 == dupfd) {
    perror("dup");
    exit(EXIT_FAILURE);
  }

  char *msg2 = "goodbye!\n";
        ssize_t msg2c = strlen(msg2);

        if (-1 == write(fd, msg2, msg2c)) {
                perror("write");
                exit(EXIT_FAILURE);
        }
}

This is the same as duplicating stdout, but in this case we open a file and duplicate that open file's descriptor.

What use is dup?

Redirection is one use!

The dup2 variant

  • dup2 replaces an existing file descriptor
man 2 dup2  # LPI chapter 5

What are the file descriptors for stdin, stdout, and stderr?

Standard I/O file descriptors

/usr/include/unistd.h

#define STDIN_FILENO    0       /* Standard input.  */
#define STDOUT_FILENO   1       /* Standard output.  */
#define STDERR_FILENO   2       /* Standard error output.  */

Replacing a file descriptor

dup2(oldfd, newfd)

To replace stdout:

dup2(oldfd, STDOUT_FILENO)

redirect_stdout.c

#include <fcntl.h>     // O_RDONLY, open
#include <unistd.h>    // dup, dup2, read, write
#include <stdlib.h>    // exit
#include <stdio.h>     // perror, fprintf
#include <string.h>    // strlen

int main(int argc, char **argv) {
        char *msg1 = "write to original stdout\n";
        ssize_t msg1c = strlen(msg1);

        if (-1 == write(STDOUT_FILENO, msg1, msg1c)) {
                perror("write");
                exit(EXIT_FAILURE);
        }

  char *path = "redirect_stdout.out";
  int fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 0644);
  if (-1 == fd) {
    perror("open");
    exit(1);
  }

  if (-1 == dup2(fd, STDOUT_FILENO)) {
    perror("dup2");
    exit(EXIT_FAILURE);
  }

  char *msg2 = "written to redirected stdout\n";
        ssize_t msg2c = strlen(msg2);

        if (-1 == write(STDOUT_FILENO, msg2, msg2c)) {
                perror("write");
                exit(EXIT_FAILURE);
        }

  printf("where will printf go now?\n");
}

Why does printf now go to the file instead of the terminal?

(Diagram)

  • bash creates process for redirect_stdout, giving it the terminal as stdout
  • redirect_stdout writes to stdout (the terminal)
  • redirect_stdout opens a new file (redirect_stdout.out)
  • redirect_stdout redirects stdout to that file (dup2)
  • redirect_stdout writes to stdout, which is now the file

(Optional) Redirecting stdin

#include <fcntl.h>     // O_RDONLY, open
#include <unistd.h>    // dup, dup2, read, write
#include <stdlib.h>    // exit
#include <stdio.h>     // perror, fprintf
#include <string.h>    // strlen

int main(int argc, char **argv) {
  pid_t pid;

  if (argc < 2) {
    printf("USAGE: %s file_to_read\n", argv[0]);
    exit(EXIT_FAILURE);
  }
  char *path = argv[1];

  const int BUFSIZE = 10;
  char buf[BUFSIZE];
  ssize_t bytes = read(STDIN_FILENO, buf, BUFSIZE);
  if (-1 == bytes) {
    perror("read");
    exit(1);
  }

        printf("bytes from original stdin: %ld\n", bytes);

  for (int i = 0; i < bytes; i++) {
    putchar(buf[i]);
  }
  putchar('\n');

  int fd = open(path, O_RDONLY);
  if (-1 == fd) {
    perror("open");
    exit(1);
  }

  if (-1 == dup2(fd, STDIN_FILENO)) {
    perror("dup2");
    exit(EXIT_FAILURE);
  }

  bytes = read(STDIN_FILENO, buf, BUFSIZE);
  if (-1 == bytes) {
    perror("read");
    exit(1);
  }

        printf("bytes from redirected stdin: %ld\n", bytes);

  for (int i = 0; i < bytes; i++) {
    putchar(buf[i]);
  }
  putchar('\n');
}

Redirecting a child process's I/O

child.c

#include <fcntl.h>     // O_RDONLY, open
#include <unistd.h>    // dup, dup2, read, write, fork
#include <stdlib.h>    // exit
#include <stdio.h>     // perror, fprintf
#include <string.h>    // strlen
#include <inttypes.h>  // intmax_t
#include <sys/wait.h>  // wait

int main(int argc, char **argv) {
  pid_t pid;

  if (argc < 2) {
    printf("USAGE: %s file_to_write\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  char *path = argv[1];
  int fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 0644);
  if (-1 == fd) {
    perror("open");
    exit(1);
  }

  switch (pid = fork()) {
  case -1:
    perror("fork");
    exit(EXIT_FAILURE);
    break;
  case 0:
    // child

    if (-1 == dup2(fd, STDOUT_FILENO)) {
      perror("dup2");
      exit(EXIT_FAILURE);
    }

    printf("child process\n");
    exit(EXIT_SUCCESS);
    break;
  default:
    // parent
    printf("parent process\n");
    exit(EXIT_SUCCESS);
    break;
  }
}

child_exec.c

#include <fcntl.h>     // O_RDONLY, open
#include <unistd.h>    // dup, dup2, read, write, fork
#include <stdlib.h>    // exit
#include <stdio.h>     // perror, fprintf
#include <string.h>    // strlen
#include <inttypes.h>  // intmax_t
#include <sys/wait.h>  // wait

int main(int argc, char **argv) {
  pid_t pid;

  if (argc < 2) {
    printf("USAGE: %s file_to_write\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  char *path = argv[1];
  int fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 0644);
  if (-1 == fd) {
    perror("open");
    exit(1);
  }

  switch (pid = fork()) {
  case -1:
    perror("fork");
    exit(EXIT_FAILURE);
    break;
  case 0:
    // child

    if (-1 == dup2(fd, STDOUT_FILENO)) {
      perror("dup2");
      exit(EXIT_FAILURE);
    }

    char *prog = "/usr/bin/ls";
    char *newargv[] = { prog, "/", NULL };
    char *newenv[] = { NULL };
    execve(prog, newargv, newenv);
    perror("execve");
    _exit(EXIT_FAILURE);
    break;
  default:
    // parent
    printf("parent process\n");
    exit(EXIT_SUCCESS);
    break;
  }
}

Author: Paul Gazzillo

Created: 2025-02-17 Mon 00:35

Validate