3

df次のコードを使用して、Linux でコマンドの結果を読み取ろうとしていますpopen

#include <iostream> // file and std I/O functions

int main(int argc, char** argv) {
    FILE* fp;
    char * buffer;
    long bufSize;
    size_t ret_code;

    fp = popen("df", "r");
    if(fp == NULL) { // head off errors reading the results
        std::cerr << "Could not execute command: df" << std::endl;
        exit(1);
    }

    // get the size of the results
    fseek(fp, 0, SEEK_END);
    bufSize = ftell(fp);
    rewind(fp);

    // allocate the memory to contain the results
    buffer = (char*)malloc( sizeof(char) * bufSize );
    if(buffer == NULL) {
        std::cerr << "Memory error." << std::endl;
        exit(2);
    }

    // read the results into the buffer
    ret_code = fread(buffer, 1, sizeof(buffer), fp);
    if(ret_code != bufSize) {
        std::cerr << "Error reading output." << std::endl;
        exit(3);
    }

    // print the results
    std::cout << buffer << std::endl;

    // clean up
    pclose(fp);
    free(buffer);
    return (EXIT_SUCCESS);
}

このコードは、終了ステータスが「2」の「メモリエラー」を表示しているため、どこで失敗しているかがわかりますが、理由はわかりません.

Ubuntu ForumsC++ Referenceで見つけたサンプル コードからこれをまとめたので、私はそれと結婚していません。system() 呼び出しの結果を読み取るためのより良い方法を誰かが提案できる場合、私は新しいアイデアを受け入れます。

元の編集:さて、bufSize否定的になりつつありますが、今ではその理由がわかりました. 私が素朴に試みたように、パイプにランダムにアクセスすることはできません。

私はこれをやろうとする最初の人になることはできません. system() 呼び出しの結果を C++ の変数に読み込む方法の例を教えて (または指摘して) もらえますか?

4

9 に答える 9

7

あなたはこれをあまりにも難しくしています。 popen(3)FILE *は、標準のパイプファイル、つまり改行で終了するレコードに対して通常の old を返します。C のようにfgets(3)を使用すると、非常に効率的に読み取ることができます。

#include <stdio.h>
char bfr[BUFSIZ] ;
FILE * fp;
// ...
if((fp=popen("/bin/df", "r")) ==NULL) {
   // error processing and return
}
// ...
while(fgets(bfr,BUFSIZ,fp) != NULL){
   // process a line
}

C++ ではさらに簡単です --

#include <cstdio>
#include <iostream>
#include <string>

FILE * fp ;

if((fp= popen("/bin/df","r")) == NULL) {
    // error processing and exit
}

ifstream ins(fileno(fp)); // ifstream ctor using a file descriptor

string s;
while (! ins.eof()){
    getline(ins,s);
    // do something
}

そこにはさらにエラー処理がありますが、それがアイデアです。FILE *要点は、 from popenを の と同じように扱い、 1FILE *行ずつ読むことです。

于 2008-11-24T04:03:22.760 に答える
5

なぜstd::malloc()失敗するのでしょうか?

明らかな理由は、「std::ftell()負の符号付き数値が返され、それが巨大な符号なし数値として扱われたため」です。

ドキュメントによると、std::ftell()失敗すると -1 が返されます。失敗する明らかな理由の 1 つは、パイプまたは FIFO でシークできないことです

逃げる道がない; コマンド出力を読まなければ長さを知ることはできず、一度しか読むことができません。必要に応じてバッファを拡張するか、オンザフライで解析して、チャンクで読み取る必要があります。

dfしかし、もちろん、おそらくその情報を取得するために使用するシステム コールを直接使用することで、問題全体を簡単に回避できますstatvfs()

于 2008-11-21T17:17:55.843 に答える
4

(用語に関する注意: Unix および Linux の「システム コール」は、一般に、ユーザー空間コードからカーネル関数を呼び出すことを指します。それを「system()呼び出しの結果」または「呼び出しの結果」system(3)と呼ぶと、より明確になります。しかし、単に「プロセスの出力をキャプチャする」と言ったほうがよいでしょう。)

とにかく、他のファイルを読み取ることができるのと同じように、プロセスの出力を読み取ることができます。具体的には:

  • pipe()fork()、およびを使用してプロセスを開始できますexec()。これによりファイル記述子が得られ、ループを使用しread()てファイル記述子からバッファとファイル記述子に変換close()できます。これは最も低いレベルのオプションであり、ほとんどの制御が可能です。
  • を使用してプロセスを開始できpopen()ます。これにより、ファイル ストリームが得られます。ループでは、Zarawesome の回答が示すように、fread()fgets()、またはを使用してストリームから一時変数またはバッファに using を読み取り、そのバッファを処理するか、C++ 文字列に追加することができます。fgetc()
  • を使用してプロセスを開始しpopen()、非標準の__gnu_cxx::stdio_filebufを使用してそれをラップし、std::istreamからを作成してstdio_filebuf、他の C++ ストリームと同様に扱うことができます。これは、最も C++ に似たアプローチです。このアプローチの例のパート 1パート 2を次に示します。
于 2008-11-21T19:20:53.380 に答える
3

このようなパイプストリームを fseek/ftell できるかどうかはわかりません。

bufSize の値を確認しましたか? malloc が失敗する理由の 1 つは、バッファーのサイズが非常に大きいためです。

于 2008-11-21T17:14:34.943 に答える
2

時間を割いて回答してくれた皆さんに感謝します。同僚がostringstreamクラスを教えてくれました。元の質問で私がやろうとしていたことを本質的に行うコード例を次に示します。

#include <iostream> // cout
#include <sstream> // ostringstream

int main(int argc, char** argv) {
    FILE* stream = popen( "df", "r" );
    std::ostringstream output;

    while( !feof( stream ) && !ferror( stream ))
    {
        char buf[128];
        int bytesRead = fread( buf, 1, 128, stream );
        output.write( buf, bytesRead );
    }
    std::string result = output.str();
    std::cout << "<RESULT>" << std::endl << result << "</RESULT>" << std::endl;
    return (0);
}
于 2008-11-24T01:43:33.620 に答える
1

アップデートの質問に答えるには:

char buffer[1024];
char * line = NULL;
while ((line = fgets(buffer, sizeof buffer, fp)) != NULL) {
    // parse one line of df's output here.
}

これで十分でしょうか?

于 2008-11-21T19:35:51.133 に答える
0

パイプはランダム アクセスではありません。それらはシーケンシャルです。つまり、バイトを読み取ると、パイプはそれを再度送信しません。つまり、当然、巻き戻しはできません。

データをユーザーに出力したいだけの場合は、次のようにすることができます。

// your file opening code

while (!feof(fp))
{
char c = getc(fp);
std::cout << c;
}

これにより、バイトが df パイプから 1 つずつ引き出され、出力に直接送られます。

df 出力全体にアクセスする場合は、ファイルにパイプしてそのファイルを読み取るか、出力を C++ 文字列などの構造に連結できます。

于 2008-11-21T17:43:43.403 に答える
0

bufSize を確認してください。ftellエラー時に -1 を返す可能性があり、これにより、NULL 値を持つバッファーで malloc による未割り当てが発生する可能性があります。

が失敗する理由ftellは、popen が原因です。パイプを検索することはできません。

于 2008-11-21T17:13:03.450 に答える
0

最初に確認することは、bufSize の値です。たまたま <= 0 の場合、その時点でサイズ 0 のバッファーを割り当てようとしているため、malloc が NULL を返す可能性があります。

もう 1 つの回避策は、malloc に n >= 1 のサイズ (bufSize + n) のバッファーを提供するように依頼することです。これにより、この特定の問題を回避できます。

それはさておき、あなたが投稿したコードはC++ではなく純粋なCであるため、含めるのは少しやりすぎです。

于 2008-11-21T17:14:30.653 に答える