在 bash 中重定向 C 程序输出的问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/499879/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 20:38:46  来源:igfitidea点击:

Problem redirecting a C program output in bash

clinuxbashredirectprintf

提问by mmutilva

I've coded a program in C that sends messages to the stdout using printf and I'm having trouble redirecting the output to a file (running from bash).

我已经用 C 编写了一个程序,该程序使用 printf 将消息发送到标准输出,但我无法将输出重定向到文件(从 bash 运行)。

I've tried:

我试过了:

./program argument >> program.out
./program argument > program.out
./program >> program.out argument
./program > program.out argument

In each case, the file program.out is created but it remains empty. After the execution ends the file size is 0.

在每种情况下,都会创建文件 program.out,但它仍然是空的。执行结束后文件大小为0。

If I omit the redirection when executing the program:

如果我在执行程序时省略重定向:

./program argument

Then, all messages sent to stdout using printf are shown in the terminal.

然后,所有使用 printf 发送到 stdout 的消息都显示在终端中。

I have other C programs for which I've no problem redirecting the output this way. Does it have to do with the program itself? with the argument passing? Where should look for the problem?

我有其他 C 程序,我可以通过这种方式重定向输出。和程序本身有关系吗?随着争论的通过?应该去哪里找问题?

Some details about the C program:

关于 C 程序的一些细节:

  • It does not read anything from stdin
  • It uses BSD Internet Domain sockets
  • It uses POSIX threads
  • It assigns a special handler function for SIGINT signal using sigaction
  • It sends lots of newlines to stdout (for those of you thinking I should flush)
  • 它不从标准输入读取任何内容
  • 它使用 BSD Internet 域套接字
  • 它使用 POSIX 线程
  • 它使用 sigaction 为 SIGINT 信号分配一个特殊的处理函数
  • 它向标准输出发送了很多换行符(对于那些认为我应该刷新的人)

Some code:

一些代码:

int main(int argc, char** argv)
{
    printf("Execution started\n");
    do
    {        
        /* lots of printf here */
    } while (1);
    /* Code never reached */
    pthread_exit(EXIT_SUCCESS);
}

回答by gclj5

Flushing after newlines only works when printing to a terminal, but not necessarily when printing to a file. A quick Google search revealed this page with further information: http://www.pixelbeat.org/programming/stdio_buffering/

换行后刷新仅在打印到终端时有效,但在打印到文件时不一定有效。一个快速的谷歌搜索显示这个页面有更多的信息:http: //www.pixelbeat.org/programming/stdio_buffering/

See the section titled "Default Buffering modes".

请参阅标题为“默认缓冲模式”的部分。

You might have to add some calls to fflush(stdout), after all.

毕竟,您可能需要添加一些对 fflush(stdout) 的调用。

You could also set the buffer size and behavior using setvbuf.

您还可以使用setvbuf设置缓冲区大小和行为。

回答by user10892

Flushing the buffers is normally handled by the exit()function, which is usually called implicitly by a returnfrom main(). You are ending your program by raising SIGINT, and apparently the default SIGINT handler does not flush the buffers.

刷新缓冲区通常由exit()函数处理,该函数通常return由 main() 中的a 隐式调用。您通过提高 SIGINT 来结束程序,显然默认的 SIGINT 处理程序不会刷新缓冲区。

Take a look at this article: Applying Design Patterns to Simplify Signal Handling. The article is mostly C++, but there is a useful C example in the 2nd section, which shows how to use SIGINT to exit your program gracefully.

看看这篇文章: 应用设计模式来简化信号处理。文章主要是C++,但在第2节中有一个有用的C示例,它展示了如何使用SIGINT优雅地退出您的程序。

As for why the behavior of a terminal differs from a file, take a look at Stevens' Advanced Programing in the UNIX EnvironmentSection 5.4 on Buffering. He says that:

至于为什么终端的行为与文件不同,请查看 Stevens在 UNIX 环境中高级编程第 5.4 节关于缓冲。他说:

Most implementations default to the following types of buffering. Standard error is always unbuffered. All other streams are line buffered if they refer to a terminal device; otherwise, they are fully buffered. The four platforms discussed in this book follow these conventions for standard I/O buffering: standard error is unbuffered, streams open to terminal devices are line buffered, and all other streams are fully buffered.
大多数实现默认为以下类型的缓冲。标准错误总是无缓冲的。如果所有其他流指向终端设备,则它们都是行缓冲的;否则,它们将被完全缓冲。本书中讨论的四种平台遵循以下标准 I/O 缓冲约定:标准错误是无缓冲的,对终端设备开放的流是行缓冲的,所有其他流都是完全缓冲的。

回答by womble

Has the program terminated by the time you check the contents of the redirected file? If it's still running, your output might still be buffered somewhere up the chain, so you don't see it in the file.

当您检查重定向文件的内容时,程序是否已终止?如果它仍在运行,您的输出可能仍被缓冲在链上的某个地方,因此您在文件中看不到它。

Apart from that, and the other answers provided so far, I think it's time to show a representative example of the problem code. There's too many esoteric possibilities.

除此之外,以及到目前为止提供的其他答案,我认为是时候展示问题代码的代表性示例了。有太多深奥的可能性。

EDIT

编辑

From the look of the sample code, if you've got a relatively small amount of printing happening, then you're getting caught in the output buffer. Flush after each write to be sure that it's gone to disk. Typically you can have up to a page size's worth of unwritten data lying around otherwise.

从示例代码的外观来看,如果您的打印量相对较少,那么您就会陷入输出缓冲区。每次写入后刷新以确保它已写入磁盘。通常情况下,您最多可以有一个页面大小的未写入数据存在其他地方。

In the absence of a flush, the only time you can be sure you've got everything on disk is when the programexits. Even a thread terminating won't do it, since output buffers like that aren't per-thread, they're per-process.

在没有刷新的情况下,您唯一可以确定磁盘上的所有内容是程序退出时。即使线程终止也不会这样做,因为这样的输出缓冲区不是每个线程的,而是每个进程的。

回答by HUAGHAGUAH

Suggestions:

建议:

  1. Redirect stderr to a file as well.
  2. Try tail -f your output file(s).
  3. Open a file and fprintf your logging (to help figure out what's going on).
  4. Search for any manual closes/duplication/piping of std* FILE handles or 1-3 file descriptors.
  5. Reduce complexity; cut out big chunks of functionality until printfs work. Then readd them until it breaks again. Continue until you identify the culprit code.
  1. 也将 stderr 重定向到文件。
  2. 尝试 tail -f 您的输出文件。
  3. 打开一个文件并 fprintf 您的日志记录(以帮助弄清楚发生了什么)。
  4. 搜索 std* FILE 句柄或 1-3 个文件描述符的任何手动关闭/复制/管道。
  5. 降低复杂性;切掉大量的功能,直到 printfs 工作。然后阅读它们,直到它再次破裂。继续,直到您确定罪魁祸首代码。

回答by ssn

Just for the record, in Perl you would use:

只是为了记录,在 Perl 中,您将使用:

use IO::Handle;

flush STDOUT;
autoflush STDOUT;