I am developing an application, in which I want to redirect the output (progress information) of dd command to my C++ program, but it is not actually getting the output, here is the code
FILE * progressInfo = popen("gzip -dc backup/backup.img.gz | pv -ptrbe -i 2 -s 2339876653 | dd of=/dev/sdb","r");
if(!progressInfo)
{
return -1;
}
char buf[1024];
while(fgets(buff, sizeof(buff),progressInfo)!=NULL)
{
std::cout << buff << endl;
}
but the problem is the progress information is not received in buff, and the output is continuously printed on terminal, and above program halts on while(fgets(buff, sizeof(buff),progressInfo)!=NULL), and as soon as the dd operation is completed, the very next line to loop block is executed.
if anyone has any idea why the output is not returned to buff, and its continuously retuned on terminal?
The output is probably being written to standard error rather than standard output. Just add " 2>&1" to the very end of your command string and you should see the output (note the leading space).
Related
I attach existing console form C++ program. Output works fine, but when I try to put some input in console, terminal originally running there try to handle it.
Program code is here:
#include <iostream>
#include "windows.h"
int main(int argc, char **argv)
{
pid_t pid = atoi(argv[1]);
BOOL fret = FreeConsole();
if (!fret)
{
std::cerr << "ERR" << GetLastError() << std::endl;
}
BOOL aret = AttachConsole(pid);
if (!aret)
{
std::cerr << "ERR" << GetLastError() << std::endl;
}
SetConsoleTitleA("TTTTTTTT");
for (int ii = 0; ii < 10; ii++)
{
std::cout << "ii " << ii << std::endl;
}
int kk;
std::cin >> kk;
std::cerr << "kk " << kk << std::endl;
return 0;
}
When I run cmd, find out PID, run my program and then write for example 12 and enter, this is result:
C:\>ii 0
ii 1
ii 2
ii 3
ii 4
ii 5
ii 6
ii 7
ii 8
ii 9
12
'12' is not recognized as an internal or external command,
operable program or batch file.
When I try once again write 12, it works better (but there is still C:>)
C:\>12
kk 12
C:\>
Edit: I need redirect stdin, stdout, and stderr to console, I can run something else then cmd or send some command to cmd.
"is not recognized as an internal or external command,
operable program or batch file." is a standard cmd.exe error message. cmd.exe is already waiting for input when you do AttachConsole so the first input is sent to cmd.exe and that is why you get that error message. Replace the first number input with a command like dir and you will see that this input is received by cmd.exe.
The second time it works but mostly by accident. cmd.exe is probably busy with other things so your application gets the input before cmd.exe is able to "listen" to stdin again. cmd.exe prints the prompt (the "path" + >) because that's what it does after each command has been performed.
cmd.exe does not expect to share the console with anyone else and its normal mode of operation is to:
Read and parse a line from stdin.
Execute. Wait for the operation to complete if the command is an internal command or a external console application. It does not wait for GUI applications.
Print %PROMPT%.
Goto 1.
Even if you could convince cmd.exe to coexist with you, what you are doing is probably undefined behavior because you are mixing low-level native Windows console functions (AttachConsole) with high-level C++ I/O functions (std::cout & std::cin). Assuming you are using the Microsoft CRT you could use _open_osfhandle + _dup2 to correctly assign the native console handle but you would still have issues with cmd.exe and there is no way to work around it.
Today I have been facing a critical bug in my program, it is just a tiny code that uses popen() to pass the commands and pipe its results to a file descriptor where I use fgets() to read the result, The problem for date command my program is been hanging(waiting) and the reason is as we know after issuing date it will show the current date and issues a yes or no question like the below.
The current date is: Fri 08/21/2015
Enter the new date: (mm-dd-yy)----this was shitting my code!!!!
I just want my program to skip this questionnaire.
Note: the string "command" in the below code is what I will receive from the user.
FILE *in;
char buff[512];
string cmd;
command += " 2>&1";
if (!(in = popen(command.c_str(), "r"))) {
status = "0"; // my logic don't bother:)
}
else {
while (fgets(buff, sizeof(buff), in) != NULL) {
cmd += buff;
}
}
Thanks to Petesh.
I have changed to 2>&1<nul instead of 2>&1, the program isn't hanging now,
The key to this solution is adding <nul gives some default value as an answer to the prompts.
I want to extract the framesize of a video from a file. For this purpose, I have launched an ffmpeg command via bash shell, and I want to extract the output. This command is working well in the bash shell, and returns the output as wanted.
ffprobe -v error -count_frames -of flat=s=_ -select_streams v:0 -show_entries stream=nb_read_frames /home/peter/DA/videos/IMG-2014-1-10-10-4-37.avi
I want to call it via C++ and read out the result. I use the IDE Qt 4.8.6 with GCC 4.8 compiler.
For my code, I use this template:
executing shell command with popen
and changed it for my demands to
#include <iostream>
#include <string>
#include <stdio.h>
using namespace std;
int main()
{
FILE* pipe = popen("echo $(ffprobe -v error -count_frames -of flat=s=_ -select_streams v:0 -show_entries stream=nb_read_frames /home/peter/DA/videos/IMG-2014-1-10-10-4-37.avi)", "r");
if(!pipe)
{
cout << "error" << endl;
return 1;
}
char* buffer = new char[512];
string result;
fgets(buffer, sizeof(buffer), pipe) ;
while(!feof(pipe))
{
if(fgets(buffer, sizeof(buffer), pipe) != NULL)
{
cout << buffer << endl;
result += buffer;
}
}
pclose(pipe);
cout << result<< endl;
return 0;
}
The Qt console returned me this warning, and it is rending with return 0:
/home/peter/DA/videos/IMG-2014-1-10-10-4-37.avi: Invalid data found when processing input
and "pipe" is empty.
When I compile the main.cpp file above with g++ in the shell it works nice too.
Old post, but as I see, there are two points here:
Error "Invalid data found when processing input"
That's an ffprobe normal file processing error. Usually it happens when there are errors inside media file, it is not related to c++ program.
ffprobe writes warning/error messages into stderr stream, but popen only captures stdout stream, that's why your program couldn't get that error message trough the pipe.
How get the stdout+stderr in my program
popen allows execute any shell command, so we can use it to redirect stderr into stdout, so your program can get that output too, like this:
FILE *pipe = popen("ffprobe ... 2>&1");
The 2> redirect handle#2 output into current &1 handle#1 output (#1=stdout, #2=stderr).
There's absolute no need to execute FILE *pipe = popen("echo $(ffprobe ...)");, because the final result will be the same: Note that $(...) returns a string with stdout command output, and echo prints it. Totally redundant.
A few observations in order to improve your code:
When a string is too big to be displayed in one screen width, it's better split it into multiple lines (maybe grouping text inside each line within some logic), because that will improve the reading of your code by other people (and eventually by yourself in a few months).
You can do this with a C/C++ compiler feature that concatenates strings separated by spaces (newlines, tab, etc.), ex. "hi " "world" is the same as "hi world" to the compiler.
When your program have to write error messages, use the stderr stream. In c++ that's std::cerr instead std::cout.
Always free memory allocated when it's no loger used (each new has to have a delete)
Avoid use using namespace std;, instead use using std::name; for each standard instance/class that you'll use. Ex. using std::string;, that avoids future problems, specially in big programs. An example of a common error is here. In general avoid using using namespace xxxx;.
Reorganizing your code, we have:
#include <iostream>
#include <stdio.h>
using std::string;
using std::cout;
using std::cerr;
using std::endl;
int main() {
static char ffprobeCmd[] =
"ffprobe " // command
"-v error " // args
"-count_frames "
"-of flat=s=_ "
"-select_streams v:0 "
"-show_entries stream=nb_read_frames "
"/home/peter/DA/videos/IMG-2014-1-10-10-4-37.avi" // file
" 2>&1"; // Send stderr to stdout
FILE *pipe = popen(ffprobeCmd, "r");
if (!pipe) {
perror("Cannot open pipe.");
return 1;
}
char* buffer = new char[512];
string result;
while ((fgets(buffer, sizeof(buffer), pipe)) != NULL) {
result += buffer;
}
// See Note below
int retCode = pclose(pipe);
if (retCode != 0) {
// Program ends with error, and result has the error message
cerr << "retCode: " << retCode << "\nMessage: " << result << endl;
return retCode;
} else {
// Program ends normally, prints: streams_stream_0_nb_read_frames="xxx"
cout << result << endl;
}
delete buffer; // free memory
return 0;
}
Note
pclose is not intended to return the executed program status code, but if you need this value, pclose does it in some c++ versions/systems, so check it. Anyway it will be zero only if everything was OK.
I'm writing a program that needs to be able to execute a shell script provided by the user. I've gotten it to execute a single shell command, but the scripts provided will be more complicated than that.
Googling got me as far as the following code snippet:
FILE *pipe;
char str[100];
// The python line here is just an example, this is *not* about executing
// this particular line.
pipe = popen("python -c \"print 5 * 6\" 2>&1", "r");
fgets(str, 100, pipe);
cout << "Output: " << str << endl;
pclose(pipe)
So that this point str has 30 in it. So far so good. But what if the command has carriage returns in it, as a shell script file would, something like the following:
pipe = popen("python -c \"print 5 * 6\"\nbc <<< 5 + 6 2>&1", "r");
With this my goal is that str eventually have 30\n11.
To put another way, assume I have a file with the following contents:
python -c "print 5 * 6"
bc <<< 5 + 6
The argument I'm sending to popen above is the string representation of that file. I want to, from within C++, send that string (or something similar) to bash and have it execute exactly as if I were in the shell and sourced it with . file.sh, but setting the str variable to what I would see in the shell if it were executed there, in this case, 30\n11.
Yes, I could write this to a file and work it that way, but that seems like it should be unnecessary.
I wouldn't think this was a new problem, so either I'm thinking about it in a completely wrong way or there's a library that I simply don't know about that already does this.
use bash -c.
#include <stdio.h>
int main()
{
FILE *pipe = popen("bash -c \"echo asdf\necho 1234\" ", "r");
char ch;
while ((ch = fgetc(pipe)) != EOF)
putchar(ch);
}
Output:
asdf
1234
(I've test on cygwin)
In my code, I use popen with cmd 'iostat -xk 2 2'. Generally the iostat cmd will output a report to stdout and sleep for interval 2 seconds, then output another report to stdout.
When I use fgets to read from popen pipe, weird things are: I only get the first report. fgets hangs there and I never get the second report 2 seconds later. I change the function fgets to fgetc but it didn't help.
Here is my C++ code:
string iostatCMD = string("iostat -xk 2 2 ");
FILE *iostatFile = popen(iostatCMD.c_str(), "r");
int fd = fileno(iostatFile);
string readall;
while(!feof(iostatFile))
{
char buffer[128];
if (fgets(buffer, 128, iostatFile) != NULL)
{
cout <<buffer;
readall += buffer;
}
}
cout <<"readall = " <<readall <<endl;
I give it a try with Python. It works very well with popen, I get two reports by iostat and no hangs:
subprocess.Popen('iostat -xk 2 2', shell=True, stdout=subprocess.PIPE).communicate()[0];
So my question is how to read continuely all the output from popen pipe in C++?