Use o shell “*” glob, mas exclua um arquivo e não corresponda a diretórios?

5

Eu tenho uma regra de makefile que cria um zip / tarbar para distribuição. Na receita, ele faz algumas coisas de "valor agregado", como garantir que os CR / LF estejam corretos e garantir que os bits de execução estejam corretos antes do empacotamento.

O projeto tem um buffet de arquivos, mas aqui estão os requisitos: (1) todos os arquivos, exceto GNUmakefile need CR/LF , (3) GNUmakefile precisa LF only, (3) todos os arquivos, exceto GNUmakefile precisa de a-x .

Veja como a receita se parece:

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

Estou usando * e tentando evitar listar explicitamente todos os arquivos no buffet porque alguns não são óbvios, como arquivos específicos do IDE. ( *[^<somefile>*] é um truque legal; obtive isso de Exclui um padrão da correspondência glob ).

O problema é que eu estou combinando TestData e TestVectors ao executar chmod a-x , então eu me excluo dos diretórios.

Preciso refinar as coisas, mas não sei como. Eu quero usar o "*" glob do shell, mas excluir um arquivo e não corresponder aos diretórios.

Como devo proceder?

    
por jww 05.10.2015 / 12:07

3 respostas

2

Apenas zsh tem globs onde você pode selecionar arquivos por tipo, portanto, assumindo o GNU make , você precisa de algo como:

SHELL = zsh
.SHELLFLAGS = -o extendedglob -c

test:
        echo ^GNUmakefile(^/)

^GNUmakefile (com extendedglob ) é para arquivos não ocultos diferentes de GNUmakefile . (^/) é um qualificador glob que seleciona arquivos de qualquer tipo diferente de diretório. Veja também (.) para arquivos do tipo regular (exclui diretórios e todos os outros tipos não regulares de arquivos como fifos, symlinks, sockets ...) que parecem mais com o que você está procurando. Adicione o qualificador D glob ( ^GNUmakefile(.D) ) para incluir arquivos ocultos ( D ot) como .gitignore .

Observe que *[^GNUmakefile*] é expandido para a lista de nomes de arquivos não ocultos que terminam em um caractere diferente de G , N , U , m , a , k , e , f , i , l ou * . Então, de fato, excluirá GNUmakefile (já que termina em e ), mas também foo.a ou file.html ou bar.exe .

Para fazer o mesmo sem alterar o shell, você precisaria recorrer a um loop como (aqui para o equivalente de ^GNUmakefile(.) ):

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

(substitua set -- * por set -- .* * para incluir arquivos ocultos).

Melhor provavelmente seria recorrer a find em vez de shell globs se você não puder garantir a disponibilidade de zsh :

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

(remova ! -name '.*' para incluir arquivos ocultos).

    
por 05.10.2015 / 12:17
2

Eu resolveria esse problema usando as funções filter-out e wildcard do GNU Make. A única parte da sua tarefa que você não pode fazer com eles é filtrar diretórios; isso tem que ser feito através do shell. O código abaixo não foi testado e assume (a) nenhum metacaractere de espaço em branco ou shell em qualquer nome de arquivo, (b) TestData/*.dat e TestVectors/*.txt não precisam ser verificados para diretórios.

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
    
por 05.10.2015 / 17:52
0

O uso do Python facilita a expansão da expressão se você deseja filtrar por critérios mais avançados. O Python está instalado em quase todos os lugares. É um pouco detalhado, mas funciona.

Exemplo de Makefile:

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')]))")
    
por 05.10.2015 / 13:47