How to create if statement from curl command output (c++) - c++

I am trying to get the output of the curl command to work inside of an if statement
I am new to C++ and don't know how I could do this.
int curlreq;
curlreq = system("curl localhost/file.txt");
string curlreqstring = to_string(curlreq);
if ((krxcrlstr.find("hello") != string::npos) ) {
cout << "hello\n";
}
else if (curlreqstring.find("hello2") != string::npos) {
cout << "hello2\n";
}
I am doing this on Windows. The project is a console app C++ 20
All the above code is doing, is printing what the curl response is, but I need that as a variable to then determine what the program should do.
As you see I am getting the contents of a file from localhost, the file itself has a singular line.

std::system returns an int with an implementation-defined value. On many platforms, 0 means success and anything else means some sort of failure. I'm making this assumption in the below example.
My advice is to use libcurl which is what the curl command is using internally. With a little setup you can make your program perform curl actions and receive what you get back into your program. If you do not have access to libcurl or find it a bit hard to get started with, you could wrap your system command in a function which performs the curl command but directs the output to a temporary file which you read after curl is done.
Example:
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
// a simple class to remove the temporary file after use
struct file_to_remove {
// remove "filename" when the object is destroyed
~file_to_remove() { std::remove(filename.c_str()); }
const std::string& str() const { return filename; }
const std::string filename;
};
// A function to "curl":
std::string Curl(std::string options_plus_url) {
// An instance to remove the temporary file after use.
// Place it where you have permission to create files:
file_to_remove tempfile{"tmpfile"};
// build the command line
// -s to make curl silent
// -o to save to a file
options_plus_url = "curl -so " + tempfile.str() + " " + options_plus_url;
// perfom the system() command:
int rv = std::system(options_plus_url.c_str());
// not 0 is a common return value to indicate problems:
if(rv != 0) throw std::runtime_error("bad curl");
// open the file for reading
std::ifstream ifs(tempfile.str());
// throw if it didn't open ok:
if(!ifs) throw std::runtime_error(std::strerror(errno));
// put the whole file in the returned string:
return {std::istreambuf_iterator<char>(ifs),
std::istreambuf_iterator<char>{}};
} // tmpfile is removed when file_to_remove goes out of scope
With the above Curl function you can perform curl commands and get the response as a std::string which you can then use in your if statements etc.
Example:
int main(int argc, char* argv[]) {
if(argc < 2) return 1; // must get an URL as argument
try {
std::string response = Curl(argv[1]);
std::cout << response << '\n';
} catch(const std::exception& ex) {
std::cout << "Exception: " << ex.what() << '\n';
}
}

Related

How do I redirect stderr to /dev/null in C++?

#include <cstddef>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
//read the lines from the piped file using cin
string response;
int i = 0;
while (getline(cin, response)) {
//if the response is empty, stop
if (response.empty()) {
break;
}
//Write each odd line (1,3, etc..) to stderr (cerr)
//Write each even line (2,4. etc..) to stdout (cout)
if (i % 2 != 1) { //send odd to stderr
cerr << "err: " << response << endl;
}
else { //send even to stdout
cout << "out: " << response << endl;
}
i++;
}
return 0;
}
I want to redirect stderr to /dev/null, how would I go about doing so? I'm new to C++ and trying to learn by practicing, however, I'm not easily able to find an answer that fits my existing program.
Besides the excellent commentary above, it is pretty easy to make a “null” streambuf sink in C++.
#include <iostream>
struct null_streambuf: public std::streambuf
{
using int_type = std::streambuf::int_type;
using traits = std::streambuf::traits_type;
virtual int_type overflow( int_type value ) override
{
return value;
}
};
To use it, just set the rdbuf:
int main()
{
std::cerr.rdbuf( new null_streambuf );
std::cerr << "Does not print!\n";
}
If you wish to be able to turn it off and on, you will have to remember the original and restore it, not forgetting to delete the new null_streambuf.
int main()
{
std::cerr << "Prints!\n";
auto original_cerr_streambuf = std::cerr.rdbuf( new null_streambuf );
std::cerr << "Does not print.\n";
delete std::cerr.rdbuf( original_cerr_streambuf );
std::cerr << "Prints again!\n";
}
This does have the objective effect of being compiled to code, which I suspect is the advantage you are looking for: the ability to dynamically enable and disable diagnostic output.
This is, however, the usual function of a debug build, where you use the DEBUG macro to decide whether or not to compile something (such as error output operations) into the final executable.
Keep in mind that this does not disable output on standard error via other means, but only through cerr.

run a project of another project of the same solution

I have 2 projects, both on the same solution.
Project#1: does a single action based on debug inputs.
for simplicity let say that the main prints the debug inputs.
Project#2: I want to run Project#1 with for loop that run over different debug
inputs.
How can I do this right and efficient?
from what I understand, calling Project#1 exe file from Project#2 is not recommended. any other way to run Project#1 ::main, without changing Project#1? with only changes in Project#2 ..
Thanks,
New to advanced c++.
You don't have to do it in separate projects, you can do all from a single one, with different command line options.
for option one you can run the command line as: pro.exe debug-print1
the project just print the arguments and exit.
for the second option you can create a file,
You can put all your debug prints in file, and iterate on each line from the file, you just need to mark it as a file for example with a -f filename.
Next step is to process multiple files or debug-prints, or combination of files and prints in the same run.
So consider the following code for example:
#include <string>
#include <iostream>
#include <fstream>
//process a line function:
void proccess_debug_line(const std::string& debug_line)
{
std::cout<<debug_line<<std::endl;
}
//process file by processing each line:
void process_debug_file(const std::string& debug_file_name)
{
std::string debug_line;
std::ifstream inputfile(debug_file_name);
if(!inputfile)
{
std::cerr<<"error openning file: "<< debug_file_name <<std::endl;
}
while(std::getline(inputfile, debug_line))
{
//use the process line
proccess_debug_line(debug_line);
}
}
//argument can be a line, or a file if there is -f before it.
int main(int argc, char **argv)
{
for(int i=1; i<argc; i++)
{
std::string param = argv[i];
if(param[0] == '-')
{
if(param == "-f") // arguments in form -f filename
{
if(i == argc-1 ) // -f is last arg
{
std::cerr<<"Missing argument for " << param << " option."<<std::endl;
}
else //-f filename
{
std::string filename = argv[++i];
process_debug_file(filename);
}
}
else if(param.substr(0,2)== "-f")// argument in form -ffilename can be legal too
{
std::string filename = &(argv[i][2]);
process_debug_file(filename);
}
else
{
std::cerr<<"Unknown option '" << param << "'"<<std::endl;
++i;
}
}
else //a 'regular' parameter (without -f before it) is a debug print
{
proccess_debug_line(param);
}
}
}

How to stop the first child process from being executed?

Aim: To design a linux shell, which shows a prompt to take input from user, creates a new process to execute that command then terminates/exits the process. Here is my code
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
using namespace std;
string cmd; //global string so cmd copied to child to execute
void HandleAsParent(){
cout<<"Linux Shell 1.0\n";
string s;
while (!exitflag) {
cout<<"myShell>";
getline(cin,cmd); //Take user input
fork();
wait(NULL);
}
}
void HandleAsChild(){
cout<<"Executing";
system(cmd.c_str());
}
int main() {
pid_t p = fork();
if(p != 0){
HandleAsParent(); //This is parent process
}
else {
HandleAsChild(); //This is child process
}
}
The problem is that, because of the first fork() call in the main,
myShell>Executing
is displayed on the first line when the program runs instead of just
myShell>
.
I am able to understand why this is happening but cannot figure out how do I stop that first child process from being executed.
Please suggest me workarounds/solutions to my problem.
Edit 1: This is one of my Assignment(for learning UNIX Processes)
questions, and It is clearly stated that the program " prompts the
user for a command, parses the command, and then executes it with a
child process "
As I already guessed, system() probably uses a combination of fork(), exec() and wait(). Out of curiosity, I googled for source code and found one on woboq.org: glibc/sysdeps/posix/system.c.
This in mind, using system(), the required child process "comes for free". So, I got this minimal sample:
#include <iostream>
void callCmd(const std::string &cmd)
{
system(cmd.c_str());
}
int main()
{
std::cout << "My Linux Shell 1.0\n"
<< "Type exit[Enter] to exit.\n";
for (;;) {
std::cout << "> ";
std::string input; std::getline(std::cin, input);
if (input == "exit") return 0;
callCmd(input);
}
}
Compiled and tested on cygwin on Windows 10:
$ g++ -std=c++11 -o mycroShell mycroShell.cc
$ ./mycroShell
My Linux Shell 1.0
Type exit[Enter] to exit.
> echo "Hello"
Hello
> exit
$
After getting this running, the system() call in callCmd() can be replaced by fork()/exec()/wait() without the necessity to change anything else.
A simplified version could look like this:
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void callCmd(const std::string &input)
{
// the pre-processing: split the input into command and arguments
std::string cmdArgs = input;
std::vector<char*> args;
char *cmd = &cmdArgs[0];
args.push_back(cmd);
for (char *c = cmd; *c; ++c) {
if (*c == ' ') {
*c = '\0'; args.push_back(c + 1);
}
}
args.push_back(nullptr); // append terminator
// simple replacement of system() (not that sophisticated)
int ret = fork();
if (ret < 0) { // failure
std::cerr << "Failed to execute '" << cmd << "'!\n";
} else if (ret == 0) { // child
execvp(cmd, args.data());
} else { // parent
waitpid(ret, nullptr, 0);
}
}
int main()
{
std::cout << "My Linux Shell 1.1\n"
<< "Type exit[Enter] to exit.\n";
for (;;) {
std::cout << "> ";
std::string input; std::getline(std::cin, input);
if (input == "exit") return 0;
callCmd(input);
}
}
Compiled and tested on cygwin on Windows 10 again:
$ g++ -std=c++11 -o mycroShell mycroShell.cc
$ ./mycroShell
My Linux Shell 1.1
Type exit[Enter] to exit.
> /usr/bin/echo "Hello"
"Hello"
> exit
$
Notes:
IMHO, the most tricky part of this is to prepare a proper argument vector for execvp.
I tried with echo "Hello" as well and it worked. This surprised me a bit as echo is a bash built-in command. I assume that it found /usr/bin/echo and used it as well as in my above output.
The error handling is rather poor – something which should be extended for serious applications.

How to use Poco ProcessHandle with sed linux command that contains asterisk widlcard

I want to create a c++ app for ubuntu 16.04 that retrieves data from folder /sys/class/net/eth0/statistics/. Specifically I need to get rx_bytes and rx_packets. The command that I have tried succesfully in terminal is the following:
sed -n -e 3p -e 13p /sys/class/net/enp0s3/statistics/*
I tried to give the same command using Poco ProcessHandle method using the following code:
#include <iostream>
#include <vector>
#include "Poco/Exception.h"
#include "Poco/Process.h"
#include "Poco/Pipe.h"
#include "Poco/PipeStream.h"
using namespace std;
using Poco::Process;
using Poco::ProcessHandle;
using Poco::Pipe;
using Poco::PipeInputStream;
using Poco::PipeOutputStream;
using Poco::Exception;
int main(int argc, char** argv)
{
try {
std::vector<std::string> args;
std::string cmd = "sed";
args.push_back("-n");
args.push_back("-e");
args.push_back("3p");
args.push_back("-e");
args.push_back("15p");
args.push_back("/sys/class/net/enp0s3/statistics/*");
Pipe outPipe;
ProcessHandle ph = Process::launch(cmd, args, 0, &outPipe, 0);
PipeInputStream istr(outPipe);
std::string s;
int c = istr.get();
while (c != -1) {
s += (char)c;
c = istr.get();
}
std::cout << "string is " << s << std::endl;
return 0;
}
catch (Poco::Exception& exc)
{
std::cerr << exc.displayText() << exc.code() << string(exc.name()) << std::endl;
return 1;
}
However I get the error message:
sed: can't read /sys/class/net/enp0s3/statistics/*: No such file or directory
When I try the code with a specific file I get the correct strings.
I know that there is the command cat alternative but is there any way for the above code to work succesfully.

After convert string to const *json, when pasing json object, shows failed: (IsObject()), how to solve this?

After convert string strjson to const char* json, when interate, shows
failed: (IsObject()), function FindMember,failed, I don't understand why showed this, I think this the json object is correct format.
//
// main.cpp
// rapid
//
// Created by Shi Yan on 10/7/17.
// Copyright © 2017 Shi Yan. All rights reserved.
//
#include <iostream>
#include "rapidjson.h"
#include "document.h"
#include <fstream>
using namespace std;
using namespace rapidjson;
void readjson(){
ifstream handle("meta_Books.json");
if(handle.is_open()){
//cout<<"open success"<<endl;
const char* json;
string strjson;
int i=1;
while(getline(handle,strjson)){
if(i>4)
break;
cout<<strjson<<endl;
cout<<strjson.length()<<endl;
i++;
json=strjson.c_str();
cout<<"*********************"<<endl;
cout<<*json<<endl;
StringStream s (json);
Document document;
document.ParseStream(s);
Value::ConstMemberIterator itr = document.FindMember("asin");
cout<<itr->name.GetString()<<" = "<< itr->value.GetString()<<endl;
}
}
}
int main() {
readjson();
return 0;
}
I think the format of json object , so why failed?
As you can see , the getline() method works well, because the output of string is an complete string
The assertion error means that FindMember() is being called on a Value that does not represent a JSON object (IsObject() is false).
Since there is only 1 FindMember() in the code you showed, that implies that document.IsObject() is false when document.FindMember() fails. Either the JSON you are parsing does not start with an object in its root, or the parse failed. Neither condition of which you are testing for in your code.
If I had to guess (and please don't make people guess!), the failing JSON document likely contains an unencoded line break in it (that is not illegal inside of JSON string values). That would cause std::getline() to exit prematurely, thus causing parsing issues.
The 1st screenshot you showed supports that conclusion, showing that strjson is being split between 2 separate "lines" when the error occurs.
Rather than using std::getline() to read the file line-by-line, risking errors on embedded line breaks, I suggest you try using RapidJSON's BasicIStreamWrapper class to read the file document-by-document instead. ParseStream() has a kParseStopWhenDoneFlag flag that allows parsing multiple root documents from a single input stream:
kParseStopWhenDoneFlag 
After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error.
For example:
#include <iostream>
#include <fstream>
#include "rapidjson.h"
#include "document.h"
#include "istreamwrapper.h"
using namespace std;
using namespace rapidjson;
void readjson()
{
ifstream handle("meta_Books.json");
if (!handle.is_open())
{
// handle error...
cout << "error opening file" << endl;
}
else
{
BasicIStreamWrapper<ifstream> s(handle);
for(int i = 1; i <= 4; ++i)
{
Document document;
ParseResult pr = document.ParseStream<kParseStopWhenDoneFlag>(s);
if (!pr)
{
// handle error...
cout << "error parsing document " << i << endl;
}
else if (!document.IsObject())
{
cout << "document " << i << " is not an object" << endl;
}
else
{
Value::ConstMemberIterator itr = document.FindMember("asin");
if (itr != document.MemberEnd())
cout << "asin = " << itr->value.GetString() << endl;
else
cout << "asin not found" << endl;
}
}
}
}
int main()
{
readjson();
return 0;
}