在Bash(和其他shell语言)中,如何在传递的参数中保留空间转义字符

问题描述 投票:0回答:2

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

--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"`
linux bash shell parameter-passing
2个回答
0
投票

问题是你希望语法引号传递给你的脚本;他们不是。全部三个

--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)。在脚本中引用位置参数可以保留这些空间,但是您的脚本无法知道调用它的人是如何创建该值的。 (它可能是以你的脚本一无所知的方式创建的;考虑从dash -c 'printf "%s\n" "$1"' _ $'\t'运行的bash这样的命令.dash获取单个制表符作为其参数,但它不知道用于创建的$'...'语法bash的任何信息它。)


0
投票

你必须在单个处理中明确使用“$ 1”或迭代“$ @”。如果您需要在单个var中保留“$ @”的空格,请使用字符串操作来屏蔽不间断的空格。

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

# 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
© www.soinside.com 2019 - 2024. All rights reserved.