在Shell脚本中引用命令行参数

时间:2020-03-05 18:45:29  来源:igfitidea点击:

以下shell脚本采用参数列表,将Unix路径转换为WINE / Windows路径,并在WINE下调用给定的可执行文件。

#! /bin/sh

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

EXEC=""
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'

注意:

  • 可执行文件的路径在第一个空格处被切断,即使它是单引号也是如此。
  • 最后一条路径中的文字" \ 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"问题似乎是echo的错误:"如果第一个操作数为-n,或者任何操作数包含反斜杠('')字符,则结果是实现定义的。" (" echo"的POSIX规范)

解决方案

回答

我们可以尝试使用\开头空格,如下所示:

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

我们也可以对\ t问题进行相同的处理,而将其替换为\ t。

回答

将$ CMD的最后一行替换为

葡萄酒'$ EXEC'$ ARGS

我们会注意到错误是" /home/chris/.wine/drive_c/Program"而不是" /home/chris/.wine/drive_c/Program"

单引号未正确插入,并且字符串被空格分隔。

回答

我确实要分配应该使用的CMD

评估$ CMD

而不是脚本最后一行中的$ CMD。这应该用路径中的空格解决问题,我不知道该怎么处理" \ t"问题。

回答

  • bashs数组是不可移植的,但是在shell中处理参数列表的唯一明智的方法
  • 参数数量以$ {#}为单位
  • 如果当前目录中的文件名以破折号开头,则脚本将发生错误的情况
  • 如果脚本的最后一行仅运行一个程序,并且退出时没有陷阱,则应执行它

考虑到这一点

#! /bin/bash

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

PROG="$(basename -- "##代码##")"

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=("")
shift

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

exec "${EXEC[@]}"