Цитирование аргументов командной строки в сценариях командной оболочки

StackOverflow https://stackoverflow.com/questions/36109

  •  09-06-2019
  •  | 
  •  

Вопрос

Следующий сценарий оболочки принимает список аргументов, преобразует пути 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" расширяется за счет двух уровней косвенности:Первый, "$p" (и/или "$ARGS") расширяется на Z:\tmp\smtlib3cee8b.smt;тогда, \t расширяется до символа табуляции.Это (по-видимому) эквивалентно

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

что приводит к

zy\tyz

и нет

zy  yz

Обновить: eval "$CMD" делает свое дело.Тот самый "\t" похоже, в проблеме виновато echo:"Если первый операнд равен -n или если какой-либо из операндов содержит символ обратной косой черты ( '\' ), результаты определяются реализацией". (Спецификация POSIX для echo)

Это было полезно?

Решение

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

eval $ CMD

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

Другие советы

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

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

#! /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[@]}"

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

вино '$ EXEC' $ ARGS

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top