Como usar o regex dentro do exec com o find?

4

É possível usar expressões regulares com base no resultado (nome do arquivo) dentro do argumento exec com find ? Eu quero ser capaz de "exec" com base em partes do argumento, como:

find . -name pattern -regex "foo (regex1) bar (Regex2)" -exec something $1 $2 ;

    
por Emre 31.07.2015 / 00:38

1 resposta

3

Você não pode usar grupos de captura do regexp no comando para executar. Se você usar find -regex para restringir as correspondências, você terá que fazer alguma correspondência extra no comando. Você pode fazer isso invocando um shell e usando suas próprias construções de correspondência de padrões. Por exemplo, se foo e bar forem sequências constantes e regex1 não corresponder a bar :

find … -exec sh -c '
  x=${0#foo}
  y=${x#*bar}
  x=${x%%bar*}
  something "$x" "$y"
' {} \;

Invocar um shell tem uma pequena sobrecarga. Você pode melhorar um pouco o desempenho invocando o shell em lotes.

find … -exec sh -c '
  for item do
    item=${item#foo}
    y=${item#*bar}
    x=${item%%bar*}
    something "$x" "$y"
  done
' sh {} +

Como você já fez alguma filtragem, talvez consiga fugir dos padrões de shell que correspondem a mais de regex1 e regex2 , mas, para os caminhos desse formulário específico, correspondem à mesma peça. Se foo e bar não puderem ser expressos com padrões de shell comuns, você poderá invocar ksh ou bash, que suportam padrões extras que são tão poderosos quanto expressões regulares: @(alter|native) , *(zero-or-more) , +(one-or-more) , ?(optional) e !(negated) . No bash , esses padrões precisam ser ativados com shopt -s extglob . Em ksh, eles estão disponíveis nativamente.

No bash, há uma construção de correspondência de expressão regular que você pode usar em condicionais : [[ $STRING =~ REGEXP ]] . O regexp é um ERE (como find -regextype posix-egrep ). (O Zsh tem um semelhante; o ksh tem =~ , mas não expõe os grupos de captura.) Os grupos de captura estão disponíveis por meio do array BASH_REMATCH .

find … -exec bash -c '
  for item do
    [[ item =~ foo(regex1)bar(regex2) ]]
    something "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
  done
' bash {} +

Uma abordagem alternativa é imprimir o resultado e filtre-o , depois chame xargs para invocar o programa. Organize para ter o primeiro e o segundo argumento como itens sucessivos e execute xargs -n 2 . Use bytes nulos como separadores para evitar o formato de cotação estranho de xargs ou use -d '\n' para usar a análise linha por linha estrita. Ferramentas GNU recentes, como sed, podem trabalhar com bytes nulos em vez de novas linhas para separar registros.

find … -print0 |
sed -z 's/^foo\(regex1\)bar\(regex2\)$/\x00/'
| xargs -n2 -0 something

Uma abordagem alternativa é eliminar e usar o recurso de globalização recursiva de ksh93, bash ou zsh: **/ corresponde a subdiretórios recursivamente. Isso não é possível para expressões de localização complexas envolvendo conectores booleanos, mas é o suficiente para a maioria dos casos. Por exemplo, no bash (observe que isso recorre a links simbólicos para diretórios, como find -L ):

shopt -s extglob globstar
for x in **/*bar*; do
  if [[ item =~ foo(regex1)bar(regex2) ]]; then
    something "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
  fi
done

No zsh:

for x in **/*bar*; do
  if [[ item =~ foo(regex1)bar(regex2) ]]; then
    something $match[1] $match[2]
  fi
done
    
por 31.07.2015 / 09:10