Related
I am looking for a way to get the output of a command when it is run from within a C++ program. I have looked at using the system() function, but that will just execute a command. Here's an example of what I'm looking for:
std::string result = system("./some_command");
I need to run an arbitrary command and get its output. I've looked at boost.org, but I have not found anything that will give me what I need.
#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::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), 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 (fgets(buffer, sizeof buffer, pipe) != NULL) {
result += buffer;
}
} catch (...) {
pclose(pipe);
throw;
}
pclose(pipe);
return result;
}
Replace popen and pclose with _popen and _pclose for Windows.
Getting both stdout and stderr (and also writing to stdin, not shown here) is easy peasy with my pstreams header, which defines iostream classes that work like 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';
// if reading stdout stopped at EOF then reset the state:
if (proc.eof() && proc.fail())
proc.clear();
// read child's stderr
while (std::getline(proc.err(), line))
std::cout << "stderr: " << line << '\n';
}
For Windows, popen also works, but it opens up a console window - which quickly flashes over your UI application. If you want to be a professional, it's better to disable this "flashing" (especially if the end-user can cancel it).
So here is my own version for Windows:
(This code is partially recombined from ideas written in The Code Project and MSDN samples.)
#include <windows.h>
#include <atlstr.h>
//
// 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;
STARTUPINFOW si = {sizeof(STARTUPINFOW)};
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 (50 ms), 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
I'd use popen() (++waqas).
But sometimes you need reading and writing...
It seems like nobody does things the hard way any more.
(Assuming a Unix/Linux/Mac environment, or perhaps Windows with a POSIX compatibility layer...)
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())*/
}
You also might want to play around with select() and non-blocking reads.
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);
}
Two possible approaches:
I don't think popen() is part of the C++ standard (it's part of POSIX from memory), but it's available on every UNIX I've worked with (and you seem to be targeting UNIX since your command is ./some_command).
On the off-chance that there is no popen(), you can use system("./some_command >/tmp/some_command.out");, then use the normal I/O functions to process the output file.
The following might be a portable solution. It follows standards.
#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::ios::binary );
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;
}
I couldn't figure out why popen/pclose is missing from Code::Blocks/MinGW. So I worked around the problem by using CreateProcess() and CreatePipe() instead.
Here's the solution that worked for me:
//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());
ListStdErr += 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;
}
Take note that you can get output by redirecting output to the file and then reading it
It was shown in documentation of std::system
You can receive exit code by calling WEXITSTATUS macro.
int status = std::system("ls -l >test.txt"); // execute the UNIX command "ls -l >test.txt"
std::cout << std::ifstream("test.txt").rdbuf();
std::cout << "Exit code: " << WEXITSTATUS(status) << std::endl;
Assuming POSIX, simple code to capture stdout:
#include <sys/wait.h>
#include <unistd.h>
#include <string>
#include <vector>
std::string qx(const std::vector<std::string>& args) {
int stdout_fds[2];
pipe(stdout_fds);
int stderr_fds[2];
pipe(stderr_fds);
const pid_t pid = fork();
if (!pid) {
close(stdout_fds[0]);
dup2(stdout_fds[1], 1);
close(stdout_fds[1]);
close(stderr_fds[0]);
dup2(stderr_fds[1], 2);
close(stderr_fds[1]);
std::vector<char*> vc(args.size() + 1, 0);
for (size_t i = 0; i < args.size(); ++i) {
vc[i] = const_cast<char*>(args[i].c_str());
}
execvp(vc[0], &vc[0]);
exit(0);
}
close(stdout_fds[1]);
std::string out;
const int buf_size = 4096;
char buffer[buf_size];
do {
const ssize_t r = read(stdout_fds[0], buffer, buf_size);
if (r > 0) {
out.append(buffer, r);
}
} while (errno == EAGAIN || errno == EINTR);
close(stdout_fds[0]);
close(stderr_fds[1]);
close(stderr_fds[0]);
int r, status;
do {
r = waitpid(pid, &status, 0);
} while (r == -1 && errno == EINTR);
return out;
}
Code contributions are welcome for more functionality:
https://github.com/ericcurtin/execxx
You can get the output after running a script using a pipe. We use pipes when we want the output of the child process.
int my_func() {
char ch;
FILE *fpipe;
FILE *copy_fp;
FILE *tmp;
char *command = (char *)"/usr/bin/my_script my_arg";
copy_fp = fopen("/tmp/output_file_path", "w");
fpipe = (FILE *)popen(command, "r");
if (fpipe) {
while ((ch = fgetc(fpipe)) != EOF) {
fputc(ch, copy_fp);
}
}
else {
if (copy_fp) {
fprintf(copy_fp, "Sorry there was an error opening the file");
}
}
pclose(fpipe);
fclose(copy_fp);
return 0;
}
So here is the script, which you want to run. Put it in a command variable with the arguments your script takes (nothing if no arguments). And the file where you want to capture the output of the script, put it in copy_fp.
So the popen runs your script and puts the output in fpipe and then you can just copy everything from that to your output file.
In this way you can capture the outputs of child processes.
And another process is you can directly put the > operator in the command only. So if we will put everything in a file while we run the command, you won't have to copy anything.
In that case, there isn't any need to use pipes. You can use just system, and it will run the command and put the output in that file.
int my_func(){
char *command = (char *)"/usr/bin/my_script my_arg > /tmp/my_putput_file";
system(command);
printf("everything saved in my_output_file");
return 0;
}
You can read YoLinux Tutorial: Fork, Exec and Process control for more information.
Command class uses system("cmd > stdout 2> stderr") to provide user with stdout and stderr, in addition to the exit code.
Test run:
./a.out 'ls .'
exit code: 0
stdout: HelloWorld
HelloWorld.c
HelloWorld.cpp
HelloWorld.dSYM
a.out
gcc_container.bash
linuxsys
macsys
test.sh
stderr:
#include <iostream>
#include <fstream>
#include <sstream>
#include <unistd.h>
using namespace std;
class Command {
public:
Command() {
exit_code_ = -1;
}
int GetExitCode() { return exit_code_;}
string GetStdOutStr() {return stdout_str_;}
string GetStdErrStr() {return stderr_str_;}
int Run(const char* cmd) {
return Run(string(cmd));
}
/**
* #brief run a given command
*
* #param cmd: command string
* #return int: the exit code of running the command
*/
int Run(string cmd) {
// create temp files
char tmp_dir[] = "/tmp/stdir.XXXXXX";
mkdtemp(tmp_dir);
string stdout_file = string(tmp_dir) + "/stdout";
string stderr_file = string(tmp_dir) + "/stderr";
// execute the command "cmd > stdout_file 2> stderr_file"
string cli = cmd + " > " + stdout_file + " 2> " + stderr_file;
exit_code_ = system(cli.c_str());
exit_code_ = WEXITSTATUS(exit_code_);
stdout_str_ = File2Str(stdout_file);
stderr_str_ = File2Str(stderr_file);
// rid of the temp files
remove(stdout_file.c_str());
remove(stderr_file.c_str());
remove(tmp_dir);
return exit_code_;
}
private:
int exit_code_;
string stderr_str_;
string stdout_str_;
/**
* #brief read a file
*
* #param file_name: file path
* #return string the contents of the file.
*/
string File2Str(string file_name) {
ifstream file;
stringstream str_stream;
file.open(file_name);
if (file.is_open()) {
str_stream << file.rdbuf();
file.close();
}
return str_stream.str();
}
};
int main(int argc, const char* argv[]) {
Command command;
command.Run(argv[1]);
cout << "exit code: " << command.GetExitCode() << endl;
cout << "stdout: " << command.GetStdOutStr() << endl;
cout << "stderr: " << command.GetStdErrStr() << endl;
return command.GetExitCode();
}
C++ stream implemention of waqas's answer:
#include <istream>
#include <streambuf>
#include <cstdio>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <string>
class execbuf : public std::streambuf {
protected:
std::string output;
int_type underflow(int_type character) {
if (gptr() < egptr()) return traits_type::to_int_type(*gptr());
return traits_type::eof();
}
public:
execbuf(const char* command) {
std::array<char, 128> buffer;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
this->output += buffer.data();
}
setg((char*)this->output.data(), (char*)this->output.data(), (char*)(this->output.data() + this->output.size()));
}
};
class exec : public std::istream {
protected:
execbuf buffer;
public:
exec(char* command) : std::istream(nullptr), buffer(command, fd) {
this->rdbuf(&buffer);
}
};
This code catches all output through stdout . If you want to catch only stderr then pass your command like this:
sh -c '<your-command>' 2>&1 > /dev/null
If you want to catch both stdout and stderr then the command should be like this:
sh -c '<your-command>' 2>&1
I have a working code that returns all files in all sub-directories given a directory with a wild card. For example: "C://*". This works perfectly fine. Now, I was wondering if it is possible to recursively iterate over all files using a certain file extension. For example: "C://*.png", without altering the code. I was looking for sth like: "C://*/../*.png", but I could not find a solution. Are there any wildcard tricks I could use?
And here is an example using FindFirstFile and FindNextFile that can recursively find files having a given extension.
#include "stdafx.h"
#include <Windows.h>
#include <atlpath.h>
#include <list>
#include <iostream>
#ifdef _UNICODE
#define cout wcout
#endif
void FindFiles(
const CString& strRootPath,
const CString& strExt,
std::list<CString>& listFiles,
bool bRecursive = true)
{
CString strFileToFind = strRootPath;
ATLPath::Append(CStrBuf(strFileToFind, MAX_PATH), _T("*.*"));
WIN32_FIND_DATA findData = { 0 };
HANDLE hFileFind = ::FindFirstFile(strFileToFind, &findData);
if (INVALID_HANDLE_VALUE != hFileFind)
{
do
{
CString strFileName = findData.cFileName;
if ((strFileName == _T(".")) || (strFileName == _T("..")))
continue;
CString strFilePath = strRootPath;
ATLPath::Append(CStrBuf(strFilePath, MAX_PATH), strFileName);
if (bRecursive && (ATLPath::IsDirectory(strFilePath)))
{
FindFiles(strFilePath, strExt, listFiles);
}
else
{
CString strFoundExt = ATLPath::FindExtension(strFilePath);
if (! strExt.CompareNoCase(strFoundExt))
istFiles.push_back(strFilePath);
}
} while (::FindNextFile(hFileFind, &findData));
::FindClose(hFileFind);
}
}
int main()
{
std::list<CString> listFiles;
FindFiles(_T("e:\\tests"), _T(".cpp"), listFiles);
for (const auto& strFile : listFiles)
std::cout << strFile.GetString() << std::endl;
return 0;
}
Note: to make things easier, I've used ATL stuff like ATL::CString and ATLPath functions. There is no problem to use them in a Win32 or Console application.
You need to search recursively for each subdirectory. I happen to have some code to do this, the following code might help.
#include <functional>
#include <io.h>
enum enumflags {
ENUM_FILE = 1,
ENUM_DIR,
ENUM_BOTH
};
//return value:
// False means that the searching has been aborted by the callback function.
// It will return true otherwise.
bool enumsubfiles(
const std::wstring &dir_with_back_slant, //for example: L"C:\\", L"E:\\test\\"
const std::wstring &filename, //for example: L"123.txt", L"*.exe", L"123.???"
unsigned int maxdepth, //0 means not searching subdirectories, 1 means maximum depth of subdirectories is 1,
// pass -1 to search all the subdirectories.
enumflags flags, //search files, directories, or both.
std::function<bool(const std::wstring &dir_with_back_slant, _wfinddata_t &attrib)> callback
)
{
_wfinddata_t dat;
size_t hfile;
std::wstring fullname = dir_with_back_slant + filename;
std::wstring tmp;
bool ret = true;
hfile = _wfindfirst(fullname.c_str(), &dat);
if (hfile == -1) goto a;
do {
if (!(wcscmp(L".", dat.name) && wcscmp(L"..", dat.name))) continue;
if (((dat.attrib&_A_SUBDIR) && (!(flags&ENUM_DIR))) || ((!(dat.attrib&_A_SUBDIR)) && (!(flags&ENUM_FILE)))) continue;
ret = callback(dir_with_back_slant, dat);
if (!ret) {
_findclose(hfile);
return ret;
}
} while (_wfindnext(hfile, &dat) == 0);
_findclose(hfile);
a:
if (!maxdepth) return ret;
tmp = dir_with_back_slant + L"*";
hfile = _wfindfirst(tmp.c_str(), &dat);
if (hfile == -1) return ret;
do {
if (!(wcscmp(L".", dat.name) && wcscmp(L"..", dat.name))) continue;
if (!(dat.attrib&_A_SUBDIR)) continue;
tmp = dir_with_back_slant + dat.name + L"\\";
ret = enumsubfiles(tmp, filename, maxdepth - 1, flags, callback);
if (!ret) {
_findclose(hfile);
return ret;
}
} while (_wfindnext(hfile, &dat) == 0);
_findclose(hfile);
return ret;
}
Here is an example of the usage of the function above:
int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;
//the default code page of my console window is 936
setlocale(CP_ACP, ".936");
enumsubfiles(L"C:\\", L"*.exe", 1, ENUM_FILE, [](const std::wstring &dir_with_back_slant, _wfinddata_t &attrib)->bool
{
std::wcout << dir_with_back_slant << attrib.name << '\n';
return true; //return true to continue, return false to abort searching.
});
return 0;
}
And you will get the following output:
C:\OpenSSL-Win64\unins000.exe
C:\putty\PAGEANT.EXE
C:\putty\PLINK.EXE
C:\putty\PSCP.EXE
C:\putty\PSFTP.EXE
C:\putty\PUTTY.EXE
C:\putty\PUTTYGEN.EXE
C:\Windows\ampa.exe
C:\Windows\bfsvc.exe
C:\Windows\explorer.exe
C:\Windows\HelpPane.exe
C:\Windows\hh.exe
C:\Windows\notepad.exe
C:\Windows\regedit.exe
C:\Windows\RtCRU64.exe
C:\Windows\SECOH-QAD.exe
C:\Windows\splwow64.exe
C:\Windows\winhlp32.exe
C:\Windows\write.exe
C:\测试\测试.exe
The maxdepth I passed to the function is 1. Pass -1 to search all the subdirectories.
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I am trying to list all the files in a directory. Problem is that it keep on skipping some of the files which are under directory ".NET". Basically it skips any files under a directory name which start with a "." What might be the possible reason?
#include "stdafx.h"
#include <windows.h>
#include <string>
#include <iostream>
#include <list>
#include <fstream>
using namespace std;
void addFileToList(list<string>& fileList,const string& directory,const string& excludeFilter,const WIN32_FIND_DATA &FindFileData)
{
string fileName = string(FindFileData.cFileName);
string filePath = directory + fileName;
if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
{
string fileExtension = fileName.substr(fileName.find_last_of(".") + 1);
if (fileExtension.empty())
{
fileList.push_back(filePath);
return;
}
else if ( ( excludeFilter.find(fileExtension) == string::npos ) )
{
fileList.push_back(filePath);
}
}
}
void GetFileListing(list<string>& fileList, string directory,const string& excludeFilter,bool recursively=true)
{
string filePath,fileName,fileExtension;
if (recursively)
{
GetFileListing(fileList, directory, excludeFilter,false);
}
WIN32_FIND_DATA FindFileData;
HANDLE hFind = INVALID_HANDLE_VALUE;
directory += "\\";
string filter = directory + "*";
// Find the first file in the directory.
hFind = FindFirstFile(filter.c_str(), &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
DWORD dwError = GetLastError();
if (dwError!=ERROR_FILE_NOT_FOUND)
{
//cout << "Invalid file handle for filter "<<filter<<". Error is " << GetLastError() << endl;
}
}
else
{
// Add the first file found to the list
if (!recursively)
{
addFileToList(fileList,directory,excludeFilter,FindFileData);
}
// List all the other files in the directory.
while (FindNextFile(hFind, &FindFileData) != 0)
{
if (!recursively)
{
addFileToList(fileList,directory,excludeFilter,FindFileData);
}
else
{
// If we found a directory then recurse into it
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && FindFileData.cFileName[0]!='.')
{
GetFileListing(fileList, directory + string(FindFileData.cFileName),excludeFilter);
}
}
}
DWORD dwError = GetLastError();
FindClose(hFind);
if (dwError != ERROR_NO_MORE_FILES)
{
//cout << "FindNextFile error. Error is "<< dwError << endl;
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
list<string> fileList;
list<string>::iterator it;
string excludeFilter;
string directory=".";
GetFileListing(fileList, directory,excludeFilter);
for( it=fileList.begin(); it!=fileList.end();++it)
{
cout<<(*it)<<endl;
}
return 0;
}
Updated the code:
Based on Hans suggestion, I added a check for the directory. Now it works! Any suggestions?
#include "stdafx.h"
#include <windows.h>
#include <string>
#include <iostream>
#include <list>
#include <fstream>
using namespace std;
void addFileToList( list<string>& fileList,const string& directory,const string& excludeFilter,const string &fileName )
{
string filePath = directory + fileName;
string fileExtension = fileName.substr(fileName.find_last_of(".") + 1);
if (fileExtension.empty())
{
fileList.push_back(filePath);
return;
}
else if ( ( excludeFilter.find(fileExtension) == string::npos ) )
{
fileList.push_back(filePath);
}
}
void GetFileListing(list<string>& fileList, string directory,const string& excludeFilter,bool recursively=true)
{
// If we are going to recurse over all the subdirectories, first of all
// get all the files that are in this directory that match the filter
string filePath,fileName,fileExtension;
if (recursively)
{
GetFileListing(fileList, directory, excludeFilter,false);
}
WIN32_FIND_DATA FindFileData;
HANDLE hFind = INVALID_HANDLE_VALUE;
directory += "\\";
string filter = directory + "*";
// Find the first file in the directory.
hFind = FindFirstFile(filter.c_str(), &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
DWORD dwError = GetLastError();
if (dwError!=ERROR_FILE_NOT_FOUND)
{
//cout << "Invalid file handle for filter "<<filter<<". Error is " << GetLastError() << endl;
}
}
else
{
// Add the first file found to the list
if (!recursively)
{
if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
{
string fileName = string(FindFileData.cFileName);
addFileToList(fileList,directory,excludeFilter,fileName);
}
}
if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// List all the other files in the directory.
while (FindNextFile(hFind, &FindFileData) != 0)
{
if (!recursively)
{
if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
{
string fileName = string(FindFileData.cFileName);
addFileToList(fileList,directory,excludeFilter,fileName);
}
else
{
if ( string(FindFileData.cFileName)=="." || string(FindFileData.cFileName)==".." ) continue;
GetFileListing(fileList, directory + string(FindFileData.cFileName),excludeFilter);
}
}
}
}
DWORD dwError = GetLastError();
FindClose(hFind);
if (dwError != ERROR_NO_MORE_FILES)
{
//cout << "FindNextFile error. Error is "<< dwError << endl;
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
list<string> fileList;
list<string>::iterator it;
string excludeFilter;
string directory=".";
GetFileListing(fileList, directory,excludeFilter);
for( it=fileList.begin(); it!=fileList.end();++it)
{
cout<<(*it)<<endl;
}
return 0;
}
Try this instead:
#include "stdafx.h"
#include <windows.h>
#include <string>
#include <iostream>
#include <list>
#include <fstream>
using namespace std;
void addFileToList(list<string>& fileList, const string& directory, const string& fileName, const string& excludeFilter)
{
string fileExtension = fileName.substr(fileName.find_last_of(".") + 1);
if (!fileExtension.empty())
{
if (excludeFilter.find(fileExtension) != string::npos)
return;
}
fileList.push_back(directory + fileName);
}
void GetFileListing(list<string>& fileList, string directory, const string& excludeFilter, bool recursively = true)
{
string filePath;
directory += "\\";
string filter = directory + "*";
WIN32_FIND_DATA FindFileData;
HANDLE hFind = FindFirstFile(filter.c_str(), &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
DWORD dwError = GetLastError();
if (dwError != ERROR_FILE_NOT_FOUND)
{
//cout << "Invalid file handle for filter " << filter << ". Error is " << GetLastError() << endl;
}
return;
}
do
{
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if ((recursively) && (lstrcmp(FindFileData.cFileName, TEXT(".")) != 0) && (lstrcmp(FindFileData.cFileName, TEXT("..")) != 0))
GetFileListing(fileList, directory + FindFileData.cFileName, excludeFilter);
}
else
addFileToList(fileList, directory, FindFileData.cFileName, excludeFilter);
}
while (FindNextFile(hFind, &FindFileData) != 0);
DWORD dwError = GetLastError();
FindClose(hFind);
if (dwError != ERROR_NO_MORE_FILES)
{
//cout << "FindNextFile error. Error is "<< dwError << endl;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
list<string> fileList;
GetFileListing(fileList, ".", "");
for(list<string>::iterator it = fileList.begin(); it != fileList.end(); ++it)
{
cout << (*it) << endl;
}
return 0;
}
I am having a problem with my code. Whenever i run the program, the last file search would return a zero value where in it shouldn't.
here is the code:
#include <vector>
#include <string>
#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <conio.h>
using namespace std;
string GetPath(string path){
char directory[1024];
vector <string> filelist;
strcpy_s(directory,path.c_str());
BOOL checker;
WIN32_FIND_DATA findFileData;
HANDLE hFind = FindFirstFile((LPCSTR)directory, &findFileData);
cout <<"Files in:" <<"\n"<<directory<<"\n"<<endl;
checker = FindNextFile(hFind, &findFileData);
while(checker)
{
checker = FindNextFile(hFind, &findFileData);
filelist.push_back(findFileData.cFileName);//save file list in vector
cout << findFileData.cFileName << endl;
}
DWORD error = GetLastError();
if( error != ERROR_NO_MORE_FILES)
{
}
/*for (unsigned i=1;i<filelist.size();i++){
cout << filelist[i]<<endl;//print out the vector
}*/
return 0;
}
int main()
{
string path;
path="C:\\Program Files\\*.*";
vector <string> handler;
path = GetPath(path);
}
Try this as a sanity check:
#include <tchar.h>
#include <stdexcept>
#include <vector>
#include <string>
#include <iostream>
#include <windows.h>
#ifdef UNICODE
typedef std::wstring tstring;
static std::wostream& tcout = std::wcout;
#else
typedef std::string tstring;
static std::ostream& tcout = std::cout;
#endif
std::vector<tstring> GetPath(tstring const& path)
{
WIN32_FIND_DATA findFileData;
HANDLE const hFind = FindFirstFile(path.c_str(), &findFileData);
if (hFind == INVALID_HANDLE_VALUE)
throw std::runtime_error(""); // realistically, throw something useful
std::vector<tstring> filelist;
do filelist.push_back(findFileData.cFileName);
while (FindNextFile(hFind, &findFileData));
FindClose(hFind);
if (GetLastError() != ERROR_NO_MORE_FILES)
throw std::runtime_error(""); // realistically, throw something useful
return filelist;
}
int _tmain()
{
std::vector<tstring> files = GetPath(_T("C:\\Program Files\\*.*"));
for (std::vector<tstring>::const_iterator iter = files.begin(), iter_end = files.end(); iter != iter_end; ++iter)
tcout << *iter << _T('\n');
}
If it works, clean up the error handling logic and use it ;-]. If it doesn't, then #young was correct and you you have localization issues; change your project settings to use Unicode as the character set and it should work.
First, you need to check hFind returned from FindFirstFile() as it may return INVALID_HANDLE_VALUE.
Second, your while loop should look like this
while(FindNextFile(hFind, &findFileData)!= 0)
{
filelist.push_back(findFileData.cFileName);//save file list in vector
}
DWORD error = GetLastError();
if( error != ERROR_NO_MORE_FILES)
{
// You are not expecting this error so you might want to do something here.
}
It seems to me that you shouldn't be calling FindNextFile inside the loop: I imagine that if you look at you output carefully you will notice that every SECOND file is missing from your list.
This is because the call to FindNextFile in the while condition loads the next file detail into FindFileData. Then the next line of code (inside the while loop) does this process again overwriting the first match with the second. Thus you only get every second entry.
For even numbers of files you also get the difficulty you describe at the end.
Well one thing I notice is that your GetPath function returns a string. At the end you do: return 0; This will call the string constructor that takes a char* as parameter and will attempt to construct the string with a null pointer. I assume this will lead to a crash in most STl implementations :-).
Coming to your problem, you could try this:
#include <Strsafe.h>
//from MSDN
void
DisplayError(LPTSTR lpszFunction)
{
// Retrieve the system error message for the last-error code
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL );
// Display the error message and exit the process
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
void
GetPath(string path)
{
vector <string> filelist;
WIN32_FIND_DATA findFileData;
HANDLE hFind = FindFirstFile(path.c_str(), &findFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
DisplayError("FindFirstFile");
return;
}
cout <<"Enumerating files in: " <<"\n"<< path << "\n" << endl;
while( FindNextFile(hFind, &findFileData) != 0 )
{
filelist.push_back( findFileData.cFileName );//save file list in vector
}
if ( ERROR_NO_MORE_FILES != GetLastError() )
{
DisplayError("FindFirstFile");
}
FindClose(hFind);
unsigned int listSize = filelist.size();
cout << "List Count: " << listSize << "\n";
for (unsigned i=0; i < listSize; ++i)
{
cout << filelist[i] << "\n";//print out the vector
}
cout << "\nListing complete\n" << endl;
}
int main()
{
string path;
path="C:\\Program Files\\*.*";
GetPath(path);
}
And tell us what error code do you receive after the while loop completes.
This is my answer
#include <vector>
#include <string>
#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <conio.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <time.h>
#include <exception>
#include <WinBase.h>
#include <tchar.h>
#include <strsafe.h>
#include <algorithm>
using namespace std;
int ifException(string directory) throw()
{
DWORD returnvalue;
returnvalue = GetFileAttributes(directory.c_str());
if(returnvalue == ((DWORD)-1))
{
return 0;
}
else
{
return 1;
}
}
string FileTime(string filename, string path){
char timeStr[ 100 ] = "";
char fpath[9999];
string buffer;
replace (path.begin(),path.end(),'*','\0');
replace (path.begin(),path.end(),'.','\0');
strcpy_s(fpath,path.c_str());
path = filename;
struct stat buf;
string filepath;
filepath = fpath;
filepath += filename;
//cout << filepath << endl;
strcpy_s(fpath,filepath.c_str());
if (!stat(fpath, &buf))
{
strftime(timeStr, 100, "%d-%m-%Y %H:%M:%S", localtime(&buf.st_mtime));
}
buffer = filename;
buffer += "\t\t";
buffer +=timeStr;
return buffer;
}
vector <string> GetPath(string path)
{
char directory[9999];
vector <string> filelist;
string buffer;
strcpy_s(directory,path.c_str());
BOOL checker;
WIN32_FIND_DATA findFileData;
HANDLE hFind = FindFirstFile((LPCSTR)directory, &findFileData);
try
{
ifException(directory);
}catch(int i)
{
if (i==0)
{
_getch();
exit(1);
}
}
buffer = findFileData.cFileName;
filelist.push_back(buffer);
checker = FindNextFile(hFind, &findFileData);
while(checker)
{
checker = FindNextFile(hFind, &findFileData);
buffer = findFileData.cFileName;
buffer = FileTime(buffer,path);
filelist.push_back(buffer);//save file list in vector
if(checker == 0)
{
filelist.resize(filelist.size());
return filelist;
}
}
return filelist;
}
int main()
{
string path;
path="C:\\Documents and Settings\\OJT\\My Documents\\*.*";// the directory
vector <string> handler;
handler = GetPath(path);
for (unsigned i=1;i<handler.size()-1;i++)
{
cout << handler[i]<<endl;//print out the vector
}
_getch();
}
I am looking for a way to get the output of a command when it is run from within a C++ program. I have looked at using the system() function, but that will just execute a command. Here's an example of what I'm looking for:
std::string result = system("./some_command");
I need to run an arbitrary command and get its output. I've looked at boost.org, but I have not found anything that will give me what I need.
#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::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), 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 (fgets(buffer, sizeof buffer, pipe) != NULL) {
result += buffer;
}
} catch (...) {
pclose(pipe);
throw;
}
pclose(pipe);
return result;
}
Replace popen and pclose with _popen and _pclose for Windows.
Getting both stdout and stderr (and also writing to stdin, not shown here) is easy peasy with my pstreams header, which defines iostream classes that work like 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';
// if reading stdout stopped at EOF then reset the state:
if (proc.eof() && proc.fail())
proc.clear();
// read child's stderr
while (std::getline(proc.err(), line))
std::cout << "stderr: " << line << '\n';
}
For Windows, popen also works, but it opens up a console window - which quickly flashes over your UI application. If you want to be a professional, it's better to disable this "flashing" (especially if the end-user can cancel it).
So here is my own version for Windows:
(This code is partially recombined from ideas written in The Code Project and MSDN samples.)
#include <windows.h>
#include <atlstr.h>
//
// 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;
STARTUPINFOW si = {sizeof(STARTUPINFOW)};
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 (50 ms), 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
I'd use popen() (++waqas).
But sometimes you need reading and writing...
It seems like nobody does things the hard way any more.
(Assuming a Unix/Linux/Mac environment, or perhaps Windows with a POSIX compatibility layer...)
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())*/
}
You also might want to play around with select() and non-blocking reads.
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);
}
Two possible approaches:
I don't think popen() is part of the C++ standard (it's part of POSIX from memory), but it's available on every UNIX I've worked with (and you seem to be targeting UNIX since your command is ./some_command).
On the off-chance that there is no popen(), you can use system("./some_command >/tmp/some_command.out");, then use the normal I/O functions to process the output file.
The following might be a portable solution. It follows standards.
#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::ios::binary );
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;
}
I couldn't figure out why popen/pclose is missing from Code::Blocks/MinGW. So I worked around the problem by using CreateProcess() and CreatePipe() instead.
Here's the solution that worked for me:
//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());
ListStdErr += 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;
}
Take note that you can get output by redirecting output to the file and then reading it
It was shown in documentation of std::system
You can receive exit code by calling WEXITSTATUS macro.
int status = std::system("ls -l >test.txt"); // execute the UNIX command "ls -l >test.txt"
std::cout << std::ifstream("test.txt").rdbuf();
std::cout << "Exit code: " << WEXITSTATUS(status) << std::endl;
Assuming POSIX, simple code to capture stdout:
#include <sys/wait.h>
#include <unistd.h>
#include <string>
#include <vector>
std::string qx(const std::vector<std::string>& args) {
int stdout_fds[2];
pipe(stdout_fds);
int stderr_fds[2];
pipe(stderr_fds);
const pid_t pid = fork();
if (!pid) {
close(stdout_fds[0]);
dup2(stdout_fds[1], 1);
close(stdout_fds[1]);
close(stderr_fds[0]);
dup2(stderr_fds[1], 2);
close(stderr_fds[1]);
std::vector<char*> vc(args.size() + 1, 0);
for (size_t i = 0; i < args.size(); ++i) {
vc[i] = const_cast<char*>(args[i].c_str());
}
execvp(vc[0], &vc[0]);
exit(0);
}
close(stdout_fds[1]);
std::string out;
const int buf_size = 4096;
char buffer[buf_size];
do {
const ssize_t r = read(stdout_fds[0], buffer, buf_size);
if (r > 0) {
out.append(buffer, r);
}
} while (errno == EAGAIN || errno == EINTR);
close(stdout_fds[0]);
close(stderr_fds[1]);
close(stderr_fds[0]);
int r, status;
do {
r = waitpid(pid, &status, 0);
} while (r == -1 && errno == EINTR);
return out;
}
Code contributions are welcome for more functionality:
https://github.com/ericcurtin/execxx
You can get the output after running a script using a pipe. We use pipes when we want the output of the child process.
int my_func() {
char ch;
FILE *fpipe;
FILE *copy_fp;
FILE *tmp;
char *command = (char *)"/usr/bin/my_script my_arg";
copy_fp = fopen("/tmp/output_file_path", "w");
fpipe = (FILE *)popen(command, "r");
if (fpipe) {
while ((ch = fgetc(fpipe)) != EOF) {
fputc(ch, copy_fp);
}
}
else {
if (copy_fp) {
fprintf(copy_fp, "Sorry there was an error opening the file");
}
}
pclose(fpipe);
fclose(copy_fp);
return 0;
}
So here is the script, which you want to run. Put it in a command variable with the arguments your script takes (nothing if no arguments). And the file where you want to capture the output of the script, put it in copy_fp.
So the popen runs your script and puts the output in fpipe and then you can just copy everything from that to your output file.
In this way you can capture the outputs of child processes.
And another process is you can directly put the > operator in the command only. So if we will put everything in a file while we run the command, you won't have to copy anything.
In that case, there isn't any need to use pipes. You can use just system, and it will run the command and put the output in that file.
int my_func(){
char *command = (char *)"/usr/bin/my_script my_arg > /tmp/my_putput_file";
system(command);
printf("everything saved in my_output_file");
return 0;
}
You can read YoLinux Tutorial: Fork, Exec and Process control for more information.
Command class uses system("cmd > stdout 2> stderr") to provide user with stdout and stderr, in addition to the exit code.
Test run:
./a.out 'ls .'
exit code: 0
stdout: HelloWorld
HelloWorld.c
HelloWorld.cpp
HelloWorld.dSYM
a.out
gcc_container.bash
linuxsys
macsys
test.sh
stderr:
#include <iostream>
#include <fstream>
#include <sstream>
#include <unistd.h>
using namespace std;
class Command {
public:
Command() {
exit_code_ = -1;
}
int GetExitCode() { return exit_code_;}
string GetStdOutStr() {return stdout_str_;}
string GetStdErrStr() {return stderr_str_;}
int Run(const char* cmd) {
return Run(string(cmd));
}
/**
* #brief run a given command
*
* #param cmd: command string
* #return int: the exit code of running the command
*/
int Run(string cmd) {
// create temp files
char tmp_dir[] = "/tmp/stdir.XXXXXX";
mkdtemp(tmp_dir);
string stdout_file = string(tmp_dir) + "/stdout";
string stderr_file = string(tmp_dir) + "/stderr";
// execute the command "cmd > stdout_file 2> stderr_file"
string cli = cmd + " > " + stdout_file + " 2> " + stderr_file;
exit_code_ = system(cli.c_str());
exit_code_ = WEXITSTATUS(exit_code_);
stdout_str_ = File2Str(stdout_file);
stderr_str_ = File2Str(stderr_file);
// rid of the temp files
remove(stdout_file.c_str());
remove(stderr_file.c_str());
remove(tmp_dir);
return exit_code_;
}
private:
int exit_code_;
string stderr_str_;
string stdout_str_;
/**
* #brief read a file
*
* #param file_name: file path
* #return string the contents of the file.
*/
string File2Str(string file_name) {
ifstream file;
stringstream str_stream;
file.open(file_name);
if (file.is_open()) {
str_stream << file.rdbuf();
file.close();
}
return str_stream.str();
}
};
int main(int argc, const char* argv[]) {
Command command;
command.Run(argv[1]);
cout << "exit code: " << command.GetExitCode() << endl;
cout << "stdout: " << command.GetStdOutStr() << endl;
cout << "stderr: " << command.GetStdErrStr() << endl;
return command.GetExitCode();
}
C++ stream implemention of waqas's answer:
#include <istream>
#include <streambuf>
#include <cstdio>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <string>
class execbuf : public std::streambuf {
protected:
std::string output;
int_type underflow(int_type character) {
if (gptr() < egptr()) return traits_type::to_int_type(*gptr());
return traits_type::eof();
}
public:
execbuf(const char* command) {
std::array<char, 128> buffer;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
this->output += buffer.data();
}
setg((char*)this->output.data(), (char*)this->output.data(), (char*)(this->output.data() + this->output.size()));
}
};
class exec : public std::istream {
protected:
execbuf buffer;
public:
exec(char* command) : std::istream(nullptr), buffer(command, fd) {
this->rdbuf(&buffer);
}
};
This code catches all output through stdout . If you want to catch only stderr then pass your command like this:
sh -c '<your-command>' 2>&1 > /dev/null
If you want to catch both stdout and stderr then the command should be like this:
sh -c '<your-command>' 2>&1