繁体   English   中英

为什么我的程序不能正确接受另一个程序的管道输出?

[英]Why won't my program accept the piped output of another program properly?

我有一个用3个.c文件编译的C程序。 本质上,该程序根据我在主菜单中定义的x和y大小输入,将正方形输出到标准输出。 相关代码如下:

void    rush(int x, int y);

int     main(void)
{
    rush(3, 3);
    return (0);
}

运行main的可执行文件,如下所示:

./a.out

给出以下内容:

o-o
| |
o-o

并将传递给rush函数的参数更改为(5,5)将产生以下结果:

o---o
|   |
|   |
|   |
o---o

你明白了。 每行由\\ n分隔,这允许函数打印正确的下一行。 我还有另一个测试程序,它是一个简单的编译主程序,可以像我想要的那样简单地打印ARGC的值,以测试这种输入将产生什么管道的行为。 第二个主程序是这样的:

#include <stdio.h>

int     main(int argc, char **argv)
{
    printf("argc value is: %d\n", argc);
    return (0);
}

运行以下命令:

./a.out | ./test

我得到以下输出:

argc value is: 1

最初对我来说这没有意义,但后来我想起来是因为某些命令需要xargs才能正确接受来自stdin的输入。 在主输入中使用xargs(5,5)作为输入:

./a.out | xargs ./test

导致:

argc value is: 9

因此,我有两个问题。 有没有一种方法不需要xargs,并且可以在c文件本身中完成? 知道测试文件的输入后,为什么argc == 9? 程序如何分离出该格式的字符串并确定要放入数组的内容?

这会很长,所以请抓住您最喜欢的饮料。 休息后不要只跳到答案。

首先,检查提供给程序的命令行参数args.c

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    int  i;
    printf("argc = %d\n", argc);
    for (i = 0; i < argc; i++)
        printf("argv[%d] = \"%s\"\n", i, argv[i]);
    return EXIT_SUCCESS;
}

使用您喜欢的C编译器进行编译; 我使用gcc:

gcc -Wall -O2 args.c -o args

如果你说

./args one two

它会输出

argc = 3
argv[0] = "./args"
argv[1] = "one"
argv[2] = "two"

所有Unix都具有命令行实用程序或shell内置的printf ,其工作方式与C printf()标准库函数非常相似。 我们可以举个例子

printf 'Hello, world!\nSecond line\nThird line\n'

我们会看到

Hello, world!
Second line
Third line

现在,如果我们用管道将两者连接起来,

printf 'Hello, world!\nSecond line\nThird line\n' | ./args

我们得到

argc = 1
argv[0] = "./args"

因为./args没有参数,并且上面的args.c完全忽略了标准输入。

xargs实用程序命令读取输入,然后将其自身的命令行参数作为命令执行,并将其读取的输入添加为附加参数。 它也是高度可配置的。 如果你跑

printf 'Hello, world!\nSecond line\nThird line\n' | xargs ./args

你会得到

argc = 7
argv[0] = "./args"
argv[1] = "Hello,"
argv[2] = "world!"
argv[3] = "Second"
argv[4] = "line"
argv[5] = "Third"
argv[6] = "line"

因为xargs将输入中的每个标记(由空格分隔)转换为命令行参数。 如果我们告诉xargs使用-d SEPARATOR选项,并以换行符作为分隔符,将每条输入行变成一个单独的参数:

printf 'Hello, world!\nSecond line\nThird line\n' | xargs -d '\n' ./args

我们得到

argc = 4
argv[0] = "./args"
argv[1] = "Hello, world!"
argv[2] = "Second line"
argv[3] = "Third line"

如果我们告诉xargs每个执行的命令最多添加两个参数,方法是添加-n 2选项,

printf 'Hello, world!\nSecond line\nThird line\n' | xargs -d '\n' -n 2 ./args

我们会得到

argc = 3
argv[0] = "./args"
argv[1] = "Hello, world!"
argv[2] = "Second line"
argc = 2
argv[0] = "./args"
argv[1] = "Third line"

此输出意味着我们的./args实际上执行了两次。 首先是有效的./args 'Hello, world!' 'Second line' ./args 'Hello, world!' 'Second line' ,第二个是./args 'Third line'

xargs的另一个重要选项是-r ,它告诉它在没有任何其他参数的情况下不要运行命令:

true | xargs -r ./args

不会输出任何内容,因为xargs看不到任何输入,并且-r选项告诉它如果没有其他参数,则不要运行我们的args程序。

在处理文件名或路径时, -0 (零号)选项告诉xargs输入分隔符是nul字符\\0 ,它在C中分隔字符串。 如果我们在xargs的输入中使用该参数,则即使带有换行符的字符串等也将正确地拆分为参数。 例如:

printf 'One thing\non two lines\0Second thing' | xargs -0 ./args

将输出

argc = 3
argv[0] = "./args"
argv[1] = "One thing
on two lines"
argv[2] = "Second thing"

如果以一种可靠的方式处理文件名或路径,这正是人们想要的。


有没有一种方法不需要xargs,并且可以在c文件本身中完成?

当然:只需阅读标准输入即可。 几乎可以肯定,xargs在所有Unixy系统上都是用C编写的。

[xargs]如何分离出该格式的字符串并决定将哪些内容放入数组中?

简短的答案是,它取决于所使用的选项,因为xargs是一个非常强大的小工具。

完整的答案是,查看源代码。 GNU xargs(findutils的一部分)的源在这里 ,而FreeBSD版本的源在这里

代码答案取决于是否可以使用POSIX.1,尤其是getline()getdelim() 如果您有一个单字符分隔符(可以是任何单字节字符,甚至是nul),则可以使用getdelim()作为单独的字符串从输入中获取每个“参数”。 这是我要做的,但是它不是 ,而是解决方案。 (现在,如果您拥有一台维护良好的Unixy计算机,则几乎可以肯定的是,其C库内置了POSIX.1支持。)

为什么argc == 9?

如果我们使用printf 'o---o\\n| |\\n| |\\n| |\\no---o\\n'复制您的输入 printf 'o---o\\n| |\\n| |\\n| |\\no---o\\n' printf 'o---o\\n| |\\n| |\\n| |\\no---o\\n'并将其通过管道传递给xargs ./args ,输出与预期的一样,

argc = 9
argv[0] = "./args"
argv[1] = "o---o"
argv[2] = "|"
argv[3] = "|"
argv[4] = "|"
argv[5] = "|"
argv[6] = "|"
argv[7] = "|"
argv[8] = "o---o"

也就是说,您的ascii艺术的每个部分都在空格处分隔,并作为命令行参数提供。 如果我们将其通过管道传递到xargs -d '\\n' ./args ,则输出为

argc = 6
argv[0] = "./args"
argv[1] = "o---o"
argv[2] = "|   |"
argv[3] = "|   |"
argv[4] = "|   |"
argv[5] = "o---o"

如果您为自己编写了该初始args.c程序,则可能可以通过探索自己找到问题的答案。 这就是使编程如此强大的原因:您可以编写工具来帮助您理解希望解决的问题。 应用Unix哲学KISS原理意味着这些工具通常也很容易编写。 只需将它们写得很好就可以了,因此您可以信任它们的结果,而不必经常重写它们。

发生这种情况是因为xargs接受整个输入(所有行,而不仅仅是一行),并用空格字符将其分割。 因此,您的测试代码得到的参数是(您可以自己打印它们以进行调试):

  1. 。/测试
  2. -
  3. |
  4. |
  5. |
  6. |
  7. |
  8. |
  9. -

如果您打算从stdin读取而不是解析参数,请使用cin >> string_variable

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM