如何将复杂的文件Glob传递给子Shell?

时间:2019-06-24 09:58:28

标签: bash git shell sh

我正在尝试将git ls-files与文件模式一起使用,以获取跟踪文件的过滤列表。 (我知道我可以对grep等人进行以下操作,但我很好奇为什么它不起作用)

#!/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"
}

从命令行调用git ls-files命令时,我正在使用的glob起作用

git -C $GIT_ROOT ls-files --full-name --cached {include,src}/**/*.{hpp,cpp,tpp,h,c}

但是在获取脚本后调用git_sources命令时不会。

问题:我如何正确地引用glob模式,以便将其传递给子命令,就像直接从命令行调用一样?

1 个答案:

答案 0 :(得分:4)

可能是因为shell如何扩展存储在变量中的glob字符串。不管您使用shell的哪个引号, not 都不会将包含glob字符串的命令扩展到生成的文件中,您可以在该文件上应用过滤器。

Shell将在扩展未引用的变量之后进行glob,但是由于大括号扩展是在变量扩展之前完成的(参考Shell Expansions),因此glob将找不到任何匹配的文件,并且shell会保留原义的glob字符串< / p>

跑步时

git -C $GIT_ROOT ls-files --full-name --cached {include,src}/**/*.{hpp,cpp,tpp,h,c}

shell根据与您的glob定义匹配的文件数(例如as)将--cached部分后的glob字符串扩展为file1..fileN

git -C $GIT_ROOT ls-files --full-name --cached file1 file2 ... fileN

但是当您的全局字符串用引号(从变量扩展)被引为

时,不是会发生同样的情况
git -C $GIT_ROOT ls-files --full-name --cached '{include,src}/**/*.{hpp,cpp,tpp,h,c}'

这是未扩展的全局字符串。

推荐的方法是使用数组扩展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[@]}"

始终使用引号扩展外壳变量(除非您有充分的理由,否则多数情况下不会这样做),并使用文件名中不含_的小写变量名。