+
соответствует хотя бы одному символу
*
соответствует любому числу (включая 0) символов
. ?
указывает ленивое выражение, поэтому оно будет соответствовать как можно меньше символов.
#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
std::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
if (!pipe) throw std::runtime_error("popen() failed!");
while (!feof(pipe.get())) {
if (fgets(buffer.data(), 128, pipe.get()) != nullptr)
result += buffer.data();
}
return result;
}
Pre-C ++ 11 version:
#include <iostream>
#include <stdexcept>
#include <stdio.h>
#include <string>
std::string exec(const char* cmd) {
char buffer[128];
std::string result = "";
FILE* pipe = popen(cmd, "r");
if (!pipe) throw std::runtime_error("popen() failed!");
try {
while (!feof(pipe)) {
if (fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
} catch (...) {
pclose(pipe);
throw;
}
pclose(pipe);
return result;
}
Замените popen
и pclose
на _popen
и _pclose
для Windows.
Я не мог понять, почему popen / pclose отсутствует в Codeblocks / MinGW. Поэтому я работал над проблемой, используя вместо этого CreateProcess () и CreatePipe (). Вот решение, которое сработало для меня:
//C++11
#include <cstdio>
#include <iostream>
#include <windows.h>
#include <cstdint>
#include <deque>
#include <string>
#include <thread>
using namespace std;
int SystemCapture(
string CmdLine, //Command Line
string CmdRunDir, //set to '.' for current directory
string& ListStdOut, //Return List of StdOut
string& ListStdErr, //Return List of StdErr
uint32_t& RetCode) //Return Exit Code
{
int Success;
SECURITY_ATTRIBUTES security_attributes;
HANDLE stdout_rd = INVALID_HANDLE_VALUE;
HANDLE stdout_wr = INVALID_HANDLE_VALUE;
HANDLE stderr_rd = INVALID_HANDLE_VALUE;
HANDLE stderr_wr = INVALID_HANDLE_VALUE;
PROCESS_INFORMATION process_info;
STARTUPINFO startup_info;
thread stdout_thread;
thread stderr_thread;
security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
security_attributes.bInheritHandle = TRUE;
security_attributes.lpSecurityDescriptor = nullptr;
if (!CreatePipe(&stdout_rd, &stdout_wr, &security_attributes, 0) ||
!SetHandleInformation(stdout_rd, HANDLE_FLAG_INHERIT, 0)) {
return -1;
}
if (!CreatePipe(&stderr_rd, &stderr_wr, &security_attributes, 0) ||
!SetHandleInformation(stderr_rd, HANDLE_FLAG_INHERIT, 0)) {
if (stdout_rd != INVALID_HANDLE_VALUE) CloseHandle(stdout_rd);
if (stdout_wr != INVALID_HANDLE_VALUE) CloseHandle(stdout_wr);
return -2;
}
ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
ZeroMemory(&startup_info, sizeof(STARTUPINFO));
startup_info.cb = sizeof(STARTUPINFO);
startup_info.hStdInput = 0;
startup_info.hStdOutput = stdout_wr;
startup_info.hStdError = stderr_wr;
if(stdout_rd || stderr_rd)
startup_info.dwFlags |= STARTF_USESTDHANDLES;
// Make a copy because CreateProcess needs to modify string buffer
char CmdLineStr[MAX_PATH];
strncpy(CmdLineStr, CmdLine.c_str(), MAX_PATH);
CmdLineStr[MAX_PATH-1] = 0;
Success = CreateProcess(
nullptr,
CmdLineStr,
nullptr,
nullptr,
TRUE,
0,
nullptr,
CmdRunDir.c_str(),
&startup_info,
&process_info
);
CloseHandle(stdout_wr);
CloseHandle(stderr_wr);
if(!Success) {
CloseHandle(process_info.hProcess);
CloseHandle(process_info.hThread);
CloseHandle(stdout_rd);
CloseHandle(stderr_rd);
return -4;
}
else {
CloseHandle(process_info.hThread);
}
if(stdout_rd) {
stdout_thread=thread([&]() {
DWORD n;
const size_t bufsize = 1000;
char buffer [bufsize];
for(;;) {
n = 0;
int Success = ReadFile(
stdout_rd,
buffer,
(DWORD)bufsize,
&n,
nullptr
);
printf("STDERR: Success:%d n:%d\n", Success, (int)n);
if(!Success || n == 0)
break;
string s(buffer, n);
printf("STDOUT:(%s)\n", s.c_str());
ListStdOut += s;
}
printf("STDOUT:BREAK!\n");
});
}
if(stderr_rd) {
stderr_thread=thread([&]() {
DWORD n;
const size_t bufsize = 1000;
char buffer [bufsize];
for(;;) {
n = 0;
int Success = ReadFile(
stderr_rd,
buffer,
(DWORD)bufsize,
&n,
nullptr
);
printf("STDERR: Success:%d n:%d\n", Success, (int)n);
if(!Success || n == 0)
break;
string s(buffer, n);
printf("STDERR:(%s)\n", s.c_str());
ListStdOut += s;
}
printf("STDERR:BREAK!\n");
});
}
WaitForSingleObject(process_info.hProcess, INFINITE);
if(!GetExitCodeProcess(process_info.hProcess, (DWORD*) &RetCode))
RetCode = -1;
CloseHandle(process_info.hProcess);
if(stdout_thread.joinable())
stdout_thread.join();
if(stderr_thread.joinable())
stderr_thread.join();
CloseHandle(stdout_rd);
CloseHandle(stderr_rd);
return 0;
}
int main()
{
int rc;
uint32_t RetCode;
string ListStdOut;
string ListStdErr;
cout << "STARTING.\n";
rc = SystemCapture(
"C:\\Windows\\System32\\ipconfig.exe", //Command Line
".", //CmdRunDir
ListStdOut, //Return List of StdOut
ListStdErr, //Return List of StdErr
RetCode //Return Exit Code
);
if (rc < 0) {
cout << "ERROR: SystemCapture\n";
}
cout << "STDOUT:\n";
cout << ListStdOut;
cout << "STDERR:\n";
cout << ListStdErr;
cout << "Finished.\n";
cout << "Press Enter to Continue";
cin.ignore();
return 0;
}
Я бы использовал popen () (++ waqas) .
Но иногда вам нужно читать и писать ...
Похоже, никто
(Предположим, что среда Unix / Linux / Mac, или, возможно, Windows с уровнем совместимости POSIX ...)
enum PIPE_FILE_DESCRIPTERS
{
READ_FD = 0,
WRITE_FD = 1
};
enum CONSTANTS
{
BUFFER_SIZE = 100
};
int
main()
{
int parentToChild[2];
int childToParent[2];
pid_t pid;
string dataReadFromChild;
char buffer[ BUFFER_SIZE + 1 ];
ssize_t readResult;
int status;
ASSERT_IS(0, pipe(parentToChild));
ASSERT_IS(0, pipe(childToParent));
switch ( pid = fork() )
{
case -1:
FAIL( "Fork failed" );
exit(-1);
case 0: /* Child */
ASSERT_NOT(-1, dup2( parentToChild[ READ_FD ], STDIN_FILENO ) );
ASSERT_NOT(-1, dup2( childToParent[ WRITE_FD ], STDOUT_FILENO ) );
ASSERT_NOT(-1, dup2( childToParent[ WRITE_FD ], STDERR_FILENO ) );
ASSERT_IS( 0, close( parentToChild [ WRITE_FD ] ) );
ASSERT_IS( 0, close( childToParent [ READ_FD ] ) );
/* file, arg0, arg1, arg2 */
execlp( "ls", "ls", "-al", "--color" );
FAIL( "This line should never be reached!!!" );
exit(-1);
default: /* Parent */
cout << "Child " << pid << " process running..." << endl;
ASSERT_IS( 0, close( parentToChild [ READ_FD ] ) );
ASSERT_IS( 0, close( childToParent [ WRITE_FD ] ) );
while ( true )
{
switch ( readResult = read( childToParent[ READ_FD ],
buffer, BUFFER_SIZE ) )
{
case 0: /* End-of-File, or non-blocking read. */
cout << "End of file reached..." << endl
<< "Data received was ("
<< dataReadFromChild.size() << "):" << endl
<< dataReadFromChild << endl;
ASSERT_IS( pid, waitpid( pid, & status, 0 ) );
cout << endl
<< "Child exit staus is: " << WEXITSTATUS(status) << endl
<< endl;
exit(0);
case -1:
if ( (errno == EINTR) || (errno == EAGAIN) )
{
errno = 0;
break;
}
else
{
FAIL( "read() failed" );
exit(-1);
}
default:
dataReadFromChild . append( buffer, readResult );
break;
}
} /* while ( true ) */
} /* switch ( pid = fork() )*/
}
Вы также можете играть с помощью select () и неблокирующих чтений.
fd_set readfds;
struct timeval timeout;
timeout.tv_sec = 0; /* seconds */
timeout.tv_usec = 1000; /* microseconds */
FD_ZERO(&readfds);
FD_SET( childToParent[ READ_FD ], &readfds );
switch ( select ( 1 + childToParent[READ_FD], &readfds, (fd_set*)NULL, (fd_set*)NULL, & timeout ) )
{
case 0: /* Timeout expired */
break;
case -1:
if ( (errno == EINTR) || (errno == EAGAIN) )
{
errno = 0;
break;
}
else
{
FAIL( "Select() Failed" );
exit(-1);
}
case 1: /* We have input */
readResult = read( childToParent[ READ_FD ], buffer, BUFFER_SIZE );
// However you want to handle it...
break;
default:
FAIL( "How did we see input on more than one file descriptor?" );
exit(-1);
}
execlp
есть ошибка: последний переданный указатель arg
должен быть (char *) NULL
для правильного завершения списка вариационных аргументов (см. execlp(3)
для справки).
– Kristóf Marussy
19 October 2013 в 19:38
Вы можете использовать библиотеку Boost.Process . Тем не менее, это не официальная часть повышения. Я видел, как он хорошо работает для других. К сожалению, прогресс boost.process, по-видимому, застопорился. pstreams - другой (по-видимому активный) проект. Конечно, стоит попробовать, я бы сказал, но это только для совместимых с posix операционных систем.
Два возможных подхода.
1 / Я не думаю, что popen()
является частью стандарта C ++ (это часть POSIX из памяти), но он доступен для всех UNIX, с которыми я работал ( и вы, кажется, нацелились на UNIX, так как ваша команда «./some_command
»).
2 / В случае, если нет popen()
, вы можете использовать system( "./some_command >/tmp/some_command.out" ) ;
, затем использовать обычный Функции ввода / вывода для обработки выходного файла.
Получение как stdout, так и stderr (а также запись в stdin, не показано здесь) легко peasy с моим заголовком pstreams , который определяет классы iostream, которые работают как popen
:
#include <pstream.h>
#include <string>
#include <iostream>
int main()
{
// run a process and create a streambuf that reads its stdout and stderr
redi::ipstream proc("./some_command", redi::pstreams::pstdout | redi::pstreams::pstderr);
std::string line;
// read child's stdout
while (std::getline(proc.out(), line))
std::cout << "stdout: " << line << '\n';
// read child's stderr
while (std::getline(proc.err(), line))
std::cout << "stderr: " << line << '\n';
}
popen
требует использования C stdio API, я предпочитаю API iostreams. popen
требует, чтобы вы вручную очистили ручку FILE
, pstreams сделали это автоматически. popen
принимает только аргумент const char*
, который требует осторожности, чтобы избежать атак оболочки, pstreams позволяет вам передавать вектор строк, подобных execv
, что является более безопасным. popen
дает вам ничего, кроме трубы, pstreams сообщает вам PID ребенка, позволяющий отправлять сигналы, например. убить его, если он заблокирован или не выйдет. Все это преимущества, даже если вам нужен только однонаправленный ввод-вывод.
– Jonathan Wakely
10 October 2012 в 18:08
Это может быть переносное решение. Соответствует стандартам.
#include<iostream>
#include<fstream>
#include<string>
#include<cstdlib>
#include<sstream>
std::string ssystem (const char *command) {
char tmpname [L_tmpnam];
std::tmpnam ( tmpname );
std::string scommand = command;
std::string cmd = scommand + " >> " + tmpname;
std::system(cmd.c_str());
std::ifstream file(tmpname, std::ios::in );
std::string result;
if (file) {
while (!file.eof()) result.push_back(file.get());
file.close();
}
remove(tmpname);
return result;
}
//for cygwin
int main(int argc, char *argv[])
{
std::string bash = "FILETWO=/cygdrive/c/*\nfor f in $FILETWO\ndo\necho \"$f\"\ndone ";
std::string in;
std::string s = ssystem(bash.c_str());
std::istringstream iss(s);
std::string line;
while ( std::getline(iss, line) )
{
std::cout << "LINE-> " + line + " length: " << line.length() << std::endl;
}
std::cin >> in;
return 0;
}
tmpnam
опасно, лучше использовать mkstemp
& quot;
– Mark Lakata
28 August 2014 в 00:44
Для Windows popen
также работает, но открывается окно консоли, которое быстро мигает над вашим пользовательским интерфейсом. Если вы хотите быть профессионалом, лучше отключить этот «мигающий» (особенно если конечный пользователь может его отменить).
Итак, вот моя собственная версия для Windows:
(Этот код частично рекомбинирован из идей, написанных в образцах кодекса и MSDN)
//
// Execute a command and get the results. (Only standard output)
//
CStringA ExecCmd(
const wchar_t* cmd // [in] command to execute
)
{
CStringA strResult;
HANDLE hPipeRead, hPipeWrite;
SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES) };
saAttr.bInheritHandle = TRUE; //Pipe handles are inherited by child process.
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe to get results from child's stdout.
if ( !CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0) )
return strResult;
STARTUPINFO si = { sizeof(STARTUPINFO) };
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdOutput = hPipeWrite;
si.hStdError = hPipeWrite;
si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags.
PROCESS_INFORMATION pi = { 0 };
BOOL fSuccess = CreateProcessW( NULL, (LPWSTR)cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
if (! fSuccess)
{
CloseHandle( hPipeWrite );
CloseHandle( hPipeRead );
return strResult;
}
bool bProcessEnded = false;
for (; !bProcessEnded ;)
{
// Give some timeslice (50ms), so we won't waste 100% cpu.
bProcessEnded = WaitForSingleObject( pi.hProcess, 50) == WAIT_OBJECT_0;
// Even if process exited - we continue reading, if there is some data available over pipe.
for (;;)
{
char buf[1024];
DWORD dwRead = 0;
DWORD dwAvail = 0;
if (!::PeekNamedPipe(hPipeRead, NULL, 0, NULL, &dwAvail, NULL))
break;
if (!dwAvail) // no data available, return
break;
if (!::ReadFile(hPipeRead, buf, min(sizeof(buf) - 1, dwAvail), &dwRead, NULL) || !dwRead)
// error, the child process might ended
break;
buf[dwRead] = 0;
strResult += buf;
}
} //for
CloseHandle( hPipeWrite );
CloseHandle( hPipeRead );
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
return strResult;
} //ExecCmd
wchar_t
и CreateProcessW
в качестве ненужного ограничения.
– Wolf
16 May 2017 в 10:13
The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.
i>. Возможно, лучше сначала скопировать командную строку в отдельный буфер, чтобы не позволяет собеседнику изменить исходный вход.
– Wolf
18 May 2017 в 12:29
result += buffer
, поэтому труба может быть неправильно закрыта. – Fred Foo 19 May 2012 в 21:27int main(){ puts("ERROR"); }
. – dreamlax 16 December 2013 в 23:03