LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie
User Name
Password
Linux - Newbie This Linux forum is for members that are new to Linux.
Just starting out and have a question? If it is not in the man pages or the how-to's this is the place!

Notices


Reply
  Search this Thread
Old 12-26-2011, 06:45 AM   #1
qwerty4061
Member
 
Registered: Nov 2011
Posts: 31

Rep: Reputation: Disabled
stdin and pipes


Hi,
I was trying to learn about pipes and standard input/output streams in linux. So I wrote a simple program.

Source Code:
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
  char *str;
  str = (char*)malloc(80*sizeof(char));
   while (1)
  {
    printf("Enter string :");
    fgets(str, 80, stdin);
    printf("%s", str);
    sleep(1);
    fflush(NULL);
  }
  free(str);
  return 0;
}
I tried it out by using "uname | ./a.out".

My output was:


Enter string :Linux
Enter string :Linux
Enter string :Linux
Enter string :Linux
Enter string :Linux
Enter string :Linux
Enter string :Linux
Enter string :Linux
:
:

This goes on forever. What I was expecting was that the program would print Linux once and then wait for next input from stdin. This error probably might be occuring because the word Linux is still on the stdin of a.out and it is reading the same thing every second (just my guess). So I tried removing the infinite loop in the code, but in that case if I do "uname | ./a.out" it works. But if I do some command that prints mulitple lines of text like "ls | ./a.out" only the first file name is printed and a.out exits. Can anyone suggest any method to solve this problem.

Thanks
 
Old 12-26-2011, 07:15 AM   #2
Doc CPU
Senior Member
 
Registered: Jun 2011
Location: Stuttgart, Germany
Distribution: Mint, Debian, Gentoo, Win 2k/XP
Posts: 1,099

Rep: Reputation: 344Reputation: 344Reputation: 344Reputation: 344
Hi there,

Quote:
Originally Posted by qwerty4061 View Post
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
  char *str;
  str = (char*)malloc(80*sizeof(char));
   while (1)
  {
    printf("Enter string :");
    fgets(str, 80, stdin);
    printf("%s", str);
    sleep(1);
    fflush(NULL);
  }
  free(str);
  return 0;
}
I tried it out by using "uname | ./a.out".

My output was:

Enter string :Linux
Enter string :Linux
Enter string :Linux
:
:

This goes on forever.
I would expect that, because your program does not check whether stdin has any more input or not. So it reads one line of text and prints that again. In the second and subsequent loop iteration, however, stdin has reached the end of input (that is, feof(stdin) is true). In that case, fgets() does not modify the input buffer (see the reference manual for that case), and you believe you get the same string over and over again.

Quote:
Originally Posted by qwerty4061 View Post
What I was expecting was that the program would print Linux once and then wait for next input from stdin.
It does - if you call it without input redirection (piping). Then stdin is the console input, which doesn't have an end-of-file condition.

Quote:
Originally Posted by qwerty4061 View Post
Can anyone suggest any method to solve this problem.
Easy: Instead of making an infinite loop using while (1), check the end-of-file condition there:
Code:
while (!feof(stdin))
  ...
Then your program terminates properly when the input stream ends.

[X] Doc CPU

Last edited by Doc CPU; 12-26-2011 at 07:17 AM.
 
1 members found this post helpful.
Old 12-26-2011, 08:31 AM   #3
qwerty4061
Member
 
Registered: Nov 2011
Posts: 31

Original Poster
Rep: Reputation: Disabled
Thanks for the detailed answer. I have been trying to find the answer for hours . I didn't understand some part of it though.


Quote:
It does - if you call it without input redirection (piping). Then stdin is the console input, which doesn't have an end-of-file condition.
What other way is there to call it other than piping?


Quote:
Easy: Instead of making an infinite loop using while (1), check the end-of-file condition there:
Code:
while (!feof(stdin))
  ...
Then your program terminates properly when the input stream ends.
I replace the infinite loop with EOF check, but now instead of printing infinite times it is printing "Linux" twice. Why is that happening.


Also wasn't fflush(NULL) supposed to take care of stale values inside STDIN. STDOUT
 
Old 12-26-2011, 09:21 AM   #4
MTK358
LQ 5k Club
 
Registered: Sep 2009
Posts: 6,443
Blog Entries: 3

Rep: Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723
Quote:
Originally Posted by Doc CPU View Post
console input, which doesn't have an end-of-file condition.
It does: press Ctrl+D.
 
Old 12-26-2011, 09:25 AM   #5
MTK358
LQ 5k Club
 
Registered: Sep 2009
Posts: 6,443
Blog Entries: 3

Rep: Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723
Quote:
Originally Posted by qwerty4061 View Post
fgets(str, 80, stdin);
printf("%s", str);
"fgets" gets raw data, printf's "%s" expecta a NULL-terminated string. Try this:

Code:
#include <stdio.h>
int main()
{
  char str[81]; // the 81st element is for the NULL character at the end
  while (! feof(stdin))
  {
    printf("Enter string :");
    size_t bytes_read = fgets(str, 80, stdin);
    str[bytes_read] = '\0';
    printf("%s", str);
    sleep(1);
    fflush(NULL);
  }
  return 0;
}
 
1 members found this post helpful.
Old 12-26-2011, 09:27 AM   #6
Doc CPU
Senior Member
 
Registered: Jun 2011
Location: Stuttgart, Germany
Distribution: Mint, Debian, Gentoo, Win 2k/XP
Posts: 1,099

Rep: Reputation: 344Reputation: 344Reputation: 344Reputation: 344
Hi there,

Quote:
Originally Posted by qwerty4061 View Post
What other way is there to call it other than piping?
call your program from the console, just like that - and supply the input via keyboard as usual. :-)

Quote:
Originally Posted by qwerty4061 View Post
I replace the infinite loop with EOF check, but now instead of printing infinite times it is printing "Linux" twice. Why is that happening.
Because I didn't finish thinking. Damn.
Well, look: fgets() reads until there is either an end-of-file condition, or a line feed. In your situation, the output of uname that you pipe into your program ends with a line feed. So fgets() reads everything, including the line feed, and then returns that data. At that moment, feof(stdin) doesn't catch yet, because you have read up to the end of the stream, but not beyond. End-of-file is recognized only when you try to read past the end. And that happens in lap 2 of your loop.

That's not nice, because it forces the programmer to produce "spaghetti code".
You can revert to your "while (1)" as the heading of your loop, but now you must supply something that ends the loop as soon as you know it should be ended - and that's after the fgets() call. Insert another line after the fgets() call:
Code:
if (feof(stdin))
   break;
That will break out of the loop and continue with the instructions after it when feof(stdin) catches.

Quote:
Originally Posted by qwerty4061 View Post
Also wasn't fflush(NULL) supposed to take care of stale values inside STDIN. STDOUT
Yes. But it neither changes the eof() status of the file, nor touches your program's receive buffer. It only discards the rest of available input data you may not have read yet. But honestly, I stopped short on using NULL as an argument, as I would have expected fflush(stdin) here. Using NULL will flush all currently open files (didn't know that yet, thanks), which may not be what you want in a larger project.

[X] Doc CPU
 
1 members found this post helpful.
Old 12-26-2011, 09:32 AM   #7
Doc CPU
Senior Member
 
Registered: Jun 2011
Location: Stuttgart, Germany
Distribution: Mint, Debian, Gentoo, Win 2k/XP
Posts: 1,099

Rep: Reputation: 344Reputation: 344Reputation: 344Reputation: 344
Hi there,

Quote:
Originally Posted by MTK358 View Post
Quote:
Originally Posted by Doc CPU View Post
Then stdin is the console input, which doesn't have an end-of-file condition.
It does: press Ctrl+D.
if you force it like that, it does. But not automatically when the input stream is exhausted.

Quote:
Originally Posted by MTK358 View Post
"fgets" gets raw data, printf's "%s" expecta a NULL-terminated string.
Check it in the reference: fgets adds a terminating 0-byte.

[X] Doc CPU
 
Old 12-26-2011, 08:48 PM   #8
qwerty4061
Member
 
Registered: Nov 2011
Posts: 31

Original Poster
Rep: Reputation: Disabled
Thanks everyone especially Doc CPU for your answers.

Quote:
"fgets" gets raw data, printf's "%s" expecta a NULL-terminated string. Try this:
I think you are wrong here. "man fgets" tells a different story.

Quote:
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A '\0' is stored after the last character in the buffer.
Quote:
Using NULL will flush all currently open files (didn't know that yet, thanks), which may not be what you want in a larger project.
I was initially using fflush(stdin). I changed it to NULL while tying to figure out why the program is not working.
 
Old 12-26-2011, 08:55 PM   #9
qwerty4061
Member
 
Registered: Nov 2011
Posts: 31

Original Poster
Rep: Reputation: Disabled
Oops I didn't see Doc CPU's last reply
 
Old 01-04-2012, 01:26 PM   #10
qwerty4061
Member
 
Registered: Nov 2011
Posts: 31

Original Poster
Rep: Reputation: Disabled
Better Code:


Code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main()
{
  char* str;
  str = (char*) (malloc(80 * sizeof(char)));
  
  while(fgets(str, 80, stdin) != NULL)
  {
    printf("%s", str);
    sleep(1);
  }

  free(str);
  return 0;
}
 
Old 01-04-2012, 03:55 PM   #11
Doc CPU
Senior Member
 
Registered: Jun 2011
Location: Stuttgart, Germany
Distribution: Mint, Debian, Gentoo, Win 2k/XP
Posts: 1,099

Rep: Reputation: 344Reputation: 344Reputation: 344Reputation: 344
Hi there,

Quote:
Originally Posted by qwerty4061 View Post
Better Code:
Code:
...
  str = (char*) (malloc(80 * sizeof(char)));
why so complicated? The return type of malloc() is a void * which is by definition compatible to any other pointer type. So the explicite type cast isn't necessary and just makes it worse to read.

[X] Doc CPU
 
Old 01-04-2012, 08:21 PM   #12
chrism01
LQ Guru
 
Registered: Aug 2004
Location: Sydney
Distribution: Rocky 9.2
Posts: 18,358

Rep: Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751
The rtn type is 'void *' so that you CAN cast it to the type you require, which you should do for robust code. It also makes it clearer for the next guy
 
Old 01-05-2012, 03:48 AM   #13
Doc CPU
Senior Member
 
Registered: Jun 2011
Location: Stuttgart, Germany
Distribution: Mint, Debian, Gentoo, Win 2k/XP
Posts: 1,099

Rep: Reputation: 344Reputation: 344Reputation: 344Reputation: 344
Hi there,

Quote:
Originally Posted by chrism01 View Post
The rtn type is 'void *' so that you CAN cast it to the type you require
for a type cast to be possible, it wouldn't have to be void *, it could as well be int - though a pointer type is more obvious, of course. But an explicit type cast is always possible, even between totally incompatible types.

Quote:
Originally Posted by chrism01 View Post
which you should do for robust code. It also makes it clearer for the next guy
For me, a type cast on the result of malloc() looks more like an attempt of obfuscation. I avoid it for the sake of clarity.

[X] Doc CPU
 
Old 01-05-2012, 07:27 AM   #14
MTK358
LQ 5k Club
 
Registered: Sep 2009
Posts: 6,443
Blog Entries: 3

Rep: Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723Reputation: 723
Why use malloc() for a fixed-size string that's only used for the lifetime of a function? use an array:

Code:
char str[80];
It will be allocated on the stack and popped off when the functions returns, no need for free().
 
1 members found this post helpful.
Old 01-05-2012, 01:13 PM   #15
qwerty4061
Member
 
Registered: Nov 2011
Posts: 31

Original Poster
Rep: Reputation: Disabled
I guess I should have been clearer when I wrote "Better Code". I wrote that in response to DocCPU's earlier comment to feof forcing developers to write spaghetti code.

Quote:
Why use malloc() for a fixed-size string that's only used for the lifetime of a function? use an array:
Yes, you are right. There is no point in using malloc in this code. In my defence, this code was doing something else entirely different initially, I was modifying it in my effort to learn about standard streams.


Quote:
For me, a type cast on the result of malloc() looks more like an attempt of obfuscation. I avoid it for the sake of clarity.
I disagree with you on this. I think it improves readability, I don't have enough programming experience/knowledge to argue about the relative merits about using an explicit cast. If you have any reason as to why not to use an explicit cast in malloc, other than a personal preference I am definitely interested in it.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Pipes jayant17 Linux - Newbie 3 02-27-2010 03:04 PM
how to use pipes? rabbit2345 Linux - Software 2 03-29-2008 01:52 PM
[C] stdout and stdin replace by pipes and execve the child chudzielec Programming 6 01-27-2008 05:52 AM
about pipes kpachopoulos Programming 1 10-15-2005 12:37 PM
redirecting stdin and stdout with pipes: convincing apps they're for a terminal? prell Programming 1 09-02-2004 06:38 AM

LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie

All times are GMT -5. The time now is 11:47 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration