4

私のプログラムは、C++ で記述しようとしている一般的なシェルです。コマンド ラインからコマンドを取得するだけでなく、ファイル内のコマンドを読み取ることができる必要があります。ファイル名は、リダイレクトではなく、オプションの引数として渡されます。

引数が存在する場合は、渡されたファイル名を開きます。それ以外の場合は、「/dev/stdin」を開きます。私は開発ファイルを開くことにわくわくしていません。それは私の主な質問ではありませんが、誰かがより良い方法を持っているなら、私はそれを聞きたいです.

最終的には、シェルに与えられたコマンドを読み取る必要がありますが、最初に、標準入力から読み取る場合はプロンプトを表示するか、入力がファイルからのものである場合はプロンプトをスキップする必要があります。getCommand私の質問は次のとおりです。グローバルを宣言したり、ブール値や同様のハックを渡すよりも、入力ストリームが標準入力であるかどうかを判断するより良い方法はありますか?

/dev ファイルを開くのではなく、どうにかして std::cin を使用できれば、ストリームをistream. そうすることで両者の区別がつきやすくなるでしょうか。例えばif (source == cin)

あらゆる提案に感謝します。

bool getCommand(ifstream source, std::string command)
{
    if (source == stdin)
        //print prompt to stdout

    // do the rest of stuff

    return true;
}


int main(int argc, char *argv[])
{
    std::ifstream input;
    std::string command;

    if (argc == 2)
    {
        input.open(argv[1], std::ifstream::in);

        if (! input)
        {
            perror("input command file stream open");
            exit(EXIT_FAILURE);
        }
    }
    else
    {
        input.open("/dev/stdin", std::ifstream::in);

        if (! input)
        {
            perror("input stdin stream open");
            exit(EXIT_FAILURE);
        }
    }

    //.......

    if (getCommand(input, command))
        //.......
}
4

2 に答える 2

3

std::istream&変数の型として使用する場合std::cinは、開く代わりに使用でき/dev/stdinます。

std::ifstream fileinput;
if (argc == 2)
{
    fileinput.open(argv[1], std::ifstream::in);

    if (! fileinput)
    {
        perror("input command file stream open");
        exit(EXIT_FAILURE);
    }
}
std::istream &input = (argc == 2) ? fileinput : std::cin;

質問の残りの部分は、tty の識別に関するものにする必要があります ( Detect if stdin is a terminal or pipe? )。しかし、それが失敗した場合、上記の変更を行ったら、次のアドレスと比較できますstd::cin

if (&input == &std::cin) {
    std::cout << "prompt:";
}

明らかに、コマンド ライン引数が である場合は識別されないため/dev/stdin、少し問題があります。しかし、誰かが少なくとも書くことができるのでyourprogram /dev/stdin < input.txt、またはsort input.txt | yourprogram /dev/stdinあなたの制限を回避してプロンプトを回避するために、面白い要件を考えると、それは実際には改善であると私はおそらく主張します:-)

于 2013-10-04T00:08:36.997 に答える
2

std::cinまず、 a から、または aから読み取りたい場合std::ifstreamは、 an の観点から作業を実装し、std::istream確実に開くことを避け/dev/stdinます。

std::istream  in(std::cin.rdbuf());
std::ifstream fin;
if (argc == 2) {
    fin.open(av[1]);
    // check that the file is actually open or produce an error
    in.rdbuf(fin.rdbuf());
}
else {
    // determine if you need to create a prompt
}

if (getCommand(in, command)) {
    ...
}

ここで、実際にプロンプ​​トを作成する必要があるかどうかを判断するには、標準入力から読み取っているかどうかを判断するだけでは不十分ですが、標準入力が画面とキーボードを備えた何かに接続されているかどうかも判断する必要があります。ただし、移植可能な方法はありません。isatty()UNIX では、ファイル記述子 0 が tty に接続されているかどうかを判別するために使用できる次のような関数があります。iword()この情報を使用して、ストリームにプロンプ​​トが必要かどうかを確認するために使用できるをセットアップします。

static int const needs_prompt = std::ios_base::xalloc();
...
in.iword(needs_prompt) = isatty(0);
...
if (in.iword(needs_prompt)) {
    std::cout << "pompt>";
}

凝りたい場合は、入力ストリーム stream に ed され、プロンプトを書き込むカスタムを作成できます。入力ストリームstd::streambufで読み取り操作が行われるたびに、edがフラッシュされます。ただし、これには、生成されるプロンプトごとに 1 回だけ読み取る必要があります (例: を使用) 。明らかに、プロンプトが必要ない場合は、. 以下は、自動プロンプト アプローチを示す簡単なプログラムです (他の作業は一切行いません)。std::ostreamtie()sync()tie()std::ostreamstd::getline(in, line)tie()std::ostream

#include <iostream>
#include <streambuf>

struct prompt
    : std::streambuf
{
    std::string   d_prompt;
    std::ostream& d_out;
    prompt(std::string const& p, std::ostream& out)
        : d_prompt(p)
        , d_out(out)
    {
    }
    int sync() {
        this->d_out << this->d_prompt << std::flush;
        return 0;
    }
};

int main()
{
    prompt       p("prompt>", std::cout);
    std::ostream helper(&p);
    std::cin.tie(&helper);

    for (std::string line; std::getline(std::cin, line); ) {
        std::cout << "read '" << line << "'\n";
    }
}
于 2013-10-04T00:08:47.637 に答える