我正在尝试使用带有文件模式的 git ls-files
来获取已过滤的跟踪文件列表。 (我知道我可以用 grep et.al 做下面的事情,但我很好奇为什么它不起作用)
#!/usr/bin/env bash
GIT_ROOT=$(git rev-parse --show-toplevel)
# only include stuff in src and include
SRC_PATTERNS="{include,src}/**/*.{hpp,cpp,tpp,h,c}"
# @brief Get list of files in git index with names relative to root
# @param $1 Any of the constraints git-ls-files knows (e.g. --cached)
function git_sources()
{
FILE_STRING=$(git -C $GIT_ROOT ls-files --full-name $1 $SRC_PATTERNS)
echo "$FILE_STRING"
}
我正在使用的 glob 在从命令行调用 git ls-files
命令时有效
git -C $GIT_ROOT ls-files --full-name --cached {include,src}/**/*.{hpp,cpp,tpp,h,c}
但在采购脚本后调用 git_sources
命令时则不然。
问题:如何正确引用 glob 模式,以便将其传递给子命令,就像直接从命令行调用一样?
最佳答案
这可能是因为 shell 如何扩展存储在变量中的 glob 字符串。无论您使用哪个引号,shell 都不会将包含 glob 字符串的命令扩展到您可以应用过滤器的结果文件中。
shell 将在展开一个未加引号的变量后进行 globbing,但是因为大括号展开是在变量展开之前完成的(引用 Shell Expansions),glob 将找不到任何匹配的文件并且 shell 会留下原义的 glob 字符串
当你运行时
git -C $GIT_ROOT ls-files --full-name --cached {include,src}/**/*.{hpp,cpp,tpp,h,c}
Shell 将 --cached
部分之后的 glob 字符串扩展为 file1..fileN
,具体取决于与您的 glob 定义匹配的文件数量,即 as
git -C $GIT_ROOT ls-files --full-name --cached file1 file2 ... fileN
但是当您的 glob 字符串被引用(从变量扩展)时,同样的事情不会发生,它被扩展为
git -C $GIT_ROOT ls-files --full-name --cached '{include,src}/**/*.{hpp,cpp,tpp,h,c}'
这是您未扩展的 glob 字符串。
推荐的方法是使用数组扩展 glob 定义的结果以生成文件名并将带引号的数组扩展作为参数传递。将您的代码更改为以下内容。
SRC_PATTERNS=({include,src}/**/*.{hpp,cpp,tpp,h,c})
现在该数组已经存储了与您的 glob 定义匹配的文件列表,我们只需将其传递给如下命令即可。 ${arr[@]}
是一个带引号的数组扩展,以确保文件名中的 shell-meta 字符不会导致名称被拆分
git -C "${GIT_ROOT}" ls-files --full-name --cached "${SRC_PATTERNS[@]}"
始终使用引号来扩展 shell 变量(除非您有充分的理由不这样做,在大多数情况下是没有的),并且在文件名中使用不带 _
的小写变量名。
关于bash - 如何将复杂的文件 glob 传递给子 shell?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56734145/