LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Newbie (https://www.linuxquestions.org/questions/linux-newbie-8/)
-   -   Shell script defining for loops of parameters of separate program (https://www.linuxquestions.org/questions/linux-newbie-8/shell-script-defining-for-loops-of-parameters-of-separate-program-4175470519/)

LiquoriceHats 07-22-2013 12:23 PM

Shell script defining for loops of parameters of separate program
 
Hello everyone,

I've written a program in c++ (program.cpp)that needs to be run multiple times for all combinations of two parameters p1, p2 which are currently manually defined before each run. What I'd like to do is something to the effect of:

for(p1=0; p1<p1max; p1++){
for(p2=0; p2<p2max; p2++){
run program.cpp
}
}
where I can change the limits of the maximum values so the whole thing can be parallelized. After some searching a shell script seems the best way to do this, but I'm new to unix and so would appreciate some help getting started. Thank you.

grail 07-22-2013 12:32 PM

Assuming bash (that's a type of shell) if you ditch the braces for 'do' and 'done' and add a second set of round brackets your set :)
Obviously you would also need to set your max values prior:
Code:

p1max=10
p2max=5

for (( p1 = 0; p1 < p1max; p1++ ))
do
    for (( p2 = 0; p2 < p2max; p2++ ))
    do
        run program.cpp
    done
done


LiquoriceHats 07-22-2013 12:41 PM

Quote:

Originally Posted by grail (Post 4994770)
Assuming bash (that's a type of shell) if you ditch the braces for 'do' and 'done' and add a second set of round brackets your set :)

Yes, I do mean bash. Does that mean that
Code:

p1max=10
p2max=20
for(( p1 = 0; p1 < p1max; p1++ ))
do
    for (( p2 = 0; p2 < p2max; p2++ ))
    do
        make
    done
done

will do what I want (my code presently compiles and runs using a makefile)?

grail 07-23-2013 09:48 AM

Yes, although if you simply wanted to run a single command 200 times then a single loop is all that is required.

jpollard 07-23-2013 11:40 AM

Note: the example given runs make - which unless something in the source has changed, will do nothing after the first time.

Normally, such parameters would be passed on the command line, so instead of make, you would put "progname $p1 $p2".

And if you were going to run multiple of these in parallel, you need to have different output files... So assuming the output is sent to stdout, that would make the command something like "progname $p1 $p2 >$p1_$p2.result&"

Where the trailing & will put each invocation in the background. This COULD (depending on the parameters) overload your system.

LiquoriceHats 07-23-2013 01:26 PM

Quote:

Originally Posted by jpollard (Post 4995332)
Note: the example given runs make - which unless something in the source has changed, will do nothing after the first time.
Normally, such parameters would be passed on the command line, so instead of make, you would put "progname $p1 $p2".

Well, I have two parameters in my program called p1 and p2, and I want the code to run for those parameters being changed according to the values in the for loops. Would the below overwrite the value of p1 and p2 in program.cpp and run it with those specified in the for loop? Do I need to do change anything in the c++ code itself to allow this?
Code:

#!/bin/sh
p1max=10
p2max=20
for(( p1 = 0; p1 < p1max; p1++ ))
do
    for (( p2 = 0; p2 < p2max; p2++ ))
    do
        program.cpp $p1 $p2
    done
done


jpollard 07-23-2013 08:20 PM

Yes - they have to be evaluated by the program and set in the appropriate place.

It is just the normal practice. The only times that can get difficult is if they are used to define array dimensions in static structures, which isn't necessary.

I only bring the subject up because you indicated you wanted to run them in parallel, and the first scrip could not do that.

evo2 07-23-2013 08:37 PM

Hi,

you will need to modify your program so that it can take the values of p1 and p2 as arguments on the command line. So your program would look something like
Code:

#include <iostream>
#include <string>
#include <cstdlib>
int main(int argc, char** argv )
{
        if ( argc < 3) {
                std::cerr << "Error: must provide two arguments on the command line."
                          << std::endl;
                exit(1);
        }
        std::string p1 = argv[1];
        std::string p2 = argv[2];
        std::cout << "p1 = " << p1 << ", p2 = " << p2 << std::endl;
}

Compile...
Code:

g++ -o program program.cpp
Then you could run it using a shell script like:
Code:

for par1 in x y z ; do
  for par2 in a b c ; do
    ./program $par1 $par2
  done
done

Evo2.

LiquoriceHats 07-24-2013 12:57 AM

Quote:

Originally Posted by jpollard (Post 4995577)
Yes - they have to be evaluated by the program and set in the appropriate place.

It is just the normal practice. The only times that can get difficult is if they are used to define array dimensions in static structures, which isn't necessary.

I only bring the subject up because you indicated you wanted to run them in parallel, and the first scrip could not do that.

That's not a problem as I'm working with vectors, but thanks for the heads up.

LiquoriceHats 07-24-2013 01:05 AM

Quote:

Originally Posted by evo2 (Post 4995580)
Hi,

you will need to modify your program so that it can take the values of p1 and p2 as arguments on the command line. So your program would look something like
Code:

#include <iostream>
#include <string>
#include <cstdlib>
int main(int argc, char** argv )
{
        if ( argc < 3) {
                std::cerr << "Error: must provide two arguments on the command line."
                          << std::endl;
                exit(1);
        }
        std::string p1 = argv[1];
        std::string p2 = argv[2];
        std::cout << "p1 = " << p1 << ", p2 = " << p2 << std::endl;
}


Thank you for being so specific and helpful. Your suggestion is pretty much the same result I got to after some more reading on command line parsing. I don't understand why you're setting p1 and p2 to be strings when I want them to have numerical values, though. Why is that?

evo2 07-24-2013 01:25 AM

Hi,

Quote:

Originally Posted by LiquoriceHats (Post 4995669)
I don't understand why you're setting p1 and p2 to be strings when I want them to have numerical values, though. Why is that?

Because the command line arguments are passed to main() as char* which convert trivially to std::string. If you want to use some sort of floaing point you will need to convert them. There are a number of ways to do this. For example you could use a function like:

Code:

#include <string>
#include <sstream>
float s2f( std::string s )
{
        std::stringstream istr(s);
        float x;
        istr >> x;
        return x;
}

or you could use one of the old C style methods.

Evo2.

LiquoriceHats 07-24-2013 03:07 AM

Quote:

Originally Posted by evo2 (Post 4995676)
Because the command line arguments are passed to main() as char* which convert trivially to std::string.

I see, that makes sense. Thank you. However, when the script starts, I get a segmentation error when not even halfway through the program which I don't when just running the program directly. Why might this be?

jpollard 07-24-2013 06:40 AM

Depends on the code.

LiquoriceHats 07-24-2013 06:52 AM

I found the mistake. Thank you very much to all of you for your help. :)


All times are GMT -5. The time now is 06:26 PM.