簡體   English   中英

為什么處於寫入模式的管道會輸出某些內容?

[英]Why does a pipe in write mode outputs something?

當我在用C ++玩管道時,偶然發現了一些相當有趣的東西。

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

int main()
{
    FILE *pystream = popen("python","w"); // Calling the python console

    fprintf(pystream,"print(2+3)"); // Making it do something   

    pclose(pystream); // Closing the pipe

    return 0;
}

此代碼輸出5.但是為什么? 並可以讀取“輸出”或將其存儲在某個地方嗎? 我對C緩沖區和管道還很陌生,所以我不知道我是否使用了正確的術語。

當您這樣編寫時,實際上是在寫剛開始的進程的stdin ,在這種情況下是python REPL。 在Linux上,python REPL直接獲取表達式,即未輸入表達式。這是系統命令

read(0, "print(2+3)", 4096) = 10

如果您在終端中執行此操作,則終端一次將每個字符讀入一個字符,當它得到回車符時,它將寫一個換行符\\n

read(0, "\r", 1)                        = 1
write(1, "\n", 1

然后執行計算並將結果寫出

write(1, "5\n", 25

您可以通過終端並將數據直接寫入python解釋器的stdin中。 如果您想了解如何輕松解決這個問題,請嘗試以下代碼。

#include <cstdio>
#include <iostream>
#include <string>
int main()
{
    FILE *pystream = popen("python","w"); // Calling the python console
    fprintf(pystream,"print(2+3)"); // Making it do something   
    fprintf(pystream,"print(2+3)"); // Making it do something   
    pclose(pystream); // Closing the pipe
    return 0;
}

您將收到一個語法錯誤,要使其正常工作,需要向標准輸入中輸入回車符或換行符以分隔兩行,即添加回車符...

fprintf(pystream,"print(2+3)\r");

您正在執行的命令的標准輸出連接到程序的標准輸出,因此,當Python寫入其標准輸出時,它也會出現在流程的標准輸出中。

如果在運行Python之前有待處理的輸出,則不會刷新該輸出,並且將在Python返回后顯示。 例如,

std::cout << "Hello";

(沒有endl ,字符串中沒有\\n )在popen()和之前

std::cout << " World\n";

pclose()意味着您將在Hello World之前看到Python輸出。


如果要寫Python並在程序中讀回結果,則不能再使用popen()pclose() 相反,您需要使用pipe()兩次(一個管道用於與Python對話,一個管道用於從Python讀取),並且您需要使用fork()exec()dup2() -可能是; 否則為dup() ,然后close()以使操作正常進行。 您將使用文件描述符,因此也會在父進程中使用read()write()系統調用。

這些都是比C ++函數更多的C函數(系統調用)。

此代碼有效:

#include <unistd.h>
#include <cstdio>
#include <cstring>

int main()
{
    int p1[2];
    int p2[2];
    if (pipe(p1) != 0 || pipe(p2) != 0)
        return 1;
    int pid;
    if ((pid = fork()) < 0)
        return 1;
    if (pid == 0)
    {
        dup2(p1[0], STDIN_FILENO);
        dup2(p2[1], STDOUT_FILENO);
        close(p1[0]);
        close(p1[1]);
        close(p2[0]);
        close(p2[1]);
        execlp("python", "python", (char *)0);
        fprintf(stderr, "failed to exec python\n");
        return 1;
    }
    else
    {
        close(p1[0]);
        close(p2[1]);
        const char command[] = "print(2+3)\n";
        int len = strlen(command);
        if (write(p1[1], command, len) != len)
        {
            fprintf(stderr, "failed to write command to python\n");
            return 1;
        }
        close(p1[1]);
        char buffer[256];
        int nbytes;
        if ((nbytes = read(p2[0], buffer, sizeof(buffer))) <= 0)
        {
            fprintf(stderr, "failed to read response from python\n");
            return 1;
        }
        printf("Python said: (%d) [%.*s]\n", nbytes, nbytes, buffer);
        close(p2[0]);
        printf("Finished\n");
    }
    return 0;
}

壞消息是,在同步讀取Python響應的同時更改此代碼以編寫多個命令是行不通的。 Python不會像輸入時將其作為終端一樣單獨處理每一行; 它會在響應之前先讀取所有數據。 您可以使用python -i解決此問題,但是Python的提示會出現在stderr 因此,您可以將其重定向到/dev/null以使其丟失:

#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
#include <cstring>

int main()
{
    int p1[2];
    int p2[2];
    if (pipe(p1) != 0 || pipe(p2) != 0)
        return 1;
    int pid;
    if ((pid = fork()) < 0)
        return 1;
    if (pid == 0)
    {
        dup2(p1[0], STDIN_FILENO);
        dup2(p2[1], STDOUT_FILENO);
        close(p1[0]);
        close(p1[1]);
        close(p2[0]);
        close(p2[1]);
        int dn = open("/dev/null", O_WRONLY);
        if (dn >= 0)
        {
            dup2(dn, STDERR_FILENO);
            close(dn);
        }
        execlp("python", "python", "-i", (char *)0);
        fprintf(stderr, "failed to exec python\n");
        return 1;
    }
    else
    {
        close(p1[0]);
        close(p2[1]);
        const char *commands[] =
        {
            "print(2+3)\n",
            "print(3+4)\n",
        };
        enum { NUM_COMMANDS = sizeof(commands) / sizeof(commands[0]) };
        for (int i = 0; i < NUM_COMMANDS; i++)
        {
            int len = strlen(commands[i]);
            if (write(p1[1], commands[i], len) != len)
            {
                fprintf(stderr, "failed to write command to python\n");
                return 1;
            }
            char buffer[256];
            int nbytes;
            if ((nbytes = read(p2[0], buffer, sizeof(buffer))) <= 0)
            {
                fprintf(stderr, "failed to read response from python\n");
                return 1;
            }
            printf("Python said: (%d) [%.*s]\n", nbytes, nbytes, buffer);
        }
        close(p1[1]);
        close(p2[0]);
        printf("Finished\n");
    }
    return 0;
}

不重定向stderr

Python 2.7.10 (default, Oct 23 2015, 19:19:21) 
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> >>> Python said: (2) [5
]
>>> Python said: (2) [7
]
Finished

使用stderr重定向:

Python said: (2) [5
]
Python said: (2) [7
]
Finished

丟失標准錯誤輸出到/dev/null的缺點是,當Python對象發送到要執行的對象時,您不會得到任何通知-代碼將掛起。 解決這個問題很有趣(第三個管道,並使用poll()epoll()或-滅掉了思想epoll() select()將是解決問題的一種方法)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM