【问题标题】:Quoting command-line arguments in shell scripts在 shell 脚本中引用命令行参数
【发布时间】:2010-09-07 08:36:04
【问题描述】:

以下 shell 脚本采用参数列表,将 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”转换为制表符。

显然,shell 没有按照我想要的方式解析引号。如何避免这些错误?

编辑:“\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 specification of echo)

【问题讨论】:

    标签: unix shell wine


    【解决方案1】:

    您可以尝试在空格前加上 \,如下所示:

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

    你也可以对你的 \t 问题做同样的事情 - 用 \\t 替换它。

    【讨论】:

    • 这两个路径都是完全有效的 Unix 路径作为脚本的输入。单引号应该消除转义空格的需要。而“\t”是由winepath 命令自动生成的(它将“/tmp”变成“Z:\tmp”)。
    【解决方案2】:

    将最后一行从 $CMD 替换为 just

    葡萄酒'$EXEC'$ARGS

    您会注意到错误是 ''/home/chris/.wine/drive_c/Program' 而不是 '/home/chris/.wine/drive_c/Program'

    单引号没有正确插入,字符串被空格分割。

    【讨论】:

      【解决方案3】:

      我确实想要分配给 CMD 的任务,你应该使用

      eval $CMD

      而不仅仅是在脚本的最后一行中的$CMD。这应该可以解决路径中空格的问题,我不知道如何处理“\t”问题。

      【讨论】:

        【解决方案4】:
        • 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[@]}"
        

        【讨论】:

        • 定义“不可移植”。我不介意需要 /bin/bash 而不是 /bin/sh,但我很犹豫是否需要最新版本。有人整理了一个 Unix shell 脚本可移植性矩阵(即,哪些功能在哪些版本中可用shell 在哪些平台上)?
        猜你喜欢
        • 2013-03-03
        • 1970-01-01
        • 2011-03-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多