使用 shell 的“*” glob,但排除一个文件且不匹配目录?

使用 shell 的“*” glob,但排除一个文件且不匹配目录?

我有一个 makefile 规则,可以构建用于分发的 zip/tarbar。在配方中,它做了一些“增值”的事情,例如确保 CR/LF 正确,并确保打包前执行位正确。

该项目有大量文件,但要求如下:(1) 除GNUmakefileneed之外的所有文件CR/LF,(3)仅GNUmakefileneed LF,(3) 除GNUmakefileneed之外的所有文件a-x

菜谱是这样的:

.PHONY: convert
convert:
    chmod a-x *[^GNUmakefile*] TestData/*.dat TestVectors/*.txt
    unix2dos --keepdate --quiet *[^GNUmakefile*] TestData/*.dat TestVectors/*.txt
    dos2unix --keepdate --quiet GNUmakefile

我正在使用*并试图避免明确列出自助餐中的所有文件,因为有些文件并不明显,例如 IDE 特定文件。 (*[^<somefile>*]这是一个巧妙的技巧;我从从全局匹配中排除一种模式)。

问题是我正在匹配TestDataTestVectors时执行chmod a-x,所以我将自己排除在目录之外。

我需要改进一些东西,但我不知道如何改进。我想使用 shell 的“*” glob,但排除一个文件并且不匹配目录。

我应该如何进行?

答案1

我将通过使用 GNU Makefilter-outwildcard函数来解决这个问题。您的任务中唯一不能用它们完成的部分是过滤掉目录;这必须通过 shell 来完成。下面的代码未经测试,并假设 (a) 任何文件名中没有空格或 shell 元字符,(b)TestData/*.dat并且TestVectors/*.txt不需要检查目录。

NORM_TOPLEVEL := $(shell for f in $(filter-out GNUMakefile,$(wildcard *)); \
                   do [ -d "$$f" ] || printf '%s\n' "$$f"; done)
NORM_TESTDIRS := $(wildcard TestData/*.dat) $(wildcard TestVectors/*.txt)

convert:
    chmod a-x $(NORM_TOPLEVEL) $(NORM_TESTDIRS)
    unix2dos --keepdate --quiet $(NORM_TOPLEVEL) $(NORM_TESTDIRS)
    dos2unix --keepdate --quiet GNUmakefile

.PHONY: convert

答案2

只有zshglob 可以按类型选择文件,因此,假设 GNU make,您需要类似的东西:

SHELL = zsh
.SHELLFLAGS = -o extendedglob -c

test:
        echo ^GNUmakefile(^/)

^GNUmakefile(带有extendedglob)适用于除GNUmakefile.(^/)是一个全局限定符选择目录以外的任何类型的文件。另请(.)参阅文件类型常规的(不包括目录和所有其他非常规类型的文件,例如 fifo、符号链接、套接字...)这看起来更像您正在寻找的内容。添加Dglob 限定符 ( ^GNUmakefile(.D)) 以包含隐藏 ( Dot ) 文件,例如.gitignore.

请注意,扩展为以、、、、、、 、、、、或*[^GNUmakefile*]以外的字符结尾的非隐藏文件名列表。所以它确实会排除(因为它以 结尾),但也会排除or或。GNUmakefil*GNUmakefileefoo.afile.htmlbar.exe

要在不更改 shell 的情况下执行相同的操作,您需要诉诸类似的循环(此处相当于^GNUmakefile(.)):

test:
        set -- *; \
        for i do \
          [ -f "$$i" ] && \
            [ ! -L "$$i" ] && \
            [ "$$i" != GNUmakefile ] && \
            set -- "$$@" "$$i"; \
          shift; \
        done; \
        [ "$$#" -gt 0 ] && echo "$$@"

(替换set -- *set -- .* *以包含隐藏文件)。

find如果您不能保证以下内容的可用性,zsh最好可能会求助于shell glob:

test:
        find . ! -name . -prune ! -name '.*' ! -name GNUmakefile \
          -type f -exec echo {} +
        find TestData/. ! -name . -prune -name '*.dat' ! -name '.*' \
          -type f -exec echo {} +

(删除! -name '.*'以包含隐藏文件)。

答案3

如果您希望过滤更高级的条件,使用 Python 可以轻松扩展表达式。几乎所有地方都安装了Python。这有点冗长,但它确实有效。

示例生成文件:

all:
    chmod a-x $(shell python -c "import glob; import os.path; print(' '.join([x for x in glob.glob('TestData/*.dat') + glob.glob('TestVectors/*.txt') if not (os.path.isdir(x) or x == 'GNUmakefile')]))")

相关内容