Language: C, OS: Linux
Code:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
fork();
printf("hello world\n");
fork();
printf("bye\n");
return 0;
}
Output:
hello world
bye
hello world
bye
hello world
bye
hello world
bye
According to this and this, printf() buffers output until a newline is encountered.
So why does we have 4 "hello world" in this case? (instead of 2 "hello world")
Edit: Sorry all, but like #GregHewgill said, I running this program from an environment where the output cannot be directly to the terminal, when I check it again on my computer, it just run as expected.
According to this and this, printf() buffers output until a newline is encountered.
Printing a newline usually flushes only if the output goes to a terminal device.
For example:
$ ./a.out >out_file
will not flush the buffer even with the newline character. So, your expectation is flawed.
The only right way to get "desired" output (2 hello world and 4 bye) is to either disable buffering totally using setbuf:
setbuf(stdout, 0);
or use fflush:
fflush(stdout);
after each printf call to flush explicitly.
Related
Suppose I have a ncurses program which does some job on curses screen, and finally print something to stdout. Call this program c.c, compiled to a.out.
I expect cat $(./a.out) first fire up ncurses, after some action, a.out quits and print c.c to stdout, which is read by cat, and thus print content of file c.c.
#include <stdio.h>
#include <ncurses.h>
int main() {
initscr();
noecho();
cbreak();
printw("hello world");
refresh();
getch();
endwin();
fprintf(stdout, "c.c");
return 0;
}
I also expect ./a.out | xargs vim, ls | ./a.out | xargs less to work.
But when I type ./a.out | xargs vim, hello world never shows up. The command seems not executed in order, vim does not open c.c.
What is the correct way to make a ncurses program to work with other linux utils?
Pipes use the standard output (stdout) and standard input (stdin).
The simplest way - rather than using initscr, which initializes the output to use the standard output, use newterm, which allows you to choose the file descriptors, e.g.,
newterm(NULL, stderr, stdin);
rather than
initscr();
which is (almost) the same as
newterm(NULL, stdout, stdin);
By the way, when you include <ncurses.h> (or <curses.h>), there is no need to include <stdio.h>.
If you wanted to use your program in the middle of a pipe, that is more complicated: you would have to drain the standard input and open the actual terminal device. But that's another question (and has already been answered).
Further reading:
initscr, newterm, endwin, isendwin, set_term, delscreen -
curses screen initialization and manipulation routines
ncurses works by writing a bunch of ansi escapes to stdout, which the terminal will interpret. You can run ./a.out > file and then inspect the file to see what you're actually writing. It'll be immediately obvious why programs are confused:
$ cat -vE file
^[(B^[)0^[[?1049h^[[1;24r^[[m^O^[[4l^[[H^[[Jhello world^[[24;1H^[[?1049l^M^[[?1l^[>c.c
The correct way of doing this is to skip all the graphical/textual UI parts when you detect that stdout is not a terminal, i.e. it's consumed by a program instead of a user:
#include <unistd.h>
#include <stdio.h>
#include <ncurses.h>
int main() {
if(isatty(1)) {
// Output is a terminal. Show stuff to the user.
initscr();
noecho();
cbreak();
printw("hello world");
refresh();
getch();
endwin();
} else {
// Output is consumed by a program.
// Skip UI.
}
fprintf(stdout, "c.c");
return 0;
}
This is the canonical Unix behavior.
If you instead want to force your UI to be shown regardless, you can draw your UI on stderr.
The following is a simple C program:
#include <unistd.h>
#include <stdio.h>
int main(void)
{
while (1)
{
printf("Hello World\n");
sleep(1);
}
}
Build and run it, the "Hello World" will be printed in the terminal:
$ ./a.out
Hello World
Hello World
Hello World
But if the stdout is redirected to a file, after running a while, there is still nothing in the file:
$ ./a.out > log.txt
^C
$ cat log.txt
$
Why doesn't the printf output to the file which stdout is redirected to?
For terminal only by default it is line buffer. In here you redirected the stdout to the file. So, now the stdout is not pointing a terminal. It pointing a file. For the file it is by default fully buffered. So, you have flush the stdout after writing it.
Refer the answer for this question.
As #js1, said, you have to call fflush(stdout) after writing it.
This question already has answers here:
printf anomaly after "fork()"
(3 answers)
Closed 7 years ago.
I was writing a multi-process program using fork() and i bumped into a problem.
Below is a sample code reproducing the problem (without any sort of error checking):
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
printf("hello world");
fork();
}
This code prints 2 "hello world" statements (one from parent and the other from child). However this should not be the case since the printffunction call is prior to the fork() system call.
After testing, the problem appears to be solved by the following:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
printf("hello world\n"); \\ addition of the new line character
// or by using fflush(stdout);
fork();
}
My guessing is that the printf buffer is being copied while its not flushed, so the child process is emptying its copy of this buffer before exiting. Hence the other printf shows.
Can anyone provide a better explanation of this issue? Or even better, correct me if i am wrong or missing something.
The file handle stdout (which is used by printf) is by default line buffered, which means output using printf will be flushed (and shown in the console) either when there's a newline or when the buffer is full.
As fork creates an exact duplicate of the parent process, both processes have the same contents in the (un-flushed) output buffer, and both will be flushed when the two processes exits.
So yes you're correct in your guessing.
(running on a mac) My C.sublime-build file looks like this:
{
"cmd" : ["gcc -Wall -g $file_name -o ${file_base_name} && ./${file_base_name}"],
"selector" : "source.c",
"shell": true,
"working_dir" : "$file_path"
}
and I have a simple program with the following code:
#include <stdio.h>
#include <unistd.h>
int main ( int argc, char *argv[] ) {
printf("hi\n");
fork();
printf("bye\n");
return 0;
}
and sublime will execute it and give me
hi
bye
hi
bye
while executing from the shell gives me the correct result,
hi
bye
bye
why is this happening?
According to ISO C:
Standard input and standard output are fully buffered, unless they
refer to a terminal device, in which case, they are line buffered.
When you're using ST3, it does not refer to a terminal device so it is fully buffered. It means hi\n and bye\n will be stored in buffer zone and fork()will copy them to child process. Then both of them will be output twice.
When you're using the shell, you're using a terminal device and it is line buffered. During thr execution, hi\n will be output firstly and buffer zone is flushed due to the \n. Then bye\n is send to buffer zone and will be output twice.
It may be that when sublime executes it that stdout, for whatever reason, is not using line buffered output but fully buffered output instead. So, when you fork() the child, the "hi\n" still resides on the child's FILE too. The output of both is only flushed when the programs exit and they both print the same output.
I'm trying to force my C program to line buffer (stdout is going to be captured by a java program), but it always seems to fully buffer instead. Here is some sample code:
#include <stdio.h>
#include <stdlib.h>
int main(){
char c;
setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
printf("Hello world\n");
c = getchar();
printf("got char: %c\n", c);
}
If I specify _IOLBF or _IOFBF, then I don't see an output until I input a char. Only if I use _IONBF will I see output before the getchar(). Shouldn't _IOLBF do the same since "Hello World\n" contains a '\n'?
I am using visual c++ 2005.
Thanks
According to this Microsoft documentation:
_IOLBF:
For some systems, this provides line buffering. However, for Win32, the behavior is the same as _IOFBF - Full Buffering.