I am having trouble having redirection working with the ability to hit enter and remain in the same shell.

My problem is if user types: ./a.out < foo.txt (where foo.txt contains a command like date)
I want to execute that command and exit the shell.However,if the user compiles the code without using redirection: ./a.out, I want to keep the user in the shell even if he presses enter.

e.g.
shell>> (pressed Enter)
shell>> (pressed Enter)
shell>> ls

The problem arises because both enter and end of file are read in as '\0' using gets. Is there a way, such that both functionality can be achieved. Any help is greatly appreciated.

Snippet of my code:

int main(int argc, char *argv[])
{
  int nbytes = 100;
  char my_string[nbytes];
  string arg_list[20];

  while(1){

    printf("shell>> ");
    my_string[0] = '\0';
    gets( my_string );

    //this should happen when end of file is reached
    if(my_string[0] == '\0'){ 
       exit(0);
    }

   // this should happen when user presses enter
    while(strlen(my_string)==0){
       printf("shell>>");
       gets(my_string);
    }

Dani AI

Generated

The root cause in the original snippet is that the code both pre-initializes the buffer (my_string[0] = '\0') and never checks the input function's return value. That makes an EOF (where the read function returns NULL) look identical to a user pressing Enter (an empty-but-valid line). As pointed out, gets is unsafe and you must inspect the read call's return — and as explained, feof alone doesn't tell you whether input came from a file or a terminal.

A robust, simple strategy:

  • Detect whether stdin is interactive at startup (use isatty(fileno(stdin))). If stdin is not a terminal, treat the program as a filter: read lines until EOF and exit.
  • If stdin is a terminal, print the prompt and read lines; an empty line will be returned by fgets/getline (it contains just a newline) while EOF makes the read call return NULL. That lets you distinguish Enter from true EOF reliably.
  • Always check the read function's return value; never rely on a pre-zeroed buffer to detect EOF. Avoid gets entirely.

Example pattern (illustrative):

#include <stdio.h>
#include <unistd.h>

int main(void) {
    char buf[1024];
    int interactive = isatty(fileno(stdin));
    if (interactive) {
        for (;;) {
            fputs("shell>> ", stdout);
            if (!fgets(buf, sizeof buf, stdin)) break; // EOF or error
            if (buf[0] == '\n') continue; // empty line: reprompt
            // process buf...
        }
    } else {
        while (fgets(buf, sizeof buf, stdin)) {
            // process redirected input until EOF
        }
    }
    return 0;
}

Notes and cautions: strip the trailing newline before processing, handle interrupted reads (EINTR) if needed, and consider getline or the GNU readline library for a more feature-rich interactive shell. Do not use gets; it was removed from modern C standards and is unsafe.

Recommended Answers

All 3 Replies

Might want to use something besides gets. From cplusplus.com gets page

http://www.cplusplus.com/reference/cstdio/gets/?kw=gets

The most recent revision of the C standard (2011) has definitively removed this function from its specification.
The function is deprecated in C++ (as of 2011 standard, which follows C99+TC3).

As far as distinguishing end of file from a user pressing ENTER, you don't harness the return value of gets anywhere and you don't test for end of file or errors with feof or ferror. Read the fgets page linked above, in particular the "return" section.

Then again, according to WaltP, don't bother with any of that, just rewrite your code completely, getting rid of all gets commands. Quite simply, there is no RIGHT way to use it.

http://www.gidnetwork.com/b-56.html

Thanks for pointing me to the articles, they were very insightful.

I did find something.

feof(stdin)

can be used to check if the input was read from the file or keyboard.

feof(stdin) can be used to check if the input was read from the file or keyboard.

No, not really. It's not uncommon to signal end-of-file from the keyboard. For example, run the following program and watch it print "Yes" when you signal end-of-file using ctrl+z (Windows) or ctrl+d (POSIX):

#include <stdio.h>

int main(void)
{
    (void)getchar();

    printf("end-of-file: %s\n", feof(stdin) ? "Yes" : "No");

    return 0;
}

Further, and more distressingly, if your code is written as a filter, stdin acts as keyboard input, file input, or even piped input depending on how you do redirection from the command line. All without changing any code. Tell me, is this being run as an interactive program or is input redirected from a file?

#include <stdio.h>
#include <string.h>

char *reverse(char *s)
{
    size_t i = 0;
    size_t j = strlen(s) - 1;

    if (s[j] == '\n') {
        --j;
    }

    while (i < j) {
        char temp = s[i];
        s[i++] = s[j];
        s[j--] = temp;
    }

    return s;
}

int main(void)
{
    char line[BUFSIZ];

    while (fgets(line, sizeof line, stdin)) {
        fputs(reverse(line), stdout);
    }

    return 0;
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.