gpt4 book ai didi

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

转载 作者:太空宇宙 更新时间:2023-11-04 07:51:54 25 4
gpt4 key购买 nike

我有一个用3.C文件编译的C程序。基本上,该程序根据我在main中定义的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的输入。使用(5,5)作为输入的xargs:
./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 'Third line'
xargs的另一个重要选项是 -r,它告诉它不要在没有任何附加参数的情况下运行命令:
true | xargs -r ./args

不输出任何内容,因为xargs看不到输入,如果没有其他参数, -r选项告诉它不要运行args程序。
当操作文件名或路径时,选项告诉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文件中完成?
当然:只要阅读标准输入。几乎可以肯定,在所有的Unixy系统上,xargs都是用C本身编写的。
[xargs]如何将该格式的字符串分离出来,并决定在数组中放入什么?
简而言之,这取决于使用的选项,因为xargs是一个非常强大的小工具。
完整的答案是,看看来源。GNU xargs(findutils的一部分)的源代码是 here,FreeBSD版本的源代码是 here
代码的答案取决于您是否可以使用POSIX.1,特别是 \0getline()。如果您有一个单字符分隔符(不管它是任何单字节字符,甚至是nul),您可以使用 getdelim()将输入中的每个“参数”作为单独的字符串来访问。这是我想做的,但它不是 ,而是 解决方案(现在,如果您有一台维护好的Unixy计算机,几乎可以肯定它的C库内置了POSIX.1支持。)
为什么argc==9?
如果我们使用 getdelim()复制您的输入并将其管道化到 printf 'o---o\n| |\n| |\n| |\no---o\n',则输出与预期一样,
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 ./args,则输出为
argc = 6
argv[0] = "./args"
argv[1] = "o---o"
argv[2] = "| |"
argv[3] = "| |"
argv[4] = "| |"
argv[5] = "o---o"

如果您为自己编写了最初的args.c程序,那么您可能已经通过探索自己找到了问题的答案。这就是编程如此强大的原因:你可以编写工具来帮助你理解你想要解决的问题。应用 Unix philosophyKISS principle意味着这些工具通常也很容易编写。先把它们写好,这样你就可以相信它们的结果,而且不需要经常重写它们。

关于c - 为什么我的程序不能正确接受另一个程序的管道输出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53245031/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com