背景
我想做的是在 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/