Bash - 在将$@保存到变量中时保证空格安全

7

当循环遍历这样的变量时,我遇到了一个问题。我准备了两个示例来展示这个问题。

示例1:

#!/bin/bash
DIRS="$@"

for DIR in $DIRS; do
    echo "$DIR"
done

ex2:

#!/bin/bash
for DIR in "$@"; do
    echo "$DIR"
done

第二个例子正如预期(并且是必需的)。接下来进行一个快速测试:
$ ex1 "a b" "c"
a
b
c
$ ex2 "a b" "c"
a b
c

我想使用第一种方法的原因是,我希望能够将多个目录传递给程序或不传递任何目录以使用当前目录。像这样:

[ $# -eq 0 ] && DIRS=`pwd` || DIRS="$@"

那么,我如何让示例1具备安全空间性质呢?
3个回答

10

使用数组而不是简单的变量。

declare -a DIRS

DIRS=("$@")

for d in "${DIRS[@]}"
do echo "$d"
done

这将产生以下结果:

$ bash xx.sh a "b c" "d e f    g" h z
a
b c
d e f    g
h
z
$

啊啊啊,我尝试了很多次,而且我已经非常接近成功了。我只是忘记在 ${DIRS[@]} 周围加上引号了。谢谢。 - Frank
@Frank:数组符号有点烦人 - 每次使用它们我都得回到参考手册。我越来越好了,但是要覆盖伯恩Shell的多年经验还需要时间。 - Jonathan Leffler

4
为什么不使用默认的扩展功能?
for DIR in "${@:-$(pwd)}"; do
    echo $DIR
done

0
一种方法是用其他字符替换参数中的空格,然后在使用参数时再将空格替换回来:
dirs="${@/ /###}"
for dir in $dirs; do
    echo "${dir/###/ }"
done

这取决于您能否想出一些字符序列,您可以确信这些字符序列永远不会出现在真实的文件名中。

对于您具体的情况,您希望能够在提供显式目录列表和默认当前目录之间进行选择,更好的解决方案可能是使用一个函数:

do_something() {
    for dir in "$@"; do
        echo "$dir"
    done
}

if [ $# -eq 0 ]; then
    do_something .
else
    do_something "$@"
fi

或者可能是:

do_something() {
    echo "$1"
}

if [ $# -eq 0 ]; then
    do_something .
else
    for dir in "$@"; do
        do_something "$dir"
    done
fi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接