I currently have a c++ Linux program that reads a parameter "P" from a file and loads it in RAM for further operations. The file has the following line :
P = 123
I would like the program to take P from shell input instead of the file. I am open to all options, as long as I can manually enter P while connected in SSH.
What I have in mind is something like an input prompt :
sudo myprogram start
enter P value : (I would manually enter "123" here)
Or maybe an argument :
sudo myprogram start 123
It must be simple to do but I do not know how, so any help is greatly appreciated !
If this is the only data that the file has then the file operation is needless.
Simply pass 123 (or whatever) to your C++ program and convert the string into integer.
Assuming you pass the integer as the second argument then:
int p = atoi(argv[2]);
A better option is to use strtol:
char *s, *ptr;
s = argv[1];
int p = strtol(s, &ptr, 10);
If you can't make changes to the C++ code then simply do:
echo "P = 123" > file && myprogram start
If your file has more content and you can't simply do echo then, replace the existing line with new value:
sed -i "s/P = [0-9]*/P = 123/" file && myprogram start
First version (enter from keyboard):
echo -n "enter P value: "
read P
Second version (pass as shell script argument):
P=$1
Third version (learn bash/shell programming):
Advanced Bash-Scripting Guide
BASH Programming - Introduction HOW-TO
and many others
This is basic C++. Take a look at the sample code below or visit the site I copied it from.
#include <iostream>
// When passing char arrays as parameters they must be pointers
int main(int argc, char* argv[]) {
if (argc < 5) { // Check the value of argc. If not enough parameters have been passed, inform user and exit.
std::cout << "Usage is -in <infile> -out <outdir>\n"; // Inform the user of how to use the program
std::cin.get();
exit(0);
} else { // if we got enough parameters...
char* myFile, myPath, myOutPath;
std::cout << argv[0];
for (int i = 1; i < argc; i++) { /* We will iterate over argv[] to get the parameters stored inside.
* Note that we're starting on 1 because we don't need to know the
* path of the program, which is stored in argv[0] */
if (i + 1 != argc) // Check that we haven't finished parsing already
if (argv[i] == "-f") {
// We know the next argument *should* be the filename:
myFile = argv[i + 1];
} else if (argv[i] == "-p") {
myPath = argv[i + 1];
} else if (argv[i] == "-o") {
myOutPath = argv[i + 1];
} else {
std::cout << "Not enough or invalid arguments, please try again.\n";
Sleep(2000);
exit(0);
}
std::cout << argv[i] << " ";
}
//... some more code
std::cin.get();
return 0;
}
}
Don't you simply want to be prompted, within your C++ program, to input the value?
If that is what you want, this simple code will do the job:
#include <iostream>
int main(int argc, char **argv) {
int p = 0;
std::cout << "Enter P value: ";
std::cin >> p;
std::cout << "Entered value: " << p << std::endl;
return 0;
}
Related
I've recently been working on a C++ project of mine, and I ran into a problem with the executable:
std::string commanddata;
std::string input;
std::string cmdname;
int commandlength = 0;
if (commandlength == 0){}else{} // This is so that G++ doesn't complain that I have got a variable and never used it (I'm compiling at -Werror)
while (true)
{
input = DS_IO.kscanf(">>>"); // This is a specific function to get input from the user
// Record the command length
int commandlength = sizeof(input);
// Make sure that the user doesn't just send us nothing
if (commandlength <= 0)
{
continue;
}
else
{
// Get the first word from the command
std::stringstream sstream(input);
sstream >> cmdname;
// Loops through the input string to remove everything up to and including a space delimiter
for (int i = 0; i <= commandlength; i++)
{
if (input[i] == 32)
{
commanddata = input;
commanddata.erase(input.begin(), input.begin() + i);
break;
}
else
{
continue;
}
}
// print out the data to make sure it's all working
std::cout << "What you entered: " << input << std::endl;
std::cout << "The command name: " << cmdname << std::endl;
std::cout << "The command metadata: " << commanddata << std::endl;
}
}
The code works fine, but my problem is as soon as it reaches the end of the loop, it freaks out and gives this error:
EXPECTED BEHAVIOUR:
chemeriov#mikumputer:~/dev/DigitalSledgehammer$ ./a.out
>>>echo Hello world
What you entered: Hello world
The command name: echo
The command metadata: Hello world
>>>
EXPERIENCED BEHAVIOUR:
chemeriov#mikumputer:~/dev/DigitalSledgehammer$ ./a.out
>>>echo Hello world
What you entered: Hello world
The command name: echo
The command metadata: Hello world
double free or corruption (out)
Aborted (core dumped)
chemeriov#mikumputer:~/dev/DigitalSledgehammer$
How to fix this?
The sizeof(input) is a constant number, like 16 bytes. To find length of the string, call input.length() method.
Thanks very much to #Soonts, who fixed my problem!
Another issue, commanddata.erase( input.begin() is obviously wrong, a container can’t erase ranges inside other unrelated containers. A correct usage is something.erase(something.begin(), ..
You can clearly tell I'm a noob at this :)
I'm supposed to take command line inputs and output them in reverse order. The code I have so far is
#include<iostream>
#include<fstream>
using namespace std;
int main(int argc, char *argv[]) {
for(int num = argc; num >= 2; num--)
cout << argv[num - 1] << " ";
for(int num = argc; num < 2; num--)
cout << argv[num - 1];
return 0
}
It does its job, but I'm getting an unwanted white space the end of the very last output so for example if I did
./reverse first second third
It would output
third_second_first_
The space after the first is unwanted and I'm having trouble getting rid of it. It's supposed to take as many arguments as I'd like. Is there a simple way to delete the last space?
Best way to delete it is by not printing it in the first place.
Test to make sure you have at least one printable input and print it. Then for all remaining inputs print out the delimiter followed by the input.
#include<iostream>
int main(int argc, char *argv[]) {
if (argc > 1)
{ // Ensure that where is at least one argument to print
std::cout << argv[argc - 1]; // print last argument without adornment
for(int num = argc - 2; num > 0; num--)
{ // Print a space and all remaining arguments.
// For visibility, I've replaced the space with an underscore
std::cout << "_" << argv[num] ;
}
}
/* unsure what this loop is supposed to do. Doesn't do anything in it's current state,
so I've commented it out.
for(int num = argc; num < 2; num--)
{
std::cout << argv[num - 1];
}
*/
return 0;
}
Input:
first second third
Output:
third_second_first
For the "simple" solution, I would do something like this:
#include <iostream>
int main(int argc, char* argv[])
{
while (--argc > 0)
std::cout << argv[argc] << ' ';
std::cout << '\n':
}
Do note that it will print the trailing space after the last (first) argument, but unless your professor has a requirement to not have it, or you have an online judge or something similar that disallows it I don't see how it really matters. It's easy to fix though:
while (--argc > 1)
std::cout << argv[argc] << ' ';
std::cout << argv[argc] << '\n':
The problem has been solved, the code re-write is as follows:
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char** argv){
std::string input;
std::vector<std::string> inputVector;
while(std::getline( std::cin, input ) ){
inputVector.push_back(input);
}
for(int i = 0; i < inputVector.size(); i++){
std::cout << i << " of " << inputVector.size()-1 << " is " << inputVector[i] << "\n";
}
return 0;
}
As a slight aside, the output is different in CMD and in Powershell visually - it looks like there are TWO endlines when this is done in Powershell (That is, there is a blank line between each proper line) and I suspect (but have not investigated) that this is because there is a whole lot of whitespace at the end of Powershell lines so when you prepend "xx of xx is " at the front, the line wraps around.
====================ORIGINAL=QUESTION=BENEATH=THIS=LINE====================
This code should just print all arguments:
//dirparser.cpp
#include <iostream>
int main(int argc, char** argv){
for( int i = 0; i<argc ; i++){
std::cout << i << " of " << argc-1 << " is " << argv[i] << "\n";
}
return 0;
}
And it seems to run fine - if I call e.g
dirparser.exe a b c
The output is as expected:
0 of 3 is dirparser.exe
1 of 3 is a
2 of 3 is b
3 of 3 is c
But when I do this, in the command line:
dir | dirparser.exe //In CMD
dir | .\dirparser.exe //In Powershell
ls | .\dirparser.exe //In Powershell
The output I get is:
0 of 0 is dirparser.exe //CMD
0 of 0 is [directory]\dirparser.exe //Powershell
0 of 0 is [directory]\dirparser.exe //Powershell
And nothing further.
It's not because dir and/or ls return nothing - calling those commands alone without piping gives me the file structure as per usual. I suspect I'm missing something essential - probably about piping behavior - but I'm fairly clueless as to where I should start.
Piping doesn't work with arguments, but standard input.
If you want to read the data send by ls or dir to your program, you need to read a stream : std::cin.
A basic C++ example : here.
Piping passes stdin to the command, not command line arguments. You need to read off the 'pipe' using stdin.
You use command line processor - that is rather complicated interpreter of user command. So this interpreter has set of rules - some rules describe how to start your program but some rules modifies behavior of command line processor. |, &, >, < are commands for interpreter but not for your program. That is why it is not treated as command line arguments. But you can pass | with help of quotes:
myprog "arg1 | arg2"
But in this case it is not pipe of streams
I've been trying to create a program in C++ that tries to accomplish this pseudocode:
get argv[1] into int
get int’s digits into array[int length]
for int i = array length; i >= 0;
gen random number into check
if check == array[i]
i
say Number i was check
end if
And I think the part I'm really struggling with is the
get argv[1] into int
get int’s digits into array[int length]
part. In my full code there isn't even an attempt because nothing I've tried works. The error I get the most is that the code compiles, but everytime it tries to cout << "Number 1:" << number I just get Number 1: 0 no matter the actual number I enter. And when 0 == 0 the code doesn't even notice.
My broken propably convention-breaking code follows:
#include <iostream>
#include <string>
int main (int argc, char **argv) {
if (argc == 1 || argc == 3) {
std::cout << "Argument count does not match (one argument expected)\n";
return(-1);
}
std::cout << "Input: " << argv[1] << "\n";
const char* text = argv[1];
int number = atoi(text);
int check = rand() % 10;
std::cout << "Check 1: " << check << "\nNumber 1: " << number << "\n";
if (check == array[i]) {
i++;
std::cout << "Success! Number " << i << " was " << check << ".\n";
}
}
}
TL;DR: My "sort of" number cracker doesn't want to put argv1 into an int with the int's digits being later put into an array.
Feel free to make me feel stupid. Hope the question isn't too specific. I'll expand on details as asked.
EDIT: This is an earlier attempt at conversion:
int array[];
for (int i = strlen(text); i >= 0; i--) {
array[i] = number % 10;
number /= 10;
}
EDIT2: So many responses, no solutions. Thank you for trying to explain this newbie so many things at once. BTW: Git
The earlier attempt is almost good: it's just that you have to actually allocate space for the array, like this:
int array[strlen(text)];
if your compiler supports variable-length arrays as an extension, and
std::vector<int> array;
array.resize(strlen(text));
if you want to stick with standard C++ and follow some good practices.
However, if you want to be tricky, you don't even need to convert the argument to a number:
if (argv[1][i] == check % 10 + '0')
does the trick too. All in all, the complete program would look like this:
#include <iostream>
#include <cstdlib>
int main(int argc, char *argv[])
{
int check = std::rand();
std::cout << check << std::endl;
char *p = argv[1] + strlen(argv[1]);
while (p - argv[1] >= 0) {
if (*--p == '0' + check % 10)
std::cout << "guessed " << p - argv[1] << "th digit" << std::endl;
check /= 10;
}
return 0;
}
Your code is relatively close to being right. You are struggling with the declaration of the array (you must specify the size for it). 32-bit int cannot have more than ten digits, so declaring
int array[10];
should be sufficient.
Before converting the number to an array of digits, check if it is negative, and flip its sign if it is negative:
if (number < 0) {
number = -number;
}
Otherwise, your number%10 trick is not going to work.
When you do the conversion, count how many digits you have. Put the result in actualCount variable: chances are that you are not going to use up all the digits in your array, so
int check = rand() % 10; // 10 is the max, not the actual digit count
should be
int check = rand() % actualCount;
Your argument checking also needs improvement: think what would happen if the user passes five parameters? If you expect exactly one argument, you should write
if (argc != 2) {
std::cout << "Argument count does not match (one argument expected)\n";
return(-1);
}
In order to extract only one digit at a time from a number you have a couple of choices.
For convenience you can use a std::string, inserting the original string (argv[1]) in it, then extracting one char at a time:
#include <string>
...
// put the input in a string
std::string text = argv[1];
for (unsigned i = 0; i < text.size(); i++)
{
// extract only one char, a digit
char ch = text.at(i);
// convert that char in a number
int n = ::atoi(& ch);
// use n
...
}
If you don't want to use std::string, you can always use a c-like array (argv[1] itself):
#include <cstring>
...
for (unsigned i = 0; i < strlen(argv[1]); i++)
{
// extract only one char, a digit
char digit = argv[1][i];
// convert that char in a number
int num = ::atoi(& digit);
// use n
...
}
How would you use an if/then statement using argv[], upon the option/parameter entered?
For example, a.out -d 1 sample.txt versus a.out -e 1 sample.txt.
int main (int argc, char *argv[])
{
ifstream infile(argv[3]);
int c;
int number = 0;
int count = 0;
while ( infile.good() ) {
if (argv[1] == "-d")
{
c = infile.get();
number = atoi(argv[2]);
if (number == count)
{
cout.put(c);
count = 0;
}
else
count++;
}
if (argv[1] == "-e")
{
cout << "entered -e" << endl; //testing code
}
}//end while
}//end main
You can't use the equality operator to compare C-style strings, you have to use std::strcmp:
if (std::strcmp(argv[1], "-d") == 0)
The reason behind that is that the == operator compares the pointers not what they point to.
I hope you want to check the input parameter -d or -e, right?
if that is the case please use strcmp()
if (!strcmp(argv[1],"-d"))
{
count++;
printf("\ncount=%d",count);
}
if (!strcmp(argv[1],"-e"))
{
printf("entered -e"); //testing code
}
The first error is in the very first line of main:
ifstream infile(argv[3]);
You cannot write that because there is no third argument. When you invoke your program like this:
a.out -d 1 < sample.txt
then the command line that the program sees looks like this:
argv[0] = "a.out"
argv[1] = "-d"
argv[2] = "1"
The < sample.txt, by contrast, is interpreted by the shell directly, and the file is streamed to the standard input of your program – and there’s nothing you can do to change that, inside your program.
As for the parsing itself, don’t do it inside the loop which reads the file, do it before and set appropriate flags.
For the actual parsing I’d suggest using a library to spare you a lot of pain. The standard Unix tool is getopt but that’s only got a C interface. There are several C++ libraries, amongst them Boost.Program_Options which is a tad too complex for my taste, though.
The argc/argv comes from C and is rather cumbersome to use, so when more than basic argument passing is done, you can transform the arguments to a vector of strings and work in a C++ style:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
main(int argc, char* argv[])
{
std::vector<std::string> args;
std::transform(argv+1, argv+argc, std::back_inserter(args), [](char* arg){ return std::string(arg); });
if (args.at(1) == "-d") { std::cout << "Arg 1 = -d" << "\n"; }
for (auto& arg: args) { std::cout << "arg: " << arg << "\n"; }
}
Still, you need to check that the argument is present. If this is a basic tool and it is acceptable that the tool aborts when the parameter is not present, then you can access args elements with args.at(x) instead of args[x]
Or check this SO question for an argument parser.
char argv[] is an array of char *
so
if (string(argv[1]) == "-d")