tags:

views:

1656

answers:

6

The following shell script takes a list of arguments, turns Unix paths into WINE/Windows paths and invokes the given executable under WINE.

#! /bin/sh

if [ "${1+set}" != "set" ]
then 
  echo "Usage; winewrap EXEC [ARGS...]"
  exit 1
fi

EXEC="$1"
shift

ARGS=""

for p in "$@";
do
  if [ -e "$p" ]
  then
    p=$(winepath -w $p)
  fi
  ARGS="$ARGS '$p'"
done

CMD="wine '$EXEC' $ARGS"
echo $CMD
$CMD

However, there's something wrong with the quotation of command-line arguments.

$ winewrap '/home/chris/.wine/drive_c/Program Files/Microsoft Research/Z3-1.3.6/bin/z3.exe' -smt /tmp/smtlib3cee8b.smt
Executing: wine '/home/chris/.wine/drive_c/Program Files/Microsoft Research/Z3-1.3.6/bin/z3.exe' '-smt' 'Z: mp\smtlib3cee8b.smt'
wine: cannot find ''/home/chris/.wine/drive_c/Program'

Note that:

  1. The path to the executable is being chopped off at the first space, even though it is single-quoted.
  2. The literal "\t" in the last path is being transformed into a tab character.

Obviously, the quotations aren't being parsed the way I intended by the shell. How can I avoid these errors?

EDIT: The "\t" is being expanded through two levels of indirection: first, "$p" (and/or "$ARGS") is being expanded into Z:\tmp\smtlib3cee8b.smt; then, \t is being expanded into the tab character. This is (seemingly) equivalent to

Y='y\ty'
Z="z${Y}z"
echo $Z

which yields

zy\tyz

and not

zy  yz

UPDATE: eval "$CMD" does the trick. The "\t" problem seems to be echo's fault: "If the first operand is -n, or if any of the operands contain a backslash ( '\' ) character, the results are implementation-defined." (POSIX specification of echo)

A: 

You can try preceeding the spaces with \ like so:

/home/chris/.wine/drive_c/Program Files/Microsoft\ Research/Z3-1.3.6/bin/z3.exe

You can also do the same with your \t problem - replace it with \\t.

Kyle Cronin
A: 

@Kyle: Both of the paths are perfectly valid Unix paths as input to the script. The single quotes should eliminate the need for escaping spaces. And the "\t" is automatically generated by the winepath command (it turns "/tmp" into "Z:\tmp").

Chris Conway
A: 

replace the last line from $CMD to just

wine '$EXEC' $ARGS

You'll note that the error is ''/home/chris/.wine/drive_c/Program' and not '/home/chris/.wine/drive_c/Program'

The single quotes are not being interpolated properly, and the string is being split by spaces.

A: 

@tef: It does indeed work if I simply invoke wine '$EXEC' $ARGS without first assigning it to CMD. The question is why? And how can I make it work with the assignment?

Chris Conway
+1  A: 

I you do want to have the assignment to CMD you should use

eval $CMD

instead of just $CMD in the last line of your script. This should solve your problem with spaces in the paths, I don't know what to do about the "\t" problem.

WMR
+2  A: 
  • bash’s arrays are unportable but the only sane way to handle argument lists in shell
  • The number of arguments is in ${#}
  • Bad stuff will happen with your script if there are filenames starting with a dash in the current directory
  • If the last line of your script just runs a program, and there are no traps on exit, you should exec it

With that in mind

#! /bin/bash

# push ARRAY arg1 arg2 ...
# adds arg1, arg2, ... to the end of ARRAY
function push() {
    local ARRAY_NAME="${1}"
    shift
    for ARG in "${@}"; do
        eval "${ARRAY_NAME}[\${#${ARRAY_NAME}[@]}]=\${ARG}"
    done
}

PROG="$(basename -- "${0}")"

if (( ${#} < 1 )); then
  # Error messages should state the program name and go to stderr
  echo "${PROG}: Usage: winewrap EXEC [ARGS...]" 1>&2
  exit 1
fi

EXEC=("${1}")
shift

for p in "${@}"; do
  if [ -e "${p}" ]; then
    p="$(winepath -w -- "${p}")"
  fi
  push EXEC "${p}"
done

exec "${EXEC[@]}"
andrew
Define "unportable". I don't mind requiring /bin/bash instead of /bin/sh, but I"m hesitant to require a recent version. Has someone put together a Unix shell script portability matrix (i.e., which features are available in which versions of which shells on which platforms)?
Chris Conway