Localizar comando que exclui caminhos listados em um arquivo

2

Eu preciso excluir um monte de caminhos de um comando find . Por exemplo:

find "$(pwd)" -not \( \
 -path "*/.git"\
 -o -path "*/.git/*"\
 -o -path "*/.vscode"\
 -o -path "*/.vscode/*"\
 -o -path "*/node_modules"\
 -o -path "*/node_modules/*"\
 -o -path "*/Image"\
 -o -path "*/Image/*"\
 -o -path "*/Rendered"\
 -o -path "*/Rendered/*"\
 -o -path "*/iNotebook"\
 -o -path "*/iNotebook/*"\
 -o -path "*/GeneratedTest"\
 -o -path "*/GeneratedTest/*"\
 -o -path "*/GeneratedOutput"\
 -o -path "*/GeneratedOutput/*"\
 -o -path "*/*_files" \) -type d

No entanto, quero ler esses caminhos de um arquivo de texto em vez de listá-los todos na linha de comando. Como posso fazer isso?

    
por Nikhil 13.09.2018 / 09:45

3 respostas

3

Construa uma matriz que você usa posteriormente em sua chamada para find . O script a seguir lê os padrões de caminho delimitados por nova linha de sua entrada padrão e chama find :

#!/bin/sh

set --

while IFS= read -r path; do
    set -- "$@" -o -path "$path"
done

shift   # remove initial "-o" from $@

find . -type d ! '(' "$@" ')'

Você executaria isso com

./script.sh <paths.txt

onde paths.txt pode parecer

*/.git
*/.git/*
*/.vscode
*/.vscode/*
*/node_modules
*/node_modules/*
*/Image
*/Image/*
*/Rendered
*/Rendered/*
*/iNotebook
*/iNotebook/*
*/GeneratedTest
*/GeneratedTest/*
*/GeneratedOutput
*/GeneratedOutput/*
*/*_files

Ou, desde que seus padrões de caminho sejam basicamente nomes de diretórios:

#!/bin/sh

set --

while IFS= read -r dirname; do
    set -- "$@" -o '(' -name "$dirname" -prune ')'
done

shift   # remove initial "-o" from $@

find . -type d ! '(' "$@" ')'

com o arquivo padrão contendo

.git
.vscode
node_modules
Image
Rendered
iNotebook
GeneratedTest
GeneratedOutput
*_files

Essa variante do código impediria que find caísse nos diretórios correspondentes aos padrões no arquivo, enquanto o primeiro script (assim como seu código) testaria os padrões -path em relação a todos os diretórios excluídos independentemente do fato de você não estar interessado em nada abaixo desses caminhos.

    
por 14.09.2018 / 18:36
2

Você pode usar grep e find ' -exec para filtrar arquivos em uma lista de caminhos, como expressões regulares ou sequências fixas. Adaptando seu exemplo, crie um arquivo chamado paths contendo

/.git$
/.git/
/.vscode$
/.vscode/
/node_modules$
/node_modules/
/Image$
/Image/
/Rendered$
/Rendered/
/iNotebook$
/iNotebook/
/GeneratedTest$
/GeneratedTest/
/GeneratedOutput$
/GeneratedOutput/
/.*_files$

Em seguida, execute

find /your/search/path -type d ! -exec sh -c "echo {} | grep -q -f paths" \; -print

Isso procura diretórios em /your/search/path e, para cada um que encontrar, usa grep para determinar se ele corresponde a um padrão em paths ; se não, imprime. Isto é pretendido como uma base para extensão; se você se importa apenas com os caminhos de diretórios que não correspondem aos padrões em um arquivo, e nenhum dos caminhos abrange várias linhas, você pode pós-processar a saída usando uma única invocação grep :

find /your/search/path -type d | grep -v -f paths

Se você realmente não está interessado em certos caminhos ( isto é, seus padrões sempre correspondem a um nome de diretório e então tudo sob aquele diretório), você pode tornar as coisas mais simples removendo:

find /your/search/path -type d \( -exec sh -c "echo {} | grep -q -f paths" \; -prune -o -print \)

com o seguinte conteúdo nos caminhos:

/.git$
/.vscode$
/node_modules$
/Image$
/Rendered$
/iNotebook$
/GeneratedTest$
/GeneratedOutput$
/.*_files$
    
por 14.09.2018 / 14:50
1

O que pode ser feito é construir o comando usando awk e passá-lo para find como variável em um script "wrapper" ou função shell

p=$( awk '{printf "-not -path %s ",$0}' "$1" )
find "$PWD"  $p -type d

E chame-o como ./find_wrapper.sh paths.txt , em que path.txt é a lista de caminhos citados.

'*/.git'
'*/.git/*'
'*/.vscode'
'*/.vscode/*'
'*/node_modules'
'*/node_modules/*'
'*/Image'
...

Por que isso é feito dessa maneira? A razão awk constrói uma linha inteira é porque não há razão para fazer isso no script - as continuações da linha \ são para tornar o comando mais organizado, mas funcionalmente não dá nenhuma vantagem. $p não é citada, pois na verdade queremos a divisão de palavras aqui. Caso contrário, find a vê como uma string gigante e não como flags e argumentos individuais. Quanto às aspas simples, isso é para evitar o efeito de glob entre aspas duplas.

Como alternativa, como pipeline

awk '{printf "-not -path %s ",$0}' "$1" | xargs -L 1  find "$PWD" -type d 
    
por 14.09.2018 / 18:01