Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bourne/POSIX shell parameter splitting

Tags:

shell

unix

sh

Assume I have some utility that can take a number of options, each followed by a filename. E.g., I could call it as myutil, myutil -o somefile, myutil -p anotherfile, myutil -o somefile -p anotherfile, etc.... I want to write a wrapper POSIX shell script that is able to call myutil with arbitrary combinations of options (depending on some conditions internal to the wrapper script, which aren't relevant to this question).

I thought of doing something like:

#!/bin/sh
file1=somefile
file2=anotherfile

if [ somecriteria = true ]; then
  OPT1="-o $file1"
fi

if [ othercriteria = true ]; then
  OPT2="-p $file2"
fi

myutil $OPT1 $OPT2

This works great—as long as neither filename has spaces: Assuming both ifs are true, myutil gets $1 = [-o], $2 = [somefile], $3 = [-p], and $4 = [anotherfile]. However, if there are spaces, e.g., if file1="some file", $1 = [-o], $2 = [some], $3 = [file], etc. Of course, what I want is for $2 = [some file].

Putting another set of quotes around the filename in OPT1 and OPT2 doesn't help; e.g., if I change it to OPT1="-o \"$file1\"", that just gets me $2 = ["some] and $3=[file"]. And putting in quotes around $OPT1 and $OPT2 in the call to myutil doesn't work either: if I do that, $1 = [-o some file].

So, is there some trick to get this working, or some other approach that would do what I want? I'd like this to stick to standard shell features, so no bash-isms or ksh-isms, please :) See this for a description of what's in the standard.

like image 227
Dave Huang Avatar asked Jan 20 '23 16:01

Dave Huang


1 Answers

After messing with it more, I found another approach that seems to do what I want:

#!/bin/sh
file1="some file"
file2=anotherfile

if [ somecriteria = true ]; then
  OPT1="$file1"
fi

if [ othercriteria = true ]; then
  OPT2="$file2"
fi

myutil ${OPT1:+-o "$OPT1"} ${OPT2:+-p "$OPT2"}

The construct ${parameter:+word} will be substituted with word if ${parameter} is set; if it's not set, it goes away. So if $OPT1 is unset, ${OPT1:+-o "$OPT1"} disappears—notably, it doesn't turn into an empty string in argv. If $OPT1 is set to some file, the above expression is substituted with -o "some file", and myutil gets $1 = [-o], $2 = [some file] as I want.

Note that myutil ${OPT1:+-o} "$OPT1" ${OPT2:+-p} "$OPT2" does not do exactly what I want, because if $OPT1 is unset, the -o goes away, but "$OPT1" turns into an empty string—$1 = [], $2 = [-p], $3 = [anotherfile]

(Edited per Dennis's suggestion)

like image 171
Dave Huang Avatar answered Jan 30 '23 09:01

Dave Huang