Executing the shell command in QProcess.Piping the input - c++

I am trying to pipe the commands and execute it, but I am not able to figure how to pipe it.
I am trying to copy multiple files at once using the shell command
for %I in (source) do copy %I (destination)
QString files = "for %I in (source) do copy %I (destination)"
QProcess copy ;
copy.start(files);
I have to implement the piping to do that.
for Eg.
QProcess sh;
sh.start("sh", QStringList() << "-c" << "ifconfig | grep inet");
sh.waitForFinished();
QByteArray output = sh.readAll();
sh.close();
How can I implement piping for my copy process?

Try this example:
QProcess sh;
sh.start( "sh", { "-c", "ifconfig | grep inet" } );
if ( !sh.waitForFinished( -1 ) )
{
qDebug() << "Error:" << sh.readAllStandardError();
return -1;
}
const auto output = sh.readAllStandardOutput();
// ...
waitForFinished() should be called in blocking mode and it must be checked if it was successful or not.

Related

how to implement pipe command from linux shell in c++?

i am working in a project to implement a mini linux shell ,
i want to implement a pipe command where it basically works like this :
command1 | command2: using the pipe character “|” will produce a pipe, redirects command1 stdout to its write channel and command2 stdin to its read channel.
or:
command1 |& command2: using the pipe character “|&” will produce a pipe, redirects command1 stderr to the pipe’s write channel and command2 stdin to the pipe’s read channel.
now command 1 can be either an external command from linux that i run using execv or a built in command that i wrote , and command2 is always an external command
my code is not working correctly and i don't know where is the problem exactly , because i implemented many commands and they all worked perfect for example (cp, redirection ... ) , so the base is good in my code , but the pipe is just wrong ! for example if the command is : showpid | ./parser.exe 1
where parser.exe is a giving file that does parsing on the command , for example here if showpid prints : shell process pid is 12311 , then calling this command showpid | ./parser.exe 1 the output should be "shell" , but in my code the output is shell process pid is 12311
this is my pipe command implementation :
this is the class of the pipe command :
class PipeCommand : public Command {
private:
int pipeNum;
int split;
string cmd1;
string cmd2;
public:
PipeCommand(const char* cmd_line);
virtual ~PipeCommand() {}
void execute() override;
};
// the pipe constructor , here i want to extract each command from the right and left side of the pipe from the cmd_line , which is the command line that i get
// fro example : " showpid | grep 1 "
PipeCommand::PipeCommand(const char* cmd_line):Command(cmd_line) {
pipeNum = -1;
isBackground = _isBackgroundComamnd(cmd_line);
string cmd1 = "", cmd2 = "";
int split = -1;
for (int i = 0; i < this->num_args; i++) {
if (strcmp(args[i], "|") == 0) {
split = i;
pipeNum = 1;
break;
}
if (strcmp(args[i], "|&") == 0) {
split = i;
pipeNum = 2;
break;
}
}
for (int i = 0; i < split; i++) {
cmd1 = cmd1 + args[i] + " ";
}
for (int i = split + 1; i < num_args; i++) {
cmd2 = cmd2 + args[i] + " ";
}
// the implementation of the pipe command
void PipeCommand::execute() {
int pipeFd[2];
int pid;
pipe(pipeFd);
pid = fork();
if (pid == 0) { // child process .
close(pipeFd[1]);
dup2(pipeFd[1], pipeNum);
if (isBuiltInCMD(args[0])) { // if the command is built in which means i wrote it i run it like this ( this works fine i checked it)
Command *newCmd = CreateBuiltInCommand(const_cast<char *>(cmd1.c_str()));
newCmd->execute();
exit(0);
} else { // if the command is external than use execv
const char **argv = new const char *[4];
argv[0] = "/bin/bash";
argv[1] = "-c";
argv[2] = cmd1.c_str();
argv[3] = nullptr;
execv(argv[0], const_cast<char **>(argv));
perror("execvp failed");
}
} else { // the parent process , basically runs the command2 , which it can be only an external command
pid = fork(); // we fork again in the parent process
if (pid == 0) { // the child process executes the secomd command using execv
dup2(pipeFd[0], STDIN_FILENO);
close(pipeFd[0]);
dup2(pipeFd[0], pipeNum);
// execute
const char **argv = new const char *[4];
argv[0] = "/bin/bash";
argv[1] = "-c";
argv[2] = cmd2.c_str();
argv[3] = nullptr;
execv(argv[0], const_cast<char **>(argv));
perror("execvp failed");
} else { // the parent process waits
waitpid(pid,NULL,0);
close(pipeFd[1]);
close(pipeFd[0]);
}
}
}
I think you should look at the order that you are closing / duping file descriptors. Specifically:
The first command needs to use existing stdin (fd 0). Don't close it.
But you should close existing stdout (fd 1) and THEN do the fd dup so it becomes 1.
The second command does it the other way.
I would test with a MUCH simpler example. Get the piping thing to work and THEN do the exec thing.
This is edited information added later.
In a C/C++ world, you have 3 standard files when the program starts:
FD 0 is stdin -- Used for input
FD 1 is stdout -- Used for normal output
FD 2 is stderr -- Used for error output
When you do this:
grep foo < file.txt | grep bar
What the shell does is:
-Does the pipe call to get the input and output files
-On the first grep for foo, close fd 0 (stdin) and open file.txt for input. It will land on 0, and thus is stdin to the grep command.
-Close stdout and assign it to the out part of the pipe
On the second grep:
-Close 1 (stdin)
-And move the pipe input portion to 1 so stdin is set.
Thus, in the end:
part 1 fd 0 (stdin) is the file
part 1 fd 1 (stdout) is the output portion of the pipe
part 2 fd 0 (stdin) is the input portion of the pipe

Is there a way to pipe IO?

I'm trying to pipe 2 IO object, i come from nodejs and we can do something like that:
const child_process = require('child_process')
const shell = child_process.spawn('/bin/sh')
shell.stdout.pipe(process.stdout)
shell.stdin.write('pwd\n')
shell.stdin.write('ls\n')
/* write all command i want */
and im looking for do the same thing in crystal
i know for the current example we can write
shell = Process.new("/bin/sh", input: Process::Redirect::Pipe, output: STDOUT, error: STDOUT)
shell.input << "ls\n"
shell.input << "pwd\n"
# all commands i want
but for some reason passing TCPSocket to Process.new input/output/error dont work very well (see here too if you have time Process and TCPSocket not close properly in crystal)
so im looking for an alternative way who will look like:
shell = Process.new("/bin/sh", input: Process::Redirect::Pipe, output: Process::Redirect::Pipe, Process::Redirect::Pipe)
shell.output.pipe(STDOUT) # not the crystal pipe but like the nodejs pipe
shell.input << "ls\n"
shell.input << "pwd\n"
# all commands i want
You can use IO.copy inside a coroutine:
shell = Process.new("/bin/sh", input: :pipe, output: :pipe, error: :pipe)
spawn { IO.copy shell.output, STDOUT }
spawn { IO.copy shell.error, STDERR }
shell.input << "ls /\n"
shell.input << "pwd\n"
shell.wait
https://carc.in/#/r/75z4

shell command inside the linux daemon

I have written the daemon in C/C++ in linux.
Now I want to get the out put of ls -l (list directory) command inside daemon and write output of command to the file.
I know how to write to the file from my daemon, but,
I don't know how to execute ls -l command and get the output in buffer.
Here is the code...
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log any failures here */
ofs << "set sid : fail";
ofs.close();
exit(EXIT_FAILURE);
}
ofs << "\nchdir :" << chdir(filePath) << "\n";
/* Change the current working directory */
if ((chdir(filePath)) < 0) {
/* Log any failures here */
ofs << "chdir : fail";
ofs.close();
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
while(1){
//here I want to execute the ls -l and get output of the command
}
You can use popen that executes a shell command and return the output as a pipe:
#include <stdio.h>
FILE* pipe = popen("ls -l", "r");
if (!pipe) return "ERROR";
You can also use system to execute any shell command:
#include <stdlib.h>
int system(const char *command);
To get the output of ls -l, forward it to a file ls -l >> myls.log than read that file.
system("ls -l >> myls.log");

Query with QProcess

I'm supposed to check whether the service is RUNNING. I've a problem with QProcess query execution, when it comes to executing the following query: SC QUERY "service name" | findstr RUNNING, though this works fine when executed directly in command line in Windows. The code snipet here as follows:
QProcess process;
process.setProcessChannelMode(QProcess::ForwardedChannels);
process.start("SC QUERY \"Service_name\" | findstr RUNNING", QIODevice::ReadWrite);
// Wait for it to start
if(!process.waitForStarted())
return 0;
QByteArray buffer;
while(process.waitForFinished())
buffer.append(process.readAll());
qDebug() << buffer.data();
Output is:
Can you help me?
It is because using these three lines will not give you the expected results:
QProcess process;
process.setProcessChannelMode(QProcess::ForwardedChannels);
process.start("SC QUERY \"Service_name\" | findstr RUNNING", QIODevice::ReadWrite);
Based on the official documentation, QProcess is supposed to work for pipe'd commands:
void QProcess::setStandardOutputProcess(QProcess * destination)
Pipes the standard output stream of this process to the destination process' standard input.
In other words, the command1 | command2 shell command command can be achieved in the following way:
QProcess process1;
QProcess process2;
process1.setStandardOutputProcess(&process2);
process1.start("SC QUERY \"Service_name\"");
process2.start("findstr RUNNING");
process2.setProcessChannelMode(QProcess::ForwardedChannels);
// Wait for it to start
if(!process1.waitForStarted())
return 0;
bool retval = false;
QByteArray buffer;
while ((retval = process2.waitForFinished()));
buffer.append(process2.readAll());
if (!retval) {
qDebug() << "Process 2 error:" << process2.errorString();
return 1;
}
qDebug() << "Buffer data" << buffer;

Stay in directory with popen

I want to make some C++ program and I'm using function popen here to send commands to command line in Unix. It works fine, but when I call cd directory, the directory doesn't change. I thing that it's same when I try to run cd directory in some script, after finishing script directory path change back. So, scripts I must run like . ./script.sh not ./sript.sh, but how to do that with popen function? I have tried to add ". " before first argument of popen, but running ". ls" makes error.
Code:
cout << "# Command from " << session->target().full() << ": " << message.body() << endl;
//cout << "Prisla zprava" << endl;
//m_session->send( "Hello World", "No Subject" );
//system( message.body().c_str() );
//if ( message.body() == "" )
FILE* outp;
char buffer[100];
string outps = "";
outp = popen( message.body().c_str(), "r" );
while ( !feof(outp) )
{
fgets( buffer, 100, outp );
outps = outps + buffer;
}
pclose(outp);
cout << "& Output from command: " << outps << endl;
m_session->send( outps.c_str(), "Output" );
In message.body(); is string which I want to run (I'm receiving this from XMPP). When the string is for example "ls", it returns string with list of files in actual directory. But when the message is "cd directory", nothing happens, like trying to change directory in scripts.
Typically, the way the popen() command executes the command is via the shell. So, it opens a pipe, and forks. The child does some plumbing (connecting the pipe to the standard input or standard output - depending on the flag) and then executes
execl("/bin/sh", "/bin/sh", "-c", "what you said", (char *)0);
So, how it all behaves is going to depend on your key environment variables - notably PATH.
If you want to execute a script in the current directory, then one of these options:
outp = popen("./script.sh", "r");
outp = popen("sh -x ./script.sh", "r");
outp = popen("sh -c './script.sh arg1 arg2'", "r");
If you want to execute the 'ls' command:
outp = popen("/bin/ls /the/directory", "r");
And if you want to change directory before running something:
outp = popen("cd /somewhere/else; ./script", "r");
And so on...
If you want to change the directory of the program that is using popen(), then you need to use the 'chdir()' system call (or possibly fchdir()). If you think you might want to get back to where you started, use:
int fd = open(".", O_RDONLY);
chdir("/some/where/else");
...do stuff in new directory
fchdir(fd);
(Clearly, you need some error checking in that lot.)
It seems you have a bit of code that you do not understand. You are reading from outp, a pipe. Naming an input pipe outp is rather confusing. You then take the string you've read and pass it to m_session->send().
Nowhere in this whole process are you interacting with the Unix command line. In particular, popen() is not.