Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game. |
Notices |
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
Are you new to LinuxQuestions.org? Visit the following links:
Site Howto |
Site FAQ |
Sitemap |
Register Now
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
|
 |
|
12-24-2005, 02:18 AM
|
#1
|
Senior Member
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078
Rep: 
|
Can't get user input when using a pipe to my C++ program
I have a program which has a mode that takes data from standard input, processes it, and sends it to standard output (like many Unix command line tools.) The problem is that when I use a pipe to supply the input, I completely lose the ability to prompt the user for input. Is this normal? Here is a very simple example of what I am doing:
Code:
#include <iostream>
int main()
{
char StandardInput[1024], UserInput[1024];
while (std::cin)
{
std::cin.read(StandardInput, 1024);
//process the data
}
std::cin.clear();
std::cerr << "What is your password?\n";
std::cin.getline(UserInput, 1024);
}
Code:
bash> cat myfile | ./myprogram
I've tried almost everything to "reset" std::cin after taking the pipe input (I've read through pretty much every header that istream is associated with) but I cannot come up with anything. What makes it worse is I'm positive I had this working sometime earlier but can't get it to work anymore. This seems like a shell issue, however I would like the solution to be standard C or C++. Any help is greatly appreciated.
ta0kira
|
|
|
12-24-2005, 12:21 PM
|
#2
|
Member
Registered: Jan 2005
Location: Chandler, AZ USA
Distribution: Mandrake/Mandriva 10.2
Posts: 186
Rep:
|
iostream is wicked picky, your logic looks a little off, try:
Code:
// Do clearing if the object has an invalid state
while(!std::cin)
{
std::cin.clear();
std::cin.ignore(25000, '\n');
}
This seems to work for me, but it may require you to push enter to continue. There are probably better ways to do this, but note: you probably want to do the fixing only when the stream has an issue. Anyhow, you cannot pipe anything to your program if you are not allowing for any parameters. You must have argv, as such:
Code:
int main (int argc, char *argv[])
{
// Do junk with arguments
return 0;
}
Anyhow, without main having parameters, you cannot'pipe' anything to it (generally). And I do not think that cin is getting the parameters (unless its functionality extends to uses I am not familiar with)
|
|
|
12-24-2005, 12:24 PM
|
#3
|
Member
Registered: Jan 2005
Location: Chandler, AZ USA
Distribution: Mandrake/Mandriva 10.2
Posts: 186
Rep:
|
Oh yeah, argc is your count of arguments, and argv is a 2d array (or you could call it an array of pointers) to all the arguments.
|
|
|
12-24-2005, 02:42 PM
|
#4
|
Senior Member
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078
Original Poster
Rep: 
|
Thanks. I've got the argc and argv under control, and I actually use them. The problem is when I replace the standard input on the command line using the shell to redirect output from another program to be used as the input for this program.
The reason std::cin errors out of the loop is because of EOF, whether I used piped input or user input. The difference is that when user input reaches EOF, the next read call prompts the user for more input. When the pipe hits EOF, however, I'm pretty sure the next read call should also prompt the user for input, which it doesn't.
What I think is happening is that when I pipe input, the shell (or kernel) makes the streambuf for std::cin point (indirectly) to the pipe file. When no piped input is provided, the streambuf defaults to console input, which is not available to the program if piped input is used.
I looked on comp.lang.c++.moderated and several people seem to think there is a non-portable solution. In the case of POSIX, it is a tty thing. I need to remain portable, however. I guess I'll leave off that part of the functionality for now until I find either a portable solution or both POSIX and DOS solutions. Thanks.
ta0kira
|
|
|
12-24-2005, 05:04 PM
|
#5
|
Member
Registered: Jan 2005
Location: Chandler, AZ USA
Distribution: Mandrake/Mandriva 10.2
Posts: 186
Rep:
|
But I am still unclear, are you trying to get piped input via cin? Why not use argv instead? Did you output each successive read to see what you are using from the buffer?
I/O usually keeps a buffer ready register that applications poll (after an interrupt is given) to tell the application that there are bytes on the buffer that are ready to be read. On some streams, like in the case of sockets, there is not a definitive EOF. It is just a I/O register that is being mapped to a memory location (or hardware address).
Last edited by elyk1212; 12-24-2005 at 05:14 PM.
|
|
|
12-25-2005, 12:38 AM
|
#6
|
Senior Member
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078
Original Poster
Rep: 
|
To understand this better, compile the example in my first post. After you do that, execute it with the command "a.out" (or whatever it comes out as.) You should be prompted for input until ctrl-D is pressed, then you should be prompted for a password until enter is pressed. Then type "ls | a.out". This will probably just show the password prompt and skip the user input no matter what code you put in between the read and getline calls. I would like it to wait for user input, however. I am using std::cin for data input from the first input operation (read), and text from the user from the second input (getline.) I am using argv to take options and file names from the command line, but I also need the ability to take things from standard input. Much like this:
Code:
> grep "find me" myfile
-vs-
> ls -1 | grep "myfile"
In the first case, grep takes the data from the file used as an argument. In the second case, it takes the data piped from ls. When taking data from a pipe in this manner, I also would like to be able to take user input in the same call to my program, which I cannot seem to do.
If you are curious, here is what I've been working on:
http://sourceforge.net/projects/spiral-ta0kira/
ta0kira
|
|
|
12-25-2005, 05:56 PM
|
#8
|
Senior Member
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078
Original Poster
Rep: 
|
Thanks, however I know already which circumstances it is a tty and which it isn't. The problem is that if the program starts with the buffer pointing to a pipe, it cannot then point to a tty for the purposes of user input.
ta0kira
|
|
|
12-26-2005, 01:38 PM
|
#9
|
Senior Member
Registered: Apr 2003
Location: Lancaster, England
Distribution: Debian Etch, OS X 10.4
Posts: 1,263
Rep:
|
I'm not really an expert on this, but I think after the shell forks a new process that takes piped input, it calls dup2() setting the stdin file descriptor(0) to the reading side of the pipe. If you want to put file descriptor 0 back to stdin of the controlling terminal you'll have to do it yourself.
I don't know enough about istream to see if this would work, but I would suggest that after you have finished reading from the file you do something like dup2(0, open("/dev/tty", O_WRONLY)); Obviously this is platform dependent, so doesn't really solve your problem, but I think it's the best you're gonna get.
|
|
|
12-26-2005, 03:39 PM
|
#10
|
Senior Member
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078
Original Poster
Rep: 
|
I figured as much. I'm really surprised at how vague the C++ standard is when it comes to streams and buffers. Some functions aren't even required to work in any given implementation of the STL. For some reason I thought that if a pipe ran out of data it would attempt to get user input instead of unconditionally reporting EOF. Thanks for your help.
ta0kira
|
|
|
04-05-2006, 03:35 AM
|
#11
|
Member
Registered: Jan 2005
Location: Chandler, AZ USA
Distribution: Mandrake/Mandriva 10.2
Posts: 186
Rep:
|
I know this thread is old, but I had come across this same issue and here is a fix listed in 'info libc' ,I also checked and it works great.
Code:
/* example */
char* id = ctermid(NULL);
FILE* term = fopen(id, "r");
char* buffer = (char*)malloc(800);
fgets(buffer, 80, term);
Invoked as
Code:
$./binary_with_output | ./example
ctermid(char*) returns the current terminal that the program was invoked from, provided it exists. Then, just open a descriptor to it as usual and read from it. It will block on a read as expected without having to redirect stdin to the keyboard I/O.
|
|
|
04-14-2006, 11:18 AM
|
#12
|
Senior Member
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078
Original Poster
Rep: 
|
Wow, is that really a part of libc, or just the Linux implementation of it? I'll take a look. Thanks.
ta0kira
|
|
|
04-14-2006, 11:40 AM
|
#13
|
Member
Registered: Jan 2005
Location: Chandler, AZ USA
Distribution: Mandrake/Mandriva 10.2
Posts: 186
Rep:
|
Yep, it is in libc, or rather glibc. It is likely a linux part of libc since it refers to calling terminals etc.. but I don't really know for sure.
The idea is you treat the current terminal as a FILE descriptor and open it as usual.
Details are in "info libc"
Under the menu as:
-> Job Control->Functions for Job Control -> Identifying the Controlling Terminal
Also, Gnu.org under online documentation/manuals has glibc.
Last edited by elyk1212; 04-14-2006 at 12:02 PM.
|
|
|
04-18-2006, 09:44 AM
|
#14
|
Senior Member
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078
Original Poster
Rep: 
|
Do you happen to know why 'isatty(NULL)' doesn't return true when stdin isn't a terminal? Thanks.
ta0kira
|
|
|
04-18-2006, 03:58 PM
|
#15
|
Member
Registered: Jan 2005
Location: Chandler, AZ USA
Distribution: Mandrake/Mandriva 10.2
Posts: 186
Rep:
|
Quote:
-- Function: int isatty (int FILEDES)
This function returns `1' if FILEDES is a file descriptor
associated with an open terminal device, and 0 otherwise.
|
My interpretation of this is that the function will return false when the file descriptor passed (or implicit stdin, I guess) is NOT associated with a valid terminal device.
Your question stated:
"...why 'isatty(NULL)' doesn't return true when stdin isn't a terminal?"
Meaning that isatty does not return true when passed a non terminal. This is desired functionality. Did you mean that it returned 1 (undesired functionality)? If it did, perhaps an explicit passing of the stdin may be required.
Please let me know. Thanks! Kyle
Last edited by elyk1212; 04-18-2006 at 04:01 PM.
|
|
|
All times are GMT -5. The time now is 01:16 PM.
|
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.
|
Latest Threads
LQ News
|
|