![](/img/trans.png)
[英]Why does write() to pipe exit program when pipe writes to stdout?
[英]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.