2

I'm executing a python file based on the output from the cat/awk output. Say I have a file something like

samp.txt:

test.py test.conf
dev.py dev.conf

Now I have a small shell script, which cats the samp.txt file and triggers the Python script.

samp.sh:

OLDIFS=$IFS; IFS=$'\n';for file in $(cat samp.txt) ;
do
    text="$file -c $1 -s $2 -t $3"
    out=`python $text`

done

When I run the shell script, it says

python: can't open file 'test.py test.conf' : [Errno 2] No such file or directory

The problem is because of the single quotes. Need some suggestions to get over it.

  • Do you specify the absolute path to test.py? – Peschke Mar 15 '19 at 7:50
  • Yes , i cd into the directory and then execute it – rajpython Mar 15 '19 at 7:52
  • Do you have both test.py and test.conf in the name quotes? For example: python ‘test.py test.conf’. In this case, python would treat that as one argument. You’d need to make sure there are in their own quotes. – Peschke Mar 15 '19 at 7:58
3

The issue is not due to any single quotes (I'm assuming you're referring to the single quotes displayed in the error output; these are just Python's way of displaying the error) but to the fact that you rely on the shell to split $text into separate words on spaces. At the same time, you have removed the space character from the IFS variable before your loop, which means that the shell won't do that splitting correctly.

The correct solution is not to get the shell to split $text on spaces, as that would still also invoke filename globbing on the generated words. Since $text contains $1, $2 and $3, these values would also be split on spaces etc. when $text is split.

Instead of relying on the shell to split the $text variable correctly, read the two words on each line of samp.txt in a while loop:

while read -r script config; do
    out=$( python "$script" "$config" -c "$1" -s "$2" -t "$3" )
done <samp.txt

If you want to use a separate variable for your arguments, and if you're using e.g. bash or another shell that has named arrays:

args=( -c "$1" -s "$2" -t "$3" )
while read -r script config; do
    out=$( python "$script" "$config" "${args[@]}" )
done <samp.txt

Using an array for the arguments instead of a text string allows the argument to be properly delimited within the array. The expansion of "${args[@]}" would be the separate elements of that array with each element properly quoted. This means that e.g. $1, $2 and $3 could contain spaces etc. without any issues.

In a POSIX (/bin/sh) script, assuming you can safely discard the positional parameters from $4 onwards:

set -- -c "$1" -s "$2" -t "$3"
while read -r script config; do
    out=$( python "$script" "$config" "$@" )
done <samp.txt
  • On a different note, most POSIX compatible shells do implement eval – Bananguin Mar 15 '19 at 8:24
  • @Bananguin Yes they do. But it's difficult to use correctly and safely, especially if user-supplied data is involved. I have never had to use eval in my own scripts. Here, for example, eval is definitely not needed. – Kusalananda Mar 15 '19 at 8:28
  • +1 for the use of a bash array to delimit array components properly. I had to try it to convince myself that it works as intended. Still learning and my hat is off to you, even though as I already said on SE, I tend to shy away from bashisms. Go POSIX instead ! – Cbhihe Mar 15 '19 at 12:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.