简体   繁体   English

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

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

I have a C program compiled with 3 .c files. 我有一个用3个.c文件编译的C程序。 Essentially, that program prints out squares to the standard output based on x and y size input which I have defined in the main. 本质上,该程序根据我在主菜单中定义的x和y大小输入,将正方形输出到标准输出。 The relevant code is below: 相关代码如下:

void    rush(int x, int y);

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

running the executable of the main like so: 运行main的可执行文件,如下所示:

./a.out

gives the following: 给出以下内容:

o-o
| |
o-o

and changing the parameters passed to the rush function to (5, 5) yields the following: 并将传递给rush函数的参数更改为(5,5)将产生以下结果:

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

You get the idea. 你明白了。 Each line is delimited by a \\n which allows the function to print the proper next line. 每行由\\ n分隔,这允许函数打印正确的下一行。 I have another test program which is a simple compiled main that simply prints the the value of ARGC as I wanted to test the behavior of what piping such an input would give. 我还有另一个测试程序,它是一个简单的编译主程序,可以像我想要的那样简单地打印ARGC的值,以测试这种输入将产生什么管道的行为。 The second main program is like so: 第二个主程序是这样的:

#include <stdio.h>

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

Running the following commands: 运行以下命令:

./a.out | ./test

I get the following output: 我得到以下输出:

argc value is: 1

Which didn't make sense to me initially, but then I remembered it was because some commands require xargs to accept input properly from stdin. 最初对我来说这没有意义,但后来我想起来是因为某些命令需要xargs才能正确接受来自stdin的输入。 Using xargs with (5, 5) as input in the main: 在主输入中使用xargs(5,5)作为输入:

./a.out | xargs ./test

resulted in: 导致:

argc value is: 9

Thus I have two questions. 因此,我有两个问题。 Is there a way to do this without needing xargs and can be done in the c files themselves? 有没有一种方法不需要xargs,并且可以在c文件本身中完成? And knowing the input to the test file, why is argc == 9? 知道测试文件的输入后,为什么argc == 9? How does the program separate out a string in that format and decide what to put in the array? 程序如何分离出该格式的字符串并确定要放入数组的内容?

This will be long, so grab your favourite drink. 这会很长,所以请抓住您最喜欢的饮料。 Don't just skip to the answers after the break. 休息后不要只跳到答案。

First, examine the command-line arguments supplied to a program, say args.c : 首先,检查提供给程序的命令行参数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;
}

Compile that using your favourite C compiler; 使用您喜欢的C编译器进行编译; I use gcc: 我使用gcc:

gcc -Wall -O2 args.c -o args

If you run say 如果你说

./args one two

it will output 它会输出

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

All Unixes have a command line utility or shell built-in printf that works much like the C printf() standard library function does. 所有Unix都具有命令行实用程序或shell内置的printf ,其工作方式与C printf()标准库函数非常相似。 We can run for example 我们可以举个例子

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

and we'll see 我们会看到

Hello, world!
Second line
Third line

Now, if we connect the two with a pipe, 现在,如果我们用管道将两者连接起来,

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

we get 我们得到

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

because there were no parameters to ./args , and the above args.c ignores standard input completely. 因为./args没有参数,并且上面的args.c完全忽略了标准输入。

The xargs utility command reads the input to it, and then executes its own command-line arguments as a command, adding the input it reads as additional parameters. xargs实用程序命令读取输入,然后将其自身的命令行参数作为命令执行,并将其读取的输入添加为附加参数。 It is highly configurable, too. 它也是高度可配置的。 If you run 如果你跑

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

you'll get 你会得到

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

because xargs turns each token in the input, separated by whitespace, into a command line argument. 因为xargs将输入中的每个标记(由空格分隔)转换为命令行参数。 If we tell xargs to turn each input line to a separate argument, by using the -d SEPARATOR option, with newline as the separator: 如果我们告诉xargs使用-d SEPARATOR选项,并以换行符作为分隔符,将每条输入行变成一个单独的参数:

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

we get 我们得到

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

If we tell xargs to add at most two arguments per command executed, by adding the -n 2 option, 如果我们告诉xargs每个执行的命令最多添加两个参数,方法是添加-n 2选项,

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

we'll get 我们会得到

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

This output means that our ./args got actually executed twice. 此输出意味着我们的./args实际上执行了两次。 First was effectively ./args 'Hello, world!' 'Second line' 首先是有效的./args 'Hello, world!' 'Second line' ./args 'Hello, world!' 'Second line' , and the second was ./args 'Third line' . ./args 'Hello, world!' 'Second line' ,第二个是./args 'Third line'

Another important option to xargs is -r , which tells it to not run the command without any additional arguments: xargs的另一个重要选项是-r ,它告诉它在没有任何其他参数的情况下不要运行命令:

true | xargs -r ./args

does not output anything, because xargs sees no input, and the -r option tells it to not run our args program if there are no additional arguments. 不会输出任何内容,因为xargs看不到任何输入,并且-r选项告诉它如果没有其他参数,则不要运行我们的args程序。

When manipulating file names or paths, the -0 (dash zero) option tells xargs that the input separator is the nul character, \\0 , which in C delimits strings. 在处理文件名或路径时, -0 (零号)选项告诉xargs输入分隔符是nul字符\\0 ,它在C中分隔字符串。 If we use that in the input to xargs, even strings with newlines and such will be correctly split into arguments. 如果我们在xargs的输入中使用该参数,则即使带有换行符的字符串等也将正确地拆分为参数。 For example: 例如:

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

will output 将输出

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

which is exactly what one would want, if processing file names or paths in a robust manner. 如果以一种可靠的方式处理文件名或路径,这正是人们想要的。


Is there a way to do this without needing xargs and can be done in the c files themselves? 有没有一种方法不需要xargs,并且可以在c文件本身中完成?

Of course: just read standard input. 当然:只需阅读标准输入即可。 xargs is almost certainly written in C itself on all Unixy systems. 几乎可以肯定,xargs在所有Unixy系统上都是用C编写的。

How does [xargs] separate out a string in that format and decide what to put in the array? [xargs]如何分离出该格式的字符串并决定将哪些内容放入数组中?

The short answer is that it depends on the options used, because xargs is a pretty powerful little tool. 简短的答案是,它取决于所使用的选项,因为xargs是一个非常强大的小工具。

The full answer is, look at the sources. 完整的答案是,查看源代码。 The source for the GNU xargs (part of findutils) is here , and the source for FreeBSD version is here . GNU xargs(findutils的一部分)的源在这里 ,而FreeBSD版本的源在这里

The code answer depends on whether you can use POSIX.1 or not, specifically getline() or getdelim() . 代码答案取决于是否可以使用POSIX.1,尤其是getline()getdelim() If you have a single-character separator (be it any single-byte character at all, even nul), you can use getdelim() to reach each "parameter" from the input as a separate string. 如果您有一个单字符分隔符(可以是任何单字节字符,甚至是nul),则可以使用getdelim()作为单独的字符串从输入中获取每个“参数”。 This is what I'd do, but it is not , it is a solution. 这是我要做的,但是它不是 ,而是解决方案。 (Nowadays, if you have a maintained Unixy computer, it is almost certain to have POSIX.1 support in its C library built-in.) (现在,如果您拥有一台维护良好的Unixy计算机,则几乎可以肯定的是,其C库内置了POSIX.1支持。)

Why is argc == 9? 为什么argc == 9?

If we duplicate your input using 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' printf 'o---o\\n| |\\n| |\\n| |\\no---o\\n' and pipe it to xargs ./args , the output is as expected, 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"

ie each part of your ascii art separated at whitespace, and supplied as a command-line parameter. 也就是说,您的ascii艺术的每个部分都在空格处分隔,并作为命令行参数提供。 If we pipe it to xargs -d '\\n' ./args , the output is 如果我们将其通过管道传递到xargs -d '\\n' ./args ,则输出为

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

If you had written that initial args.c program for yourself, you probably could have found the answer to your questions yourself via exploration. 如果您为自己编写了该初始args.c程序,则可能可以通过探索自己找到问题的答案。 That is what makes programming so powerful: you can write tools that help you understand the problems you wish to solve. 这就是使编程如此强大的原因:您可以编写工具来帮助您理解希望解决的问题。 Applying the Unix philosophy and the KISS principle means those tools are often quite simple to write as well. 应用Unix哲学KISS原理意味着这些工具通常也很容易编写。 Just write them well in the first place, so you can trust their results, and don't need to rewrite them too often. 只需将它们写得很好就可以了,因此您可以信任它们的结果,而不必经常重写它们。

This happens because xargs takes the entire input (all lines, not just a single line) and splits it by white-space characters. 发生这种情况是因为xargs接受整个输入(所有行,而不仅仅是一行),并用空格字符将其分割。 So the arguments your test code gets are (you can print them yourself to debug): 因此,您的测试代码得到的参数是(您可以自己打印它们以进行调试):

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

If you meant to read from stdin rather than parsing arguments use cin >> string_variable . 如果您打算从stdin读取而不是解析参数,请使用cin >> string_variable

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

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