我有一个用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的输入。在主输入中使用带有(5,5)作为输入的xargs:
./a.out | xargs ./test
导致:
argc value is: 9
因此,我有两个问题。有没有一种方法不需要xargs,并且可以在c文件本身中完成?知道测试文件的输入后,为什么argc == 9?程序如何分离出该格式的字符串并确定要放入数组的内容?
答案 0 :(得分:2)
这会很长,所以请抓住您最喜欢的饮料。休息后不要只跳到答案。
首先,检查提供给程序的命令行参数,例如 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都具有命令行实用程序或内置的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程序。
在处理文件名或路径时,-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的一部分)的来源是here,而FreeBSD版本的来源是here。
代码答案取决于您是否可以使用POSIX.1,特别是getline()
或getdelim()
。如果您有一个单字符分隔符(可以是任何单字节字符,甚至是nul),则可以使用getdelim()
作为单独的字符串从输入中访问每个“参数”。这是我要做的,但不是unix,而是posix解决方案。 (现在,如果您拥有一台维护良好的Unixy计算机,则几乎可以肯定的是,其C库内置了POSIX.1支持。)
为什么argc == 9?
如果我们使用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 philosophy和KISS principle意味着这些工具通常也很容易编写。首先,只需将它们写得很好,这样您就可以信任它们的结果,而不必经常重写它们。
答案 1 :(得分:0)
之所以发生这种情况,是因为xargs接受整个输入(所有行,而不仅仅是一行),并用空格字符将其分割。因此,您的测试代码得到的参数是(您可以自己打印以进行调试):
如果您打算从stdin读取而不是解析参数,请使用cin >> string_variable
。