Bash globbing do argumento multi-part

5

Abaixo estão alguns exemplos de um comando find que estou tentando executar. Eu estou procurando maneiras diferentes que eu possa usar o globbing para gerar (como um exemplo) um comando de busca com predicados reunidos.

Não funciona porque find precisa de um -nome antes de cada um e de um -o entre cada um.

find . -name \*.{sh,pl,sql}
#find . -name *.sh *.pl *.sql

Não funciona devido ao trailing -o . Poderia puxar um argumento garantido para falhar lá, mas não ideal. Além disso, meu atalho agora é maior que minha saída.

find . 'for X in {sh,pl,sql}; do echo -name \\*.$X -o ;done'
#find . -name \*.sh -o -name \*.pl -o -name \*.sql -o

Falha porque estão agrupados como um argumento ( find: unknown predicate '-name *.sh' ). Além disso, ainda exibindo uma falta de adesão com -o.

find . -name\ \*.{sh,pl,sql}

Funciona, mas não envolve globbing (re: não-resposta):

find .  -regex '.*\(sh\|pl\|sql\)'    
    
por Jeff Ferland 20.01.2012 / 17:09

3 respostas

3

A ideia de -false é a chave, IMHO. Estou apenas adicionando a ele:

find . -false $(echo "-o -name *."{sh,pl,sql})

Você acabou de citar tudo para fazer o bash repetir todo o padrão, incluindo -o -name , e depois "quebrar" o agrupamento feito pela citação retornando-o de uma subcamada. O problema com essa abordagem é que as cotações no padrão não funcionarão.

EDIT: Veja o comentário de Michał Šrajer para outra armadilha desta solução. Note que você não pode simplesmente colocar uma barra invertida antes da estrela: a substituição do comando retorna um * , que será expandido, ou um \* , que será passado como está para ser encontrado (!). Pelo menos, é assim que meu bash local funciona.

Isso é o melhor que posso fazer:

 (GLOBIGNORE='*:.*'; find . \( -false $(echo "-o -name *."{sh,pl,sql,xml}) \) -print)

Boa sorte:)

Btw, se você estiver não adicionando mais parâmetros a find , é claro que use apenas xargs ; Nesse caso, funciona perfeitamente, com citações e tudo:

echo "-o -name *."{sh,pl,sql} | xargs find . -false
    
por 20.01.2012 / 22:09
1

find não usa glob() . Usa fnmatch() . É por isso que os únicos meta caracteres disponíveis no predicado -name são * , ? , [ e ] .

Para resolver o% trailing -o , sugiro colocá-lo em colchetes e adicione um predicado extra -false :

( -name AAA -o -name BBB -o -name CCC -o -false )
    
por 20.01.2012 / 17:26
0

Estou assumindo que find é um exemplo aqui e você não está interessado em uma solução que permite combinar os padrões em um argumento find (que seria -regex ). Existem duas abordagens possíveis: você pode construir uma string e deixar a shell dividi-la em vários argumentos onde houver espaço em branco, ou você pode construir uma matriz.

Com a abordagem de construção de string, usando a substituição de comando, ela não é muito mais fácil do que a sua tentativa.

find . $(for X in sh pl sql; do echo "-name \*.$X" -o; done | sed 's/-o *$//')

Você também pode criar a string em um loop antes do comando find ; Eu acho que isso criaria um script mais claro¹.

find_options=
for x in sh pl sql; do
  find_options="$find_options -o -name \*.$x"
done
find_options=${find_options# -o}
find . $find_options

Se o seu shell suporta matrizes (ou seja, é bash ou ksh ou zsh), eu recomendo usá-las em vez disso, para que você não tenha que inserir citações extras.

find_options=()
for x in sh pl sql; do
  find_options+=(-o -name "*.$x")
done
unset "find_options[0]"
find . "${find_options[@]}"

¹ Na linha de comando, você não precisa se preocupar com a portabilidade e pode citar o mínimo necessário, já que não precisa se preocupar com a entrada do usuário. E você pode lidar com a repetição através de copiar e colar.

    
por 21.01.2012 / 14:21