[Tutor] Pass arguments from bash script to embedded python script

Cameron Simpson cs at cskk.id.au
Mon Oct 21 19:13:08 EDT 2019


On 21Oct2019 09:45, Stephen P. Molnar <s.molnar at sbcglobal.net> wrote:
>First of all, let me emphasize that this is not a homework assignment.
>I have a large number of data files form a Quantum Chemistry program 
>which all have the same format.
>
>I have written a short python script to extract the data required for 
>another program.
>
>>#!/usr/bin/env python3
>># -*- coding: utf-8 -*-
>>"""
>>
>>Created on Tue Sep 24 07:51:11 2019
>>
>>"""""
>>import numpy as np
>>
>>data = np.genfromtxt(fname, usecols=(1), skip_header=27, 
>>skip_footer=1, encoding=None)
>>
>>print(data)
>>
>>np.savetxt('fname.dG', data, fmt='%.10g', header='fname1')
>>print(data)

Can you show us a bare command line invocation of this Python programme?  
It is not clear to me what you expect to be in "fname"; I would have 
assumed a filename, but your shell script suggests otherwise.

>and I have a bash script in which I have embedded the python script:
>
>>#!/bin/bash

Please, just say "shell script" instead of "bash script", and use 
"#!/bin/sh".  All systems have a /bin/sh, not all systems have a 
/bin/bash, and nothing in your script needs anything that is special to 
bash.

>># Run.dG.list_1
>>
>>while IFS= read -r d
>>do
>>    python3 DeltaGTable_V_sw.py
>>done <ligand.list

Small recommendation: when you write a while loop in the shell which 
reads from a file, you should avoid letting the commands _inside_ the 
while loop also read from that file - they will consume it if they do 
so, and your loop will terminate early. Rewrite the above loop like 
this:

    while IFS= read -r d <&3
    do
        python3 DeltaGTable_V_sw.py 3<&-
    done 3<ligand.list

This attaches the "ligang.list" file to the "while" as file descriptor 
3, reads from file descriptor 3, and arranges to _not_ pass file 
descriptor 3 to the python programme. Much much safer.

>The ligand.list for the bash file have the format:
>>
>>>fname = ['C-VX3.log', '15-7.log', '14-7.log']
>>>fname1 = ['C-VX3', '15-7', '14-7'
>
>where fname and fname1 are three typical data files.

Are these 2 lines:

    fname = ['C-VX3.log', '15-7.log', '14-7.log']
    fname1 = ['C-VX3', '15-7', '14-7'

lines from the ligang.list file? i.e. actual Python assignment 
statements?

>my problem is how do I pass the argument's in the two files from the 
>bash script to the python script? It has been suggested to me that the 
>solution is an implementation of the sys/.argv function but it seems 
>that every reference that I can find goes the other way from python to 
>bash.

Yah, because people call shell scripts or plain commands via subprocess 
and routinely get this incorrect.

For your purpose, you have a shell variable $d containing a line from 
the "ligand.list" file. I'd like to see an example of such a line, I 
found your description unclear above.

However, to get you going...

You can pass that line as a single command line string to the Python 
programme like this:

    python3 DeltaGTable_V_sw.py "$d"

That will include any internal spaces etc in that single string, 
courtesy of the quotes. Within the Python programm that string will be 
sys.argv[1]; sys.argv being the command line invocation and sys.argv[0] 
being the script name "DeltaGTable_V_sw.py".

You will need to "import sys" to get access to sys.argv of course.

That puts the parsing of the line in your Python code: you will need to 
break it up on speaces or whatever is sesnsible (as I say, I'm unsure 
what is really in the line). If "ligand.list" is just a text file with 
filenames in it, one per line, you won't need to do anything to it - 
just use the string.

Alternatively the shell script could break up the line on spaces if you 
go:

    python3 DeltaGTable_V_sw.py $d

This just omites the quotes, which causes the shell to grep $d into 
"words". Supposing the line had:

    C-VX3.log g15-7.log 14-7.log

then inside the Python programme sys.argv would have 4 strings:

    DeltaGTable_V_sw.py
    C-VX3.log
    g15-7.log
    14-7.log

and you can use sys.argv[1:] to access the latter 3 strings.

Your shell script is, however, so simple that you may as well just do it 
_all_ in Python:

    with open("ligand.list") as ligands:
        for line in ligands:
            # drop trailing newline and whitespace
            text = line.rstrip()
            ... do whatever you want with text ...

This sidesteps the shell entirely.

Note that this kind of thing does get wordy in Python if your command 
line stuff gets more comoplicated, and there will be a sweet spot where 
it is nice to have a wrapper shell script like yours. I think you're 
possibly just below that point (shell script too simple to bother with).

We can provide more detailed help if you can make the contents of 
ligans.list more clear, and the desired invocation of the Python 
programme more clear.

Cheers,
Cameron Simpson <cs at cskk.id.au>


More information about the Tutor mailing list