簡體   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