2

Given a shell command (let us call it "oldcommand"), is there a simple shell script newcommand.sh which will behave as oldcommand except that it will have a new "-location" option, and for example,

  newcommand -location path -nonfileoption1 x1 -fileoption2 x2 -fileoption3 x3  -nonfileoption4 x4

will execute

 oldcommand  -nonfileoption1 x1 -fileoption2 path/x2 -fileoption3 path/x3  -nonfileoption4 x4
2
  • 1
    How should newcommand know which arguments of oldcommand needed prefixing with path? You would have to write newcommand yourself (or get someone to write it for you).
    – roaima
    Dec 2, 2015 at 12:08
  • @roaima see Thomas Dickey's answer. Obviously we assume that we have at our disposal a (reasonably short) list of all the available options for oldcommand . Dec 2, 2015 at 12:19

3 Answers 3

4

If all you care about is an option at the beginning, that's a couple of lines of script. Parsing the option in an arbitrary place and passing it to the oldcommand requires several lines and some knowledge of which options have values, and which do not.

For the simple case,

#!/bin/sh
[ "$#" -gt 2 && "x$1" = "x-location" ] && cd $2 && shift 2 && exec oldcommand "$@"
newcommand "$@"

For the complicated case, this sounds like something that you could do with getopt (not portable). Further reading:

Per discussion with OP, here is the pattern which may be used to simplify the problem: options beginning with "`-fileopt" should have the location option's value prepended. So (no real error checking, and not really handling embedded special characters in the pathnames):

#!/bin/sh
PREP=
if [ "$#" -gt 2 ]
then
    PREP="$2/"
    shift 2
    ARGS=
    while [ "$#" != 0 ]
    do
        case "x$1" in
        (x-fileopt*)
            ARGS="$ARGS $1 ${PREP}$2"
            shift 2
            ;;
        (*)
            ARGS="$ARGS $1"
            shift 1
        esac
    done
    oldcommand $ARGS
else
    oldcommand "$@"
fi
6
  • Thanks for your help. I clarify that I do not want to cd during the execution, and I am interested in what you call the "hard" case . You say that the hard case is doable, could you point a reference/link to some code snippets doing similar jobs that might be a starting point for me ? Dec 2, 2015 at 9:56
  • How is the value for the -location option used? Dec 2, 2015 at 9:58
  • I thought I had already made that clear in my OP. The -location option is an absolute path, prepended to all the filename-type options of the command. Dec 2, 2015 at 10:02
  • I see - the pattern did not leap out at me. I'll suggest something for that. Dec 2, 2015 at 10:05
  • If "-fileoption" is not literally that, of course, the number of cases can get large (which was what I commented on initially). Dec 2, 2015 at 10:14
2

This smells a lot like an XY problem. If you want oldcommand to treat all relative file names as relative to a different directory, change the current directory!

(cd path && oldcommand  -nonfileoption1 x1 -fileoption2 x2 -fileoption3 x3  -nonfileoption4 x4)

The parentheses ensure that the directory change is local: the next command will be executed in the original directory.

If you want to package this in a script:

#!/bin/sh
cd "$1" && shift && "$@"

Usage:

run-in path oldcommand  -nonfileoption1 x1 -fileoption2 x2 -fileoption3 x3  -nonfileoption4 x4

For command line usage, though, a temporary directory change might be more convenient. It requires a smidgen more typing, but it allows filename completion to work out of the box. (You can make completion work for the wrapper script method, but you'll need to define a completion function for the run-in script.)

cd path
oldcommand  -nonfileoption1 x1 -fileoption2 x2 -fileoption3 x3  -nonfileoption4 x4
cd -

or

pushd path
oldcommand  -nonfileoption1 x1 -fileoption2 x2 -fileoption3 x3  -nonfileoption4 x4
popd

Compared to what you're asking, this makes all relative file names relative to path, not just the ones passed on the command line but any relative file name that oldcommand uses internally. This also assumes that oldcommand doesn't itself change to a different directory. Both assumptions are met by many commands.

1
  • You have a point. The assumption is also met by the example I was needing it for. Now I feel a little guilty for having somewhat wasted the other answerers' time. Dec 3, 2015 at 4:25
1
opt()
    case ${1##*:*}:${#2} in
    (--:*) ! x=$((x-2))  ;;
    (-nonfileoption:[!0]*) p=;;
    (-fileoption:[!0]*)    p=\$2/;;
    (*:0)  eval $"{$x"':?Bad option::arg: "$1::$2"}';;
    (*)    eval $"{$((x-1))"':?Bad option: "$1"}'
    esac

[ " -location" = " $1" ] && x=2 parm= &&
while  [ "$#" -ge "$((x+=2))" ]
do     eval 'opt  "${'"$((x-1))"'}" "${'"$x}\"" ||
          until [ "$x" -eq "$#" ] && break 2
          do    parm=$parm' "${'$((x+=1))}\"
          done
       parm=$parm' "${'$((x-1))'}"  "'$p\${$x}\"
done&& eval "set -- $parm"
7
  • Thank you for your solution. It'll take some time for me to decipher it ... Dec 2, 2015 at 13:49
  • @EwanDelanoy - any luck?
    – mikeserv
    Dec 2, 2015 at 17:20
  • Currently I cannot parse case $1:$2 in (the only syntax I know is case $myvariable in) and (-fileoption:?"${2#?}") Dec 3, 2015 at 4:36
  • @EwanDelanoy - case syntax isnt necessarily case $var in ... its case word in .... a shell word is one or more concatenated strings. like 'string1'\''string3' where the backslash escaped ' apostrophe is string2 - but all three make one word. and so $1:$2 is just case valueof$1:valueof$2 in all strung together. the (-option:?"${2#?}") pattern matches -option to $1 and the : to : and ? and "${2#?}" for a not-null "$2", see?
    – mikeserv
    Dec 3, 2015 at 4:49
  • @EwanDelanoy - its way better now. is it any easier to get?
    – mikeserv
    Dec 3, 2015 at 5:39

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.