tags:

views:

96

answers:

5

Hi experts,

My program needs to accept three kinds of input commands below:

./Myprogram input.txt
./Myprogram < input.txt
./Myprogram

I'm thinking about using argc to check the number of arguments to resolve the first two situations (since redirection doesn't count as an argument). But then I stuck on the last case, which simply waits for an user input.

I'm wondering if there is a way to tell if a redirection is present in the shell command?

For a more complicated scenario such as a mix of redirection and argv forms (see below). Is there a way to do it or it's simply a bad design for taking user commands?

./Myprogram input1.txt input2.txt input3.txt
./Myprogram input1.txt < input2.txt input3.txt
./Myprogram

Any help will be much appreciated!

Z.Zen

+6  A: 

Redirection will never be seen by your program as an argument. So in:

./Myprogram input.txt
./Myprogram < input.txt
./Myprogram

the second and third forms are identical. As for your second set of possibilities:

./Myprogram input1.txt input2.txt input3.txt
./Myprogram input1.txt < input2.txt input3.txt
./Myprogram

the second line is equivalent to:

./Myprogram input1.txt input3.txt < input2.txt

and it's also indistinguishable from:

./Myprogram input1.txt input3.txt

(the only different being where standard input actually comes from).

A typical way some programs handle mixed input from stdin and files specified on the command line is to accept "-" as a special filename meaning "use stdin as the input file at this position in the argument list". Many such programs will default to processing a singleton-list of "-" if the argument list is empty.

R..
@R.. Thank you! It seems that I forgot the 2nd and 3rd cases are the same to the program. Since I don't really need to know whether there is a person on the other end, the handling code for these two cases should be the same. I guess I will just try to avoid the second case in the complicated scenario since it's not what users usually expect and it's just unnecessary for the purpose of my program.
Z.Zen
A: 

I'm wondering if there is a way to tell if a redirection is present in the shell command?

No. From your program's point of view there is no difference between these two cases:

./Myprogram < input.txt
./Myprogram

In both the cases the program is not taking any command line argument and would get it's input from the standard input.
In the first case it's the shell that is connecting the contents of the file input.txt to the stdin of your program your program knows nothing about this.

codaddict
While this answer is correct, it's possible that the OP really wanted to know if it's possible to detect whether stdin is a terminal or an ordinary file.
R..
+1  A: 

@R.. is correct for the usual cases.

If you want to have interactive behavior in case #3 but not #2, beyond letting the terminal buffer the user's input by line, you can use isatty (specifically isatty(0)) to determine whether there's a person on the other end.

This is not standard C, but neither is the notion of a terminal or a shell!

Potatoswatter
+4  A: 

The basic algorithm is:

if (there are no arguments left after parsing options)
    call_function(stdin);
else
{
    foreach remaining argument
    {
        FILE *fp;
        if (strcmp(argument, "-") == 0)
            call_function(stdin);
        else if ((fp = fopen(argument, "r")) == 0)
            ...error handling...
        else
        {
            call_function(fp);
            fclose(fp);
        }
    }
}

You could pass the file name to the 'call_function()' too, and sometimes I write the code with the output file stream specified. That function ('call_function()') is what processes one file - reading to the end of the file. It does not close the file; it was given an open file and should not close it.

The first 'if' deals with the I/O redirection case, of course.

I wrote, many years go, a function to handle this loop. It simplifies my life whenever I need to write a command in this 'UNIX filter' idiom - which is quite often. Along with a standardized error reporting package, it greatly simplifies life. Having it as a function also permits me to use variants on it, such as one that creates a backup of the file before it is overwritten, or which safely overwrites the file if the function completes successfully.

Jonathan Leffler
Thanks for telling me this battle-tested filter algorithm!
Z.Zen
Special handling of `'-'` argument as `stdin` might be useful.
J.F. Sebastian
@J.F.Sebastien: oh, yeah; good point - that's buried in the function (which I last modified 2 1/2 years ago). `if (strcmp(argument, "-") == 0) call_function(stdin); else { FILE *fp = ...as above...}`.
Jonathan Leffler
A: 

It is possible to tell whether there is data to read by using select() on stdin. This will not tell you whether there is a redirection (you won't be able to tell when the file is empty, or when for some reason the user managed to put something on stdin before your program got a chance to test for it). Whether it works for your case or not depends on what you want to do in borderline cases.

Alternatively, you can use isatty() on stdin to find out if it's a tty or not. It's what most programs will do to find out whether they are interactive or not, and probably better in your case.

Now, you may notice that blocking waiting for user input in the third case is what all standard tools do, and is probably the behavior most users expect of your program too.

Jean