Seb
Seb

Reputation: 193

Bash parse & concatenate arguments for executing python

I've got a bootstrap (bash) that should filter out some arguments before it starts the propper python script.

The problem is that whenever i pass a string with spaces into the bootstrap it gets mangled up once it arrives at python

e.g executing

./myBootStrap.sh --preload "argl bargl" -j -as -argl --postload "my Test"

prints this

Executing myBootStrap --preload "argl bargl" -j -as -argl --postload "my Test"

and my python script prints its argument

got arguments ['myBootStrap','--preload', '"argl', 'bargl"', '-j', '-as', '-argl', '--postload', '"my', 'Test"']

as you see the "argl bargl" and "my Test" get split up into ['"argl','bargl"'] & ['"my', 'Test"'] instead of staying combined.

any idea whats wrong with my code ?

thanks heaps!


myBootStrap.sh

#!/bin/bash
declare -a argv
for ((i=1;i<=${#@};i+=1))
do

   arg=${@:i:1}

   if [[ "$arg" == "--preload"* ]];then
      i=$i+1
      marg=${@:$((i)):1}
      preLoadO=$arg
      preLoadA=" \"${marg}\""
      argv=("${argv[@]}" $arg)
      argv=("${argv[@]}" $preLoadA)

   elif [[ "$arg" == "--postload"* ]];then
      i=$i+1
      marg=${@:$((i)):1}
      postLoadO=$arg
      postLoadA=" \"${marg}\""
      argv=("${argv[@]}" $arg)
      argv=("${argv[@]}" $postLoadA)       
   else          
      argv=("${argv[@]}" $arg)
   fi
done

arguments=$(printf " %s" "${argv[@]}")
arguments=${arguments:1}

echo "Executing myBootStrap" $arguments 
exec myBootStrap $arguments 

and the python script myBootStrap

#!/usr/bin/env python
import sys
print 'got arguments %s'%sys.argv

Upvotes: 1

Views: 732

Answers (1)

Dennis Williamson
Dennis Williamson

Reputation: 360065

Quoting almost always fixes this type of problem.

exec myBootStrap "$arguments"

Demo:

$ a='"abc def" ghi'
$ echo "$a"
"abc def" ghi
$ args $a
3 args: <"abc> <def"> <ghi>
$ args "$a"
1 args: <"abc def" ghi>
$ cat args
#! /bin/sh
# Greg Wooledge's args script
printf "%d args:" $#
printf " <%s>" "$@"
echo

Edit:

OK, I spent some time analyzing what your Bash script is actually doing. It's going through a lot of gyrations to simply try to produce exactly the same arguments it was given and then pass them to the Python script.

It could simply be replaced by:

exec myBootStrap "$@"

However, I presume that you're actually doing some other processing there which we don't see. Based on that, I've modified your script so it can be used as a basis for something like that.

#!/bin/bash
declare -a argv
for ((i = 1; i <= $#; i += 1))
do

   arg=${@:i:1}

   if [[ "$arg" == "--preload"* ]]; then
      marg=${@: ++i:1}
      preLoadO=$arg
      preLoadA="${marg}"
      argv+=("$arg")
      argv+=("$preLoadA")

   elif [[ "$arg" == "--postload"* ]]; then
      marg=${@: ++i:1}
      postLoadO=$arg
      postLoadA="${marg}"
      argv+=("$arg")
      argv+=("$postLoadA")
   else
      argv+=("$arg")
   fi
done

exec ./myBootStrap "${argv[@]}"

The arguments must be passed as a quoted array. You had already built the array, but then you flattened it with a printf.

Note that an array slice is already an arithmetic context so you don't need $(()) inside it. I removed the separate i=i+1 (which concatenates characters so you get 1+1+1+1 after a while) and just put a pre-increment inside the array slice. The space before the first plus is required since :+ is significant inside an brace expansion. If you want, you can do the increment separately like this: ((i++)); marg=${@:i:1} (of course on separate lines if you prefer).

I changed your array appends to the much simpler += form and added quoting to them.

Upvotes: 4

Related Questions