在 bash 中,如何创建仍然存在于我的文件系统上的已提交文件数组?

在 bash 中,如何创建仍然存在于我的文件系统上的已提交文件数组?

我正在使用 bash 脚本创建 Git 挂钩。我想对我提交的文件运行一个进程(linting)。我只想在文件实际存在时运行该进程(换句话说,不考虑在我的提交中删除的文件)。我试过这个

    CHANGED_FILES=$(git diff --cached --name-only | grep ".js$")
    echo "$CHANGED_FILES"
    
    if [ -n "$CHANGED_FILES" ]; then
      REAL_FILES=()
      for pathname in "${CHANGED_FILES[@]}"; do
        if [ -e "$pathname" ]; then
            REAL_FILES+=( "$pathname" )
        fi
      done

  echo "real files are ..."
  echo "$REAL_FILES"

但以上仅在单个文件发生更改时才有效。如果有两个或更多文件发生更改,则不会发生任何事情,我的输出看起来就像

    src/app/test1.js
    src/app/test2.js
    real files are ...

因此,当 CHANGED_FILES 数组包含 2 个或更多实际存在的文件时,不会为“REAL_FILES”保存任何内容。

我该如何解决上述问题?

答案1

您的脚本的问题在于 CHANGED_FILES 是标量变量,而不是数组。

尝试:

CHANGED_FILES=( $(git diff --cached --name-only | grep ".js$") )

即在命令替换周围添加括号$(git...)

请注意,这受通常的 shell 单词分割的影响,因此如果有任何机会 (*) 在任何路径名中可能存在空格/换行符/shell 元字符,您应该使用mapfile流程替代代替命令替换,可以选择同时使用和 的-zNUL 分隔符。例如:git diffgrep

mapfile -d '' -t CHANGED_FILES < \
  <(git diff -z --cached --name-only | grep -z '\.js$')

顺便说一句,mapfilereadarray在 bash 中是同义词。请参阅help mapfile了解详情。在 中kshreadarray只有 和mapfile不存在。

(*) 有总是有机会这样做,因为这些字符是 UNIX 文件系统上的有效文件名字符。路径名中唯一无效的字符是 NUL(路径名的文件名部分中唯一无效的字符是/和 NUL)。因此,您应该始终以防御性方式编写脚本,并假设某处总会至少存在一个这样令人讨厌的文件名。即始终使用该mapfile版本。


顺便说一句,如果你想打印 $REAL_FILES 数组的所有元素(而不仅仅是第一个元素),那么使用[@]下标或使用declare -p/ typeset -p(这更适合调试。并生成可以在 bash 中执行而无需引用 / 的输出)空白问题)。

例如,而不是这个:

echo "$REAL_FILES"

做这个:

echo "${REAL_FILES[@]}"

或这个:

typeset -p REAL_FILES

declarevstypeset是另一个bashvsksh差异。ksh 有readarraytypeset,然后 bash 从 ksh 复制它们并将它们重命名为mapfiledeclare并将 ksh 版本添加为它们的别名。我不知道为什么)

答案2

您不需要echo "$REAL_FILES"将数组连接成一个字符串:

echo "${REAL_FILES[*]}"

或者您可以执行每个元素的打印输出:

printf "%s\n" "${REAL_FILES[@]}"

相关内容