Como passar o caracter curinga '*' para o parâmetro path do comando find através de uma variável no script?

8

Eu quero usar find para encontrar arquivos em um conjunto de pastas restritas por curingas, mas onde houver espaços no nome do caminho.

Na linha de comando, isso é fácil. Todos os exemplos a seguir funcionam.

find  te*/my\ files/more   -print
find  te*/'my files'/more  -print
find  te*/my' 'files/more  -print

Eles encontrarão arquivos, por exemplo, em terminal/my files/more e tepid/my files/more .

No entanto, preciso que isso seja parte de um script; o que eu preciso é algo assim:

SEARCH='te*/my\ files/more'
find ${SEARCH} -print

Infelizmente, não importa o que eu faça, não consigo misturar curingas e espaços em um comando find em um script. O exemplo acima retorna os seguintes erros (observe a duplicação inesperada da barra invertida):

find: ‘te*/my\’: No such file or directory
find: ‘files/more’: No such file or directory

Tentar usar aspas também falha.

SEARCH="te*/'my files'/more"
find ${SEARCH} -print

Isso retorna os seguintes erros, tendo ignorado o significado das aspas:

find: ‘te*/'my’: No such file or directory
find: ‘files'/more’: No such file or directory

Aqui está mais um exemplo.

SEARCH='te*/my files/more'
find ${SEARCH} -print

Como esperado:

find: ‘te*/my’: No such file or directory
find: ‘files/more’: No such file or directory

Todas as variações que tentei retornam um erro.

Eu tenho uma solução alternativa, que é potencialmente perigosa porque retorna muitas pastas. Eu converto todos os espaços em um ponto de interrogação (caractere curinga de caractere único) assim:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /?}       # Convert every space to a question mark.
find ${SEARCH} -print

Isso é o equivalente de:

find te*/my?files/more -print

Isso retorna não apenas as pastas corretas, mas também terse/myxfiles/more , o que não é esperado.

Como posso conseguir o que estou tentando fazer? O Google não me ajudou: (

    
por Paddy Landau 07.09.2014 / 18:12

3 respostas

5

O mesmo comando deve funcionar bem em um script:

#!/usr/bin/env bash
find  te*/my\ files/ -print

Se você precisar para tê-lo como uma variável, fica um pouco mais complexo:

#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print

AVISO:

Usar eval como esse não é seguro e pode resultar na execução de código arbitrário e possivelmente nocivo se os nomes dos arquivos puderem conter determinados caracteres. Veja FAQ bash 48 para detalhes.

É melhor passar o caminho como argumento:

#!/usr/bin/env bash
find "$@" -name "file*"

Outra abordagem é evitar find e usar os recursos e globs de globbing estendidos do bash:

#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done

A opção globstar bash permite usar ** para corresponder recursivamente:

globstar
      If set, the pattern ** used in a pathname expansion con‐
      text will match all files and zero or  more  directories
      and  subdirectories.  If the pattern is followed by a /,
      only directories and subdirectories match.

Para fazê-lo funcionar 100% como encontrar e incluir dotfiles (arquivos ocultos), use

#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done
shopt -s dotglob 

Você pode até echo diretamente sem o loop:

echo te*/my\ files/**
    
por terdon 21.11.2014 / 15:06
2

Que tal matrizes?

$ tree Desktop/ Documents/
Desktop/
└── my folder
    └── more
        └── file
Documents/
└── my folder
    ├── folder
    └── more

5 directories, 1 file
$ SEARCH=(D*/my\ folder)
$ find "${SEARCH[@]}" 
Desktop/my folder
Desktop/my folder/more
Desktop/my folder/more/file
Documents/my folder
Documents/my folder/more
Documents/my folder/folder

(*) expande-se em uma matriz de qualquer que corresponda ao curinga. E "${SEARCH[@]}" se expande em todos os elementos da matriz ( [@] ), com cada citação individualmente.

tardiamente, percebo que a própria descoberta deve ser capaz disso. Algo como:

find . -path 'D*/my folder/more/'
    
por muru 21.11.2014 / 11:30
0

Eu finalmente descobri a resposta.

Adicione uma barra invertida a todos os espaços:

SEARCH='te*/my files/more'
SEARCH=${SEARCH// /\ }

Neste momento, SEARCH contém te*/my\ files/more .

Em seguida, use eval .

eval find ${SEARCH} -print

É simples assim! Usar eval ignora a interpretação de que ${SEARCH} é de uma variável.

    
por Paddy Landau 21.11.2014 / 15:52