2013-10-03 132 views
4

我的程序是一個我想用C++編寫的常見shell。除了從命令行獲取命令之外,它還必須能夠讀取文件中的命令 - 文件名作爲可選參數傳遞,而不是通過重定向傳遞。有沒有一種優雅的方式來確定ifstream是否附加到stdin?

如果arg存在,我打開傳遞的文件名,否則打開「​​/ dev/stdin」。我對打開dev文件並不興奮,這不是我的主要問題,但如果有人有更好的方法,我很樂意聽到它。

最後,我必須閱讀命令給shell,但首先我必須提示如果我正在從標準輸入讀取或提示如果輸入來自文件的提示。我的問題是:有沒有更好的方法來確定在getCommand輸入流是stdin比聲明一個全局或傳遞布爾或類似的黑客?

它發生在我身上,如果我可以以某種方式使用std :: cin而不是打開/ dev文件,我可以將該流作爲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)) 
     //....... 
} 
+1

「我必須提出,如果我從標準輸入讀取輸入提示」 - 這似乎是錯誤的條件。如果您正在閱讀終端,您應該提示提示。這只是反社會的編寫特殊代碼*停止*有人管道或重定向輸入到您的程序:-) –

+0

是的,尋找輸入是一個終端。您可以使用源代碼來查找要使用的函數。或者對bash shell進行拉伸以查看它調用的文件描述符0. –

+0

示例代碼中還有許多其他問題。通過值傳遞std :: ifstream,而不是通過引用傳遞std :: string,當它是一個out參數。 – goji

回答

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的情況,所以這是一個有點bodge。不過,我可能會認爲這實際上是給定的滑稽要求的提高,因爲爲了解決您的限制,避免提示:-)

2

首先,如果你想有人能至少寫yourprogram /dev/stdin < input.txt,或sort input.txt | yourprogram /dev/stdin無論是從std::cin或從std::ifstream閱讀,我將實現在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)) { 
    ... 
} 

現在,以確定是否真正需要寫一個提示,這是不夠的確定你是否從標準輸入讀取,但你也需要確定是否標準輸入連接到屏幕和鍵盤。然而,沒有可移植的方式來這樣做。在UNIX上,有一些功能,如isatty(),可用於確定文件描述符0是否連接到tty。如果你想獲得看上你可以創建自定義std::streambuf這是迷上了一個std::ostream

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

:我會用這些信息來建立一個iword()然後可以用來檢查數據流需要一個提示它是tie()編輯到輸入流中的數據流並在sync()上寫入提示:每次對輸入流執行讀操作時,刷新tie() ed std::ostream。但是,這要求你只爲每個正在製作的提示讀取一次,例如使用std::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"; 
    } 
} 
相關問題