views:

464

answers:

4

I'm working with a utility (unison, but that's not the point) that accepts parameters like:

$ unison -path path1 -path path2 -path path3

I would like to write a sh script that I could run like this:

$ myscript path1 path2 path3

I'm hoping for a Posix compliant solution, but bash-specific would also be good.

I'm guessing it should be something like:

#!/bin/sh
unison ${*/ / -path }

But this doesn't work.

EDIT: OK, I think I got something:

#!/bin/bash
PARAMS=
for arg in "$@"
do
    PARAMS+=" -path '$arg'"
done
unison $PARAMS

The problems are this only works in bash, and I'm pretty sure there's a better way to quote the parameters.

+4  A: 

Unchecked, it could be as simple as:

exec unison -path $1 -path $2 -path $3

If you don't embed spaces in your path names, then you can deal with a variable number of arguments with:

arglist=""
for path in "$@"
do
    arglist="$arglist -path $path"
done
exec unison $arglist

If you have spaces in your path names, then you have to work a lot harder; I usually use a custom program called escape, which quotes arguments that need quoting, and eval:

arglist=""
for path in "$@"
do
    path=$(escape "$path")
    arglist="$arglist -path $path"
done
eval exec unison "$arglist"

I note that using Perl or Python would make handling arguments with spaces in them easier - but the question asks about shell.

It might also be feasible in Bash to use a shell array variable - build up the arguments into an array and pass the array as the arguments to the unison command.

Jonathan Leffler
+1  A: 

If you want a heavily bash-specific version, you can try

#! /bin/sh

eval eval exec \
  unison -path\\ \\\"{$(eval echo \\\"\\\${1..$#}\\\" | sed 's/ /,/g')}\\\"

If you remove all the triple-backslash-quoted characters, this becomes easier to understand, but I won't spoil the fun by explaining it :-)

The main complication is to handle file names with spaces. That accounts for the triple-backlash quoting and double eval.

Idelic
Upvoted for crazy one-liner
itsadok
+3  A: 

If you use Bash's arrays, all your quoting problems go away.

#!/bin/bash
args=()
for i in "$@"; do
    # With Bash >= 3:
    args+=(-path "$i")
    # +=() doesn't work in Bash 2
    # args=("${args[@]}" -path "$i")
done
exec unison "${args[@]}"
ephemient
+1 for arrays instead of messy and fragile quoting/escapes
Gordon Davisson
Ditto @Gordon, plus thanks for including the bit about Bash 2 compatibility!
Ed Brannin
+2  A: 

In Bash, you can use "${@/#/-path }" which will replace the beginning of each positional parameter with "-path ". To represent the end of the string use % instead of #.

Here is a simple demo script using sed and repeated -e options. (Of course there are more efficient ways to use sed.)

#!/bin/bash
echo "Resulting arguments: ${@/#/-e }"
sed "${@/#/-e }"

And running it like this:

$ echo abc | demo s/a/A/ s/b/B/

We get:

Resulting arguments: -e s/a/A/ -e s/b/B/
ABc
Dennis Williamson