Bash: passando chaves como argumentos para a função bash

3

Adoro usar o seguinte padrão para pesquisar em arquivos:

grep --color=auto -iRnHr --include={*.js,*.html,} --exclude-dir={release,dev,} "span" .

Eu gostaria, no entanto, de ter este embrulhado em um comando bash como este:

findinfiles {*.js,*.html,} {release,dev,} "span"  // syntax is just a guessing

Não consigo resolver o problema de passar esses tipos de chaves para uma função bash e usá-las com $1 , $2 e assim por diante. Quando eu uso o seguinte:

function findinfiles() {
    echo $1, $2, $3
}

Então:

me@pc ~> findinfiles {*.js,*.html,} {release,dev,} "span"
*.js, *.html, release

É claro que passar argumentos para o grep não funcionará dessa maneira. Parece que os argumentos são indexados, mas não devidamente agrupados.

Alguém pode me ensinar como lidar com esses tipos de argumentos?

    
por oleq 29.11.2012 / 13:01

2 respostas

4

Quando você executa findinfiles {*.js,*.html,} {release,dev,} "span" , eis o que acontece:

  1. O Bash analisa aspas e divide o comando em palavras: findinfiles , {*.js,*.html,} {release,dev,} span .
  2. O Bash expande as chaves, resultando na lista de palavras findinfiles , *.js , *.html , release , dev , span .
  3. O Bash expande os padrões curinga *.js e *.html para a lista de nomes de arquivos correspondentes. Se nenhum nome de arquivo corresponder a um padrão, ele será deixado sozinho.
  4. Bash consulta o nome findinfiles , descobre que é uma função e executa a função com os parâmetros fornecidos.

Você pode impedir que as chaves e curingas sejam expandidos na chamada de função, mas as chaves aparecerão literalmente nos argumentos, o que não é particularmente útil.

Sugiro alterar o formato da sua chamada de função. Em vez de usar chaves, use apenas vírgulas e divida manualmente os argumentos em vírgulas dentro da função.

findinfiles () {
  local patterns="$1" excludes="$2" pattern="$3"
  shift 3
  typeset -a cmd dirs
  if [[ $# -eq 0 ]]; then dirs=(.); else dirs=("$@"); fi
  cmd=(grep --color=auto -iRnHr)
  local IFS=,
  for x in $patterns; do
    cmd+=("--include=$x")
  done
  for x in $excludes; do
    cmd+=("--exclude-dir=$x")
  done
  "${cmd[@]}" "${dirs}"
}

Explicações:

  • Armazene os três primeiros parâmetros em variáveis locais. Quaisquer parâmetros extras são diretórios para pesquisar.
  • Defina dirs como a lista de parâmetros extras. Se não houver nenhum, use . (diretório atual).
  • Defina IFS como vírgula. Quando um script contém uma expansão de variável sem nome como $patterns e $excludes acima, o shell executa o seguinte:

    1. Substitua a variável pelo seu valor.
    2. Divida a variável em palavras separadas, sempre que contiver um caractere que esteja presente em IFS . Por padrão, IFS contém caracteres de espaço em branco (espaço, tabulação e nova linha).
    3. Trate cada palavra como um padrão curinga e expanda-a para a lista de arquivos correspondentes. Se não houver nenhum arquivo correspondente, deixe o padrão sozinho.

    (Para evitar essas etapas de expansão, use aspas duplas em torno da substituição de variável, como em patterns="$1" e assim por diante no script acima.)

  • A função cria a linha de comando para executar incrementalmente na variável de matriz cmd . No final, o comando é executado.

Como alternativa, você pode criar as seguintes configurações:

shopt -s extglob globstar
fif_excludes='release dev'
alias fif='grep --color=auto -inH $fif_excludes'

Executar comandos como

fif span **/*.@(js|html)

Explicações:

  • shopt -s extglob ativa o formato @(js|html) do padrão de caractere curinga, que corresponde a js ou html . (Esta opção ativa outros formulários padrão, veja o manual para detalhes).
  • shopt -s globstar ativa o padrão **/ que corresponde a subdiretórios em qualquer profundidade (isto é, realiza uma travessia recursiva).
  • Para alterar a lista de exclusões (que espero que não aconteça com frequência), modifique a variável fif_excludes .
por 30.11.2012 / 03:03
2

o bash expande {a,b,}1 para a1 b1 1 ..

Seu comando é expandido para grep --color=auto -iRnHr --include=*.js --include=*.html --include= --exclude-dir=release --exclude-dir=dev --exclude-dir= span (em um diretório sem nenhum arquivo js e html, caso contrário, seria uma inclusão para cada arquivo)

Você pode citar os parâmetros, mas isso provavelmente não é o ideal ... findinfiles "{*.js,*.html,}" "{release,dev,}" "span" (A globulação ainda é necessária)

Você deve poder expandi-lo adequadamente com um eval , como esta versão:

function findinfiles() {
    eval "grep --color=auto -iRnHr --include=$1 --exclude-dir=$2 '$3'"
}
    
por 29.11.2012 / 14:13