Pipes and command line arguments not working

Oct 9, 2021 at 7:38pm
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 Oct 9, 2021 at 7:39pm
Oct 9, 2021 at 8:32pm
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 Oct 9, 2021 at 8:33pm
Oct 9, 2021 at 8:49pm
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 Oct 9, 2021 at 9:06pm
Oct 9, 2021 at 10:04pm
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 Oct 9, 2021 at 10:05pm
Oct 9, 2021 at 10:26pm
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 Oct 9, 2021 at 11:02pm
Nov 13, 2021 at 1:43pm
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.