Bash redirect to stdin after freopen()

82 views Asked by At

I'm encountering a limitation of bash input redirection that's preventing me from accomplishing a task, and I haven't been able to find a way to work around it so far. Essentially, I need to be able to redirect two separate files to a program's stdin separately, as opposed to all at once.

Here's an example program:

#include <stdio.h>

int main(void) {
    char myArr[200][100];

    int i = 0;
    while (fgets(myArr[i], 100, stdin)) {
        i++;
    }

    freopen("/dev/tty", "rw", stdin);

    int choice = 0;
    while (choice != 1 && choice != 2) {
        printf("Enter menu input: ");
        scanf("%d", &choice);
    }

    if (choice == 1) {
        for (int j = 0; j < i; j++) {
            printf("%s\n", myArr[j]);
        }
    } else if (choice == 2) {
        exit(-1);
    }
}

This program takes in input from stdin until EOF is reached, and counts the number of successful reads (akin to counting the number of lines in the input). I'm passing a whole file's worth of input using ./a.out < longInput.txt, which causes fgets to stop reading once the end of longInput.txt is reached.

I use freopen() to reset stdin so that I can start inputting menu options again after longInput.txt has reached EOF. However, this doesn't work as intended when trying to use bash redirection for menu inputs in the second part of the program.

My question is: how can I redirect input from a second input file that only contains menu options, after I've redirected from the first file and reset stdin, without hard-coding that second file name in my call to freopen()?

Assume that I cannot modify the source of that program, and I only have access to a compiled executable. Here are some references I've already tried, to no avail:

Thanks!

1

There are 1 answers

2
Chris Dodd On

Most commonly, you'd do this sort of thing by providing arguments to your program rather than by messing around with stdin. So you'd have something like:

int main(int ac, char **av) {
    if (ac != 3) {
        fprintf(stderr, "usage: %s <data input> <menu input>\n", av[0]);
        exit(1); }
    FILE *dataInput = fopen(av[1], "r");
    if (!dataInput) {
        fprintf(stderr, "can't read data input from %s\n", av[1]);
        exit(1); }
    // read your data input
    char myArr[200][100];
    int i = 0;
    while (i < 200 && fgets(myArr[i], 100, dataInput)) {
        i++;
    }
    
    FILE *meunInput = fopen(av[2], "r");
    if (!menuInput) {
        fprintf(stderr, "Can't open menu input %s\n", av[2]);
        exit(1); }
    // now read your menu input.

Then in bash, you just invoke your program with two arguments with the file names to read from. If you want to read from pipes (running other commands), you can use program redirects:

myprogram <(command that generates data input) <(command that generates menu input)