Pipes and command line arguments not working

HI guys.

Wrote an extremely simple program that displays the command line arguments a user types. It works all well and good when running the program with some command line args. ./CommandArg my name is adam . This prints my /n name /n is /n adam

But when I create a file called hello.txt, save its content's with " hello from a txt file" and finally pipe the output of the cat command to my program, nothing displays.

So am I misunderstanding what piping does? I thought piping takes the output of a program as input to another program and obviously I thought this input would be input through the command line args?

1
2
3
4
5
6
7
8
9
10
11
12
13

#include <iostream>

using namespace std;

int main(int argc,char* argv[])
{
    for(int i = 1; i < argc; ++i){

        cout << argv[i] << endl;
    }
}
Last edited on
Piping is when the stdout (e.g. cout) of one program becomes the stdin (e.g. cin) for another program. Separate concept from command-line arguments.

The xargs program can be used to convert stdin into usable command-line arguments.
Last edited on
Makes sense, so what cat hello.txt | CommandArg is doing is the following;

1) | creates a pipe
2) sets stdin of CommandArg to stdout of cat
3) runs the programs

So now I'm faced with yet another snag, so the atty() function tests if stdin is a keyboard(I think?), if so, then the following block gets the redirected information.

but the problem is IF I run the program with redirection then stdin is now permanently set to the output of a program such as cat. Let's say I ask the user to enter an age regardless of if the program is running from a terminal or not, the age will not be gotten from the keyboard but still from the pipe. So how do I change or maybe restore stdin of my program back to the keyboard?

Side question: why is the function isatty() named so? tty is short hand for terminal these days, why are we checking if it's a terminal, shouldn't we be checking if it's a keyboard? so maybe isakeyboard()?

Even if we use piping such as cat hello.txt | ./CommandArg
We are still running this command in the terminal (tty) ??????


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

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

using namespace std;


int main(int argc,char* argv[])
{  

   // redirection read
    if(!isatty(fileno(stdin))){

     string p;

     while(cin >> p)
        cout << p << endl;

    for(int i = 1; i < argc; ++i)
        cout << argv[i] << endl;
    }else{

       cout << "enter your name" << endl;
       string p;
       getline(cin,p);
       cout << p << endl;
    }

    cout << "enter age" << endl;
    int age;
    cin >> age;

}
Last edited on
TTY stands for TeleTYpewriter, see https://askubuntu.com/questions/481906/what-does-tty-stand-for

Yeah, you can think of it as meaning "terminal". As in, whether the stream in question is going/coming to a terminal, or is being redirected. So if !isatty is true for stdout or stdin, that means that stream is being used in redirection.

I haven't personally used it myself, so someone else might have better answer, but if I recall, that isatty function is a funny trick used by programs like ls to change their behavior depending on whether the program is being called directly, or is being used with a pipe.
https://stackoverflow.com/questions/8584356/why-does-ls-give-different-output-when-piped

https://github.com/wertarbyte/coreutils/blob/master/src/ls.c
1
2
3
4
5
6
7
8
9
10
11
      if (isatty (STDOUT_FILENO))
        {
          format = many_per_line;
          /* See description of qmark_funny_chars, above.  */
          qmark_funny_chars = true;
        }
      else
        {
          format = one_per_line;
          qmark_funny_chars = false;
        }


but the problem is IF I run the program with redirection then stdin is now permanently set to the output of a program such as cat
"Doctor it hurts whenever I do this!" "Then don't do that".

I don't know of a way to override the pipe to force the user to have to type; I'm sure there is some (perhaps hacky) way to do it, but I doubt it's a common pattern. Why pipe in the first place if you didn't want the program using the piped data?

We are still running this command in the terminal (tty) ?
What does the return value of isatty print?
Last edited on
As far as reassigning stdin and stdout back to the terminal I found this post - https://stackoverflow.com/questions/58044156/how-can-i-redefine-stdin-to-point-to-the-console-after-using-file-redirection-in

The only problem is, it doesn't seem to fix the problem, my application still just ends

and in fact I ran the program without redirection and it seems like enter age does not get printed, so obviously the call to freopen() didn't succeed or at least not in the way I anticipated it to, any ideas?

I mean programs such as ls and many other Linux applications must have solved this problem as you can choose to input from stdin or a pipe.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

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

using namespace std;


int main(int argc,char* argv[])
{

    if(!isatty(fileno(stdin))){

     string p;

     while(cin >> p)
        cout << p << endl;

    for(int i = 1; i < argc; ++i)
        cout << argv[i] << endl;
    }else{

       cout << "enter your name" << endl;
       string p;
       getline(cin,p);
       cout << p << endl;
    }

    FILE* ttystdin = freopen("/dev/tty", "r", stdin);
    FILE* ttystdout = freopen("/dev/tty", "r", stdout);
    FILE* ttystderr = freopen("/dev/tty", "r", stderr);

    if(tty == NULL)
        cout << "didn't work" << endl;

    cout << "enter age" << endl;
    int age;
    cin >> age;
}



As far as isatty(); ah okay, isatty() does NOT check if stdin is a keyboard, it checks if the programs stdin is set to the terminal
because if we use a pipe, then stdin of that program is no longer a terminal but possibly a file.

example:

cat "hello.txt" | sampleProg

here sampleProgs stdin is no longer the terminal but the output of the cat program that takes hello.txt as input(to cat).
Last edited on
UNIX programs usually don't care if cin is a terminal or not. That's a fundamental part of making pipes and file redirection work.

I think this is an xy program. Please describe the actual program you're trying to solve, not the solution that you're having trouble with.

Topic archived. No new replies allowed.