0

Having a hard time formulating this question. Suggestions welcome.

My overall goal is to read in one line of the file at a time, and run a python program using those arguments, then go to the next line.

For example, say my program is myprog.py and it takes 3 input parameters. If I were using the command line alone, I would do this:

chmod +x myprog.py
./myprog.py arg1 arg2 arg3

But I want to have many possible values for arg1, arg2, and arg3. So I set up a text table in readparas.txt:

arg1_1 arg2_1 arg3_1
arg1_2 arg2_2 arg3_2
arg1_3 arg2_3 arg3_3
...

I want to read in the first line and run myprog. Then go to the second line and run myprog, etc.

The problem is some of the arguments go together. For example, arg2 and arg3 may be different settings of a single parameter. So arg2 and arg3 would need to be treated as one argument.

To complicate things, I want to do this using a bash script. The following was mostly taken from Sergey Mashchenko at SharcNet:

#!/bin/bash
i = 0
while read arg1 arg2 arg3
   do
   i = $(($i+1))
   mkdir RUN$i
   cd RUN$i
   ./myprog.py $arg1 $arg2 $arg3 #need groups of args here
   cd ..
   done < readparas.txt

I have more than three arguments, though, and groups of them need to be joined together. For example, suppose I have 8 arguments. I want to 'group' arguments 2 through 4 and arguments 6 through 8. How can I do that?

EDIT

On the suggestion by @chepner, I tried the following (yes there are actually 19 arguments, and I want to join f through j and k through s).

#!/usr/bin/bash #with thanks to rici for pointing this out

i=0
while read a b c d e f g h i j k l m n o p q r s
    do
    i = $(($i+1))
    mkdir RUN$i
    cd RUN$i
    ./my_prog "$a" "$b" "$c" "$d" "$e" "$f$g$h$i$j" "$k$l$m$n$o$p$q$r$s"
    cd ..
    done < readparas.txt 

It gives me this error:

-bash: ./epi_cmd.sh: usr/bin/bash: bad interpreter: No such file or directory

So does the commented suggestion by @redxef so I'm not sure if one of them is right, or if I've messed something else up. I know myprog.py runs when I run it through the command line (without bash, as in my example at the top). There must be an error in the bash?

EDIT 2

Okay, I fixed the bash error by realizing that I really did have a file that wasn't being read properly, and by realizing that I was defining 'i' twice.

This runs but returns another error:

#!/usr/bin/bash

i=0
while read a1 b1 c1 d1 e1 f1 g1 h1 i1 j1 k1 l1 m1 n1 o1 p1 q1 r1 s1
    do
    i = $(($i+1))
    mkdir RUN$i
    cd RUN$i
    ../myprog.py $a1 $b1 $c1 $d1 $e1 $f1\ $g1\ $h1\ $i1\ $j1 $k1\ $m1\ $n1\ $o1\ $p1\ $q1\ $r1\ $s1 
    cd ..
    done < readparas.txt 

The error message is:

./epi_cmd.sh: line 6: i: command not found

Then says all of the arguments are unrecognized.

I know there were requests for output, but that wouldn't fit here. I'm writing a .json file and two .txt files using myprog.py, and myprog.py itself is about 200 lines long....

StatsSorceress
  • 3,019
  • 7
  • 41
  • 82
  • Can you escape the argument separator between those connected? Like this: `arg0 arg1\ arg2\ arg3 arg4 arg5` where arg0 is one, arg1 through 3 are connected and 4 and 5 are separate again? Also cd'ing into a newly created directory and running a program from within that directory looks strange to me. – redxef Feb 12 '17 at 22:05
  • Your hash-bang line needs to be `#!/usr/bin/bash`, not `#!usr/bin/bash` – rici Feb 12 '17 at 22:30
  • Thanks @rici. You may be right, but that didn't fix the problem. – StatsSorceress Feb 12 '17 at 22:31
  • 1
    I am right about the shebang. I have no idea what you've done wrong since you haven't pasted the exact file you are trying to execute, so I could well be wrong about your error; my crystal ball is unfortunately fallible. – rici Feb 12 '17 at 22:32
  • rici++. Also, my favourite shebang notation for bash is `#!/usr/bin/env bash`, which pulls bash from the path rather than an explicit location. This allows my scripts to work on both OS X and FreeBSD, which put bash in different places. The proviso is that if you have multiple versions of bash in different places, you need to take care that the right one is found first. – ghoti Feb 12 '17 at 22:45
  • @StatsSorceress ... now that you've fixed your shebang, what problem are you having with chepner's solution? Can you perhaps add a couple of lines of sample input, your expected output, and your actual output? I too am having problems with my clairvoyance this evening. – ghoti Feb 12 '17 at 22:47
  • 1
    the command `i = $(($i+1))` is wrong. Remove the spaces : `i=$(($i+1)`. This is why you get `i command not found ` error. – George Vasiliou Feb 12 '17 at 23:06
  • @GeorgeVasiliou That was it! Thank you. I'm going to post the complete solution. – StatsSorceress Feb 12 '17 at 23:32

2 Answers2

3

This isn't that tricky, if I understand correctly.

while read -r -a p; do
    args=(
      "${p[0]}"
      "${p[1]}${p[2]}${p[3]}"
      "${p[4]}"
      "${p[5]}${p[6]}${p[7]}"
    )

    ./myprog.py "${args[@]}"
done < readparas.txt

or without arrays

while read -r p1 p2 p3 p4 p5 p6 p7 p8; do
    ./myprog.py "$p1" "$p2$p3$p4" "$p5" "$p6$p7$p8"
done < readparas.txt
chepner
  • 497,756
  • 71
  • 530
  • 681
0

I ended up having to do some list/string hacks to get the types to match what the rest of my program was expecting. I also didn't realize that I still needed to match the names of the arguments (I figured the order would take care of that), so I've included the flags here for anyone else who gets stuck on that part.

The full bash script is:

#/usr/bin/bash

i=0
while read a1 b1 c1 d1 e1 f1 g1 h1 i1 j1 k1 l1 m1 n1 o1 p1 q1 r1 s1
    do
    i=$(($i+1))
    mkdir RUN$i
    cp $a1 RUN$i
    cd RUN$i
    ../myprog.py --filename $a1 --reps $b1 --pop $c1 --susc $d1 --pl_trans $e1 --inf_period $f1\ $g1\ $h1\ $i1\ $j1 --eps $k1\ $m1\ $n1\ $o1\ $p1\ $q1\ $r1\ $s1 
    cd ..
    done < readparas.txt

And the main() portion of myprog.py looks like this:

if __name__ == "__main__":


#Thanks to http://stackoverflow.com/questions/7605631/passing-a-list-to-python-from-command-line Thomas answer

#At the top, import argparse
#This allows arguments to be parsed from the command line

    parser = argparse.ArgumentParser()
    parser.add_argument('--filename')
    parser.add_argument('--reps')
    parser.add_argument('--pop')
    parser.add_argument('--susc')
    parser.add_argument('--pl_trans')
    parser.add_argument('--inf_period', nargs='*')
    parser.add_argument('--eps', nargs='*')

    args = parser.parse_args()

    pop_file = args.filename
    susc = float(args.susc)
    pl_trans = float(args.pl_trans)
    i_period = args.inf_period[0].split() 
    #args.inf_period is a list; args.inf_period[0] a string
    inf_period = [int(i_period[i]) for i in range(len(i_period))]
    n_eps = args.eps[0].split()
    eps = [float(n_eps[i]) for i in range(len(n_eps))]
    reps = int(args.reps)
    pop = int(args.pop)

    returned_answer = function_in_body(susc, pl_trans, inf_period, eps, reps, pop)
StatsSorceress
  • 3,019
  • 7
  • 41
  • 82
  • `$f1\ $g1\ $h1\ $i1\ $j1` is not good style. You should use `"$f1 $g1 $h1 $i1 $j1"`. The difference is that the variable expansions are not quoted in the first one, while they are in the second one. You'll see the difference if `$f1` contains white-space, for example; in the first one, the value will be word-split into several arguments; in the quoted one, it won't be. It's good style to always quote variable expansions; then you don't have to think about whether they might contain shell metacharacters or whitespace. – rici Feb 13 '17 at 01:09