bash - 使用包含引号的字符串作为 bash 中命令的参数,无需额外转义

标签 bash variables quoting

背景

我想做的是在 find 命令中排除 git 存储库的所有子模块。我知道我可以排除像这样的单个目录:

find . -not -path './$submodule/*'

因此,我构建了一个命令来生成大量此类语句并存储它们:

EXCLUDES=$(for submodule in $(git submodule status | awk '{print $2}'); do
    echo -n "-not -path './$submodule/*' ";
done)

问题

但是当我运行 find 时。 $EXCLUDES,这不起作用。我怀疑这是因为我不理解 bash 引用策略。例如,假设(# 标记输出):

tree .
# .
# ├── bar
# │   └── baz.scala
# └── foo.scala

set -x
EXCLUDES="-not -path './bar/*'"
find . -type f $EXCLUDES
# + find . -not -path ''\''./bar/*'\''' <---- weird stuff
# foo.scala
# baz.scala

find . -type f -not -path './bar/*'
# + find . -type f -not -path './bar/*'
# foo.scala

我如何告诉 bash 不要做奇怪的引用事情(参见上面标记的行)?

编辑:@eddiem 建议使用 git ls-files,我将在这个具体案例中执行此操作。但我仍然对在一般情况下如何执行此操作感兴趣,在这种情况下,我有一个带引号的变量,并且希望将其用作命令的参数。

最佳答案

您注意到的“奇怪的东西”是因为 bash 只扩展 $EXCLUDES一次,替换为您存储在 EXCLUDES 中的值。它不会递归处理 EXCLUDES 的内容删除单引号,就像在命令行上指定带引号的字符串时一样。相反,bash 会对 $EXCLUDES 中的特殊字符进行转义。 ,假设您希望它们在那里:

-not -path './bar/*'

变成了

-not -path ''\''./bar/*'\'''
             ^^         ^^ escaped single quotes
           ^^             ^^ random empty strings I'm actually not sure about
               ^       ^ single quotes around the rest of your text.

因此,正如 @Jean-FrançoisFabre 所说,如果您省略 EXCLUDES=... 中的单引号,你不会得到奇怪的东西。

那为什么不是第一个 find按预期工作吗?因为 bash 扩展了 $EXCLUDES变成一个单词,即 argv 的单个元素被传递到 find .* 但是,find期望它的参数是单独的单词。结果,find没有达到您的预期。

据我所知,执行此类操作的最可靠方法是使用数组:

declare -a EXCLUDES    #make a new array
EXCLUDES+=("-not" "-path" './bar/*')
    # single-quotes       ^       ^ so we don't glob when creating the array

并且您可以多次重复 += 行以排除所需的内容。然后,使用这些:

find . -type f "${EXCLUDES[@]}"

"${name[@]}"形式,带有所有标点符号,将数组的每个元素扩展为一个单独的单词,但不会进一步扩展这些单词。所以./bar/*将保持原样,不会被通配。 (如果您确实想要通配, find . -type f ${EXCLUDES[@]} (不带 "" )将扩展数组的每个元素。)

编辑顺便说一下,要查看数组中的内容,请执行 set|grep EXCLUDES 。您将单独列出每个元素。您还可以echo "${EXCLUDES[@]}" ,但我发现它对于调试不太有用,因为它不显示索引。

* 请参阅 the man page 的“扩展”部分。 “参数扩展”,扩展以$开头的东西,无法更改命令行上的字数 — "$@" 除外和"${name[@]}" .

关于bash - 使用包含引号的字符串作为 bash 中命令的参数,无需额外转义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40245617/

相关文章:

linux - 确定性地从财富中生成输出

bash - 在awk中转换日期格式,行包含2个日期和一个ip

python - 如何更新python中的变量列表?

Bash 在使用反引号时提示 here-document 中的语法错误

bash - 如何在单引号字符串中转义单引号

bash - 使用适当的引用自动重写 bash 脚本的工具?

bash - 通过bash脚本在Postgres docker容器上执行查询

c - 是否可以在 C 中声明一个变量,其名称由用户在运行时指定?

java - 在 Tomcat 中设置环境变量 TESSDATA_PREFIX

bash - 为什么 shell 会忽略通过变量传递给它的参数中的引号字符?