首页 > 解决方案 > 在 Bash(和其他 shell 语言)中,如何在传递的参数中保留空格转义字符

问题描述

我有一个脚本,当直接调用它时,它可以与以下参数组合正常工作:

--path=/path/to/a\ folder\ with\ spaces
--path='/path/to/a folder with spaces'
--path="/path/to/a folder with spaces"

但我很难将参数从帮助脚本传递给它。

我尝试了很多事情,但在每种情况下,好像我的脚本收到以下内容:--path=/path/to/a folder with spaces这使我的脚本感到困惑,并且好像/path/to/a是路径(即 --path 的参数)并且folder with spaces是附加参数。

作为一个最小的示例,以下都不会产生所需的结果(将以下内容保存到所需的工作目录中./so_example.shchmod +x ./so_example.sh):

#!/usr/bin/env bash
set -x

printf "%s\n" "$@"

echo "$@"

echo -e "$@"

printf "%s\n" $@

echo $@

echo -e $@

以下列方式调用它时:

./so_example.sh --path=/path/to/a\ folder\ with\ spaces
./so_example.sh --path='/path/to/a folder with spaces'
./so_example.sh --path="/path/to/a folder with spaces"

我得到以下输出:

+ printf '%s\n' '--path=/path/to/a folder with spaces'
--path=/path/to/a folder with spaces
+ echo '--path=/path/to/a folder with spaces'
--path=/path/to/a folder with spaces
+ echo -e '--path=/path/to/a folder with spaces'
--path=/path/to/a folder with spaces
+ printf '%s\n' --path=/path/to/a folder with spaces
--path=/path/to/a
folder
with
spaces
+ echo --path=/path/to/a folder with spaces
--path=/path/to/a folder with spaces
+ echo -e --path=/path/to/a folder with spaces
--path=/path/to/a folder with spaces

我希望获得以下输出之一以获得正确的解决方案:

--path=/path/to/a\ folder\ with\ spaces`
--path='/path/to/a folder with spaces'`
--path="/path/to/a folder with spaces"`

标签: linuxbashshellparameter-passing

解决方案


您必须在单个处理中显式使用“$1”或迭代“$@”。如果您需要在单个 var 中保留 "$@" 的空格,请使用字符串操作来使用不间断空格进行掩码。

以下示例将使用 --path="PATTERN" 和 --path "PATTERN"。它从 "$@" 中删除所有路径并将其移动到单个 var $paths 中(以防 "$@" 包含您要保留的其他参数)

# remove options from positional parameters and read arguments
i=1
while [ $i -le $# ]
  do
    case "$1" in
      --path=*)
        test -a "$1"
        if [ $? != 0 ]
          then
            # allow nontrivial names in --path=PATTERN
            # (space, tab, newline, linefeed, formfeed, vertical tab)
            paths="${paths} ${1//[[:space:]]/[[:space:]]}"
            i=$(($i-1))
          else
            set -- "$@" "$1"
        fi
      ;;
      --path)
        test -a "$1"
        if [ $? != 0 ]
          then
            # allow nontrivial names in --path PATTERN
            # (space, tab, newline, linefeed, formfeed, vertical tab)
            paths="${paths} $1=${2//[[:space:]]/[[:space:]]}"
            shift
            i=$(($i-1))
          else
            set -- "$@" "$1"
        fi
      ;;
       *)
        set -- "$@" "$1"
      ;;
    esac
    shift
    i=$(($i+1))
done

# you can now pass the arguments to your command. the [[:space:]] will
# recognized as wildcard and will match all names containing spaces
<command> $paths

# however, if you want to display names (or if these paths not exist),
# you can convert it back (only spaces unfortunately)
for path in $paths
  do
    # revert back replacements [[:space:]] -> ' '
    path="${path//\[\[:space:]]/ }"

    # print everything before separator: ${VAR%%=*}
    # print the separator: (=)
    # print double quote: (\")
    # print everything after separator: ${VAR#*=}
    # print double quote: (\")

#    echo -e " ${path%%=*} = \" ${path#*=} \" "
    echo -e "${path%%=*}=\"${path#*=}\""
done

# other arguments are still available in positional parameters
for rest in "$@"
  do
    echo -e $rest
done

推荐阅读