Цитирование аргументов командной строки в сценариях оболочки - PullRequest
3 голосов
/ 30 августа 2008

Следующий скрипт оболочки принимает список аргументов, превращает пути Unix в пути WINE / Windows и вызывает указанный исполняемый файл под 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

Однако что-то не так с цитатой аргументов командной строки.

$ 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'

Обратите внимание, что:

  1. Путь к исполняемому файлу обрезается в первом пробеле, даже если он заключен в одинарные кавычки.
  2. Буква "\ t" в последнем пути преобразуется в символ табуляции.

Очевидно, что цитаты разбираются не так, как я предполагал оболочкой. Как я могу избежать этих ошибок?

РЕДАКТИРОВАТЬ: "\ t" расширяется через два уровня косвенности: во-первых, "$p" (и / или "$ARGS") расширяется до Z:\tmp\smtlib3cee8b.smt; затем \t расширяется до символа табуляции. Это (на первый взгляд) эквивалентно

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

что дает

zy\tyz

и не

zy  yz

ОБНОВЛЕНИЕ: eval "$CMD" делает свое дело. Проблема «\t», похоже, является ошибкой эха: «Если первый операнд -n или любой из операндов содержит символ обратной косой черты ('\'), результаты определяются реализацией». ( POSIX спецификация echo)

Ответы [ 4 ]

3 голосов
/ 22 сентября 2008
  • массивы bash непереносимы, но единственный разумный способ обработки списков аргументов в оболочке
  • Количество аргументов в $ {#}
  • С вашим скриптом произойдет что-то плохое, если в текущем каталоге есть имена файлов, начинающиеся с тире
  • Если последняя строка вашего скрипта просто запускает программу, и при выходе нет ловушек, вы должны выполнить ее

Имея это в виду

#! /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[@]}"
1 голос
/ 03 сентября 2008

Если вы хотите назначить CMD, вы должны использовать

eval $CMD

вместо $CMD в последней строке вашего скрипта. Это должно решить вашу проблему с пробелами в путях, я не знаю, что делать с проблемой "\ t".

0 голосов
/ 30 августа 2008

заменить последнюю строку с $ CMD на

вино '$ EXEC' $ ARGS

Вы заметите, что ошибка '' /home/chris/.wine/drive_c/Program ', а не' /home/chris/.wine/drive_c/Program'

Одинарные кавычки не интерполируются должным образом, а строка разделяется пробелами.

0 голосов
/ 30 августа 2008

Вы можете попробовать поставить перед пробелами символ \ как:

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

Вы также можете сделать то же самое с вашей проблемой \ t - замените ее на \\ t.

...