支持管道(有用的 Hello World)

发布于 2024-10-09 19:51:26 字数 3195 浏览 0 评论 0原文

我正在尝试编写一组简单的 C++ 程序,这些程序遵循基本的 Unix 哲学:

  1. 让每个程序做好一件事。
  2. 期望每个程序的输出都成为另一个未知程序的输入。

我在尝试将一个的输出作为另一个的输入,并将一个的输出作为其自身的单独实例的输入时遇到问题。简而言之,我有一个程序 add ,它接受参数并输出总和。我希望能够将输出通过管道传输到另一个 add 实例。

./add 1 2 | ./add 3 4

应该产生10,但目前产生7

我遇到了两个问题:

  1. cin 等待来自控制台的用户输入。我不想要这个,并且无法找到一个简单的示例来显示标准输入流的使用,而无需在控制台中查询用户。如果有人知道一个例子,请告诉我。
  2. 我不知道如何在支持管道的同时使用标准输入。目前看来,它不起作用。如果我发出命令 ./add 1 2 | ./add 3 4 结果是 7。

相关代码如下:

add.cpp snippet

// ... COMMAND LINE PROCESSING ...
    std::vector<double> numbers = multi.getValue(); // using TCLAP for command line parsing

    if (numbers.size() > 0)
    {
    double sum = numbers[0];

    double arg;
    for (int i=1; i < numbers.size(); i++)
    {
      arg = numbers[i];

      sum += arg;
    }

    std::cout << sum << std::endl;
  }
  else
  {
      double input;
      // right now this is test code while I try and get standard input streaming working as expected
      while (std::cin)
      {
            std::cin >> input;

            std::cout << input << std::endl;
      }

    }
// ... MORE IRRELEVANT CODE ...

所以,我想我的问题是有人看到什么吗为了支持管道标准输入,此代码不正确?是否有一些众所周知的(或隐藏的)资源可以清楚地解释如何实现支持 的示例应用程序基本的 Unix 哲学

@Chris Lutz 我已将代码更改为以下内容。 cin 仍然等待控制台上的用户输入,并且不仅仅从管道传递的标准输入中获取问题。我是否错过了处理这个问题的一些琐碎的事情?我还没有尝试过 Greg Hewgill 的答案,但不知道这会有什么帮助,因为问题仍然与 cin 有关。

// ... COMMAND LINE PROCESSING ...
    std::vector<double> numbers = multi.getValue(); // using TCLAP for command line parsing

    double sum = numbers[0];

    double arg;
    for (int i=1; i < numbers.size(); i++)
    {
      arg = numbers[i];

      sum += arg;
    }

    // right now this is test code while I try and get standard input streaming working as expected
    while (std::cin)
    {
          std::cin >> arg;

          std::cout << arg << std::endl;
    }

    std::cout << sum << std::endl;
// ... MORE IRRELEVANT CODE ...

编辑:几乎就在那里,这允许输出通过管道传输到输入,但看起来它是通过管道传输重复的。我正在发出 ./add 1 2 | ./add 3 4 并得到13。通过 while 循环中的一些额外的 cout 语句,当我只有一个 cout 语句。为什么我收到重复的?

// ... COMMAND LINE PROCESSING ...
    std::vector<double> numbers = multi.getValue(); // using TCLAP for command line parsing

    double sum = numbers[0];

    double arg;
    for (int i=1; i < numbers.size(); i++)
    {
      arg = numbers[i];

      sum += arg;
    }

    if (!isatty(fileno(stdin)))
    {
      while (std::cin)
      {
          std::cin >> arg; // this may require the use of std::strtod(), but to simplify the example I'm keeping it as this.

          sum += arg;
      }
    }

    std::cout << sum << std::endl;
// ... MORE IRRELEVANT CODE ...

I am trying to write a collection of simple C++ programs that follow the basic Unix philosophy by:

  1. Make each program do one thing well.
  2. Expect the output of every program to become the input to another, as yet unknown, program.

I'm having an issue trying to get the output of one to be the input of the other, and getting the output of one be the input of a separate instance of itself. Very briefly, I have a program add which takes arguments and spits out the summation. I want to be able to pipe the output to another add instance.

./add 1 2 | ./add 3 4

That should yield 10 but currently yields 7.

I've encountered two problems:

  1. The cin waits for user input from the console. I don't want this, and haven't been able to find a simple example showing a the use of standard input stream without querying the user in the console. If someone knows of an example please let me know.
  2. I can't figure out how to use standard input while supporting piping. Currently, it appears it does not work. If I issue the command ./add 1 2 | ./add 3 4 it results in 7.

The relevant code is below:

add.cpp snippet

// ... COMMAND LINE PROCESSING ...
    std::vector<double> numbers = multi.getValue(); // using TCLAP for command line parsing

    if (numbers.size() > 0)
    {
    double sum = numbers[0];

    double arg;
    for (int i=1; i < numbers.size(); i++)
    {
      arg = numbers[i];

      sum += arg;
    }

    std::cout << sum << std::endl;
  }
  else
  {
      double input;
      // right now this is test code while I try and get standard input streaming working as expected
      while (std::cin)
      {
            std::cin >> input;

            std::cout << input << std::endl;
      }

    }
// ... MORE IRRELEVANT CODE ...

So, I guess my question(s) is does anyone see what is incorrect with this code in order to support piping standard input? Are there some well known (or hidden) resources that explain clearly how to implement an example application supporting the basic Unix philosophy?

@Chris Lutz
I've changed the code to what's below. The problem where cin still waits for user input on the console, and doesn't just take from the standard input passed from the pipe. Am I missing something trivial for handling this? I haven't tried Greg Hewgill's answer yet, but don't see how that would help since the issue is still with cin.

// ... COMMAND LINE PROCESSING ...
    std::vector<double> numbers = multi.getValue(); // using TCLAP for command line parsing

    double sum = numbers[0];

    double arg;
    for (int i=1; i < numbers.size(); i++)
    {
      arg = numbers[i];

      sum += arg;
    }

    // right now this is test code while I try and get standard input streaming working as expected
    while (std::cin)
    {
          std::cin >> arg;

          std::cout << arg << std::endl;
    }

    std::cout << sum << std::endl;
// ... MORE IRRELEVANT CODE ...

EDIT: Almost there, this allows the output to be piped to the input, however it appears it's piping a duplicate. I'm issuing ./add 1 2 | ./add 3 4 and getting 13. With some extra cout statements in the while loop, I can see two values of 3 coming from cin when I only have one cout statement. Why am I getting a duplicate?

// ... COMMAND LINE PROCESSING ...
    std::vector<double> numbers = multi.getValue(); // using TCLAP for command line parsing

    double sum = numbers[0];

    double arg;
    for (int i=1; i < numbers.size(); i++)
    {
      arg = numbers[i];

      sum += arg;
    }

    if (!isatty(fileno(stdin)))
    {
      while (std::cin)
      {
          std::cin >> arg; // this may require the use of std::strtod(), but to simplify the example I'm keeping it as this.

          sum += arg;
      }
    }

    std::cout << sum << std::endl;
// ... MORE IRRELEVANT CODE ...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

深海夜未眠 2024-10-16 19:51:26

也许问题就在这里:

if (numbers.size() > 0)

如果您有任何参数,它会添加它们并忽略所有管道数据。所以当然 ./add 3 返回 3 - 它有一个参数,所以它忽略管道数据。

您应该修复代码以添加两个输入(如果给出了输入)和参数,而不是非此即彼。请记住:命令行参数并不排除管道输入。

Perhaps the problem is here:

if (numbers.size() > 0)

If you have any arguments, it adds them and ignores all piped data. So of course ./add 3 returns 3 - it has an argument, so it ignores the piped data.

You should fix your code to add both input (if input is given) and the arguments, not either-or. Remember: Command line arguments doesn't preclude piped input.

春庭雪 2024-10-16 19:51:26

您可能会发现有用的一个函数是 isatty(),它告诉您文件描述符是否连接到交互式会话。您可以这样使用它:

if (!isatty(fileno(stdin))) {
    while (std::cin) {
        // ...
    }
}

这只会尝试从终端读取输入,如果它不是交互式的(意味着标准输入是从文件或管道重定向的)。

One function you might find useful is isatty(), which tells you whether a file descriptor is connected to an interactive session or not. You might use it like this:

if (!isatty(fileno(stdin))) {
    while (std::cin) {
        // ...
    }
}

This will only try to read input from the terminal if it's not interactive (meaning stdin is redirected from a file or pipe).

人间☆小暴躁 2024-10-16 19:51:26

我想说最简单的是忽略从程序中的 stdin 读取。相反,只让程序从参数中读取,并像这样调用它: ./add 1 2 | xargs ./add 3 4

xargs 将使第一个 add 的输出成为第二个 add 的参数。

I would say the easiest is to ignore reading from stdin in your program. Instead, only let the program read from arguments, and call it like this: ./add 1 2 | xargs ./add 3 4

xargs will make the output from the first add an argument to the second add.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文