função BASH não está escapando caracteres de controle

4
Hey guys eu tenho uma função que eu estou usando para encontrar coisas, mas infelizmente sempre que eu passar um caractere de controle ( $intVal ou testing : etc) engasga. Eu queria saber qual foi a correção?

Eu posso entender que usar $ ou % ou : etc no grep sem escapar causa esse problema, mas desde que eu estou passando por referência eu não tenho certeza de como escapar ...

De qualquer forma, aqui está o código.

function ffind()
{
     if [ $1 ] ; then
         find -type f | grep -ir '$1' * | grep -v '.svn'
     else
         echo "'$1' is not a valid resource"
     fi
}

Exemplo (s):

$ ffind $intVal
'' is not a valid resource

$ ffind "testing :"
bash: [: testing: unary operator expected
'testing :' is not a valid resource
    
por ehime 31.08.2012 / 21:14

1 resposta

8

Primeiro exemplo

$ ffind $intVal
'' is not a valid resource

Isso não funciona porque $foo é a sintaxe das variáveis e você não tem uma variável chamada intVal set, portanto $intVal é traduzido para uma string vazia. Como a variável também é sem aspas, nenhum argumento é passado para ffind .

Para corrigir isso, escape do $ - ou \$intVal (barra invertida) ou '$intVal' (citação simples).

Se você realmente tiver uma variável chamada intVal , coloque duplo em vez disso - "$intVal" - isso será expanda o valor da variável, mas não a dividirá.

Note que não existe tal coisa como "passar por referência" no bash. Há apenas passagem por valor e (complicado) passo a passo.

Segundo exemplo

$ ffind "testing :"
bash: [: testing: unary operator expected
'testing :' is not a valid resource

Isso não funciona porque você esqueceu de colocar aspas em torno de $1 na linha if [ $1 ] , portanto, está sujeito a divisão de palavras e três argumentos são passados para o [ builtin:

  • " [ "
  • " testing "
  • " : "
  • " ] "

em vez dos dois esperados:

  • " [ "
  • " testing : "
  • " ] "

O Exemplo # 1 também é afetado por isso, pois [ $1 ] divide em (" [ ", " ] ") e não (" [ ", " ", " ] "). Exemplo # 1 funciona por acidente, no entanto, já que aparentemente [ ] é válido. (Eu não sabia disso ...)

Para corrigir esse problema, coloque aspas duplas em torno de $1 - [ "$1" ] .

Observação: Embora [ seja padrão, há também um operador [[ específico do bash, que na verdade tem regras de análise diferentes do resto do código - em particular, não dividir variáveis expandidas. No bash, [[ $1 ]] e [[ "$1" ]] são equivalentes, ao contrário de suas alternativas [ .

Mais bugs

Sua função também tem vários outros problemas que não são mostrados nos exemplos.

find -type f | grep -ir '$1' * | grep -v '.svn'

Nesta linha:

  1. A palavra '$1' tem aspas simples ao redor. Isso significa que o bash não expandirá seu conteúdo - você está realmente dizendo grep para procurar o regex $1 , e não o argumento da linha de comando.

    Para corrigir isso, use aspas duplas - "$1"

  2. O primeiro comando grep está sendo solicitado para pesquisar recursivamente o conteúdo de todos os arquivos no diretório atual ( -r e * curinga).

    Ao mesmo tempo, você está canalizando a saída de find -type f para grep - aparentemente tentando dizer grep para pesquisar os nomes de todos os arquivos.

    Isso não funcionará porque grep , como a maioria dos filtros, não lerá de stdin se receber um ou mais arquivos para pesquisar. Eu não sei o que você está tentando pesquisar - nomes de arquivos ou conteúdo de arquivos - então escolha um:

    • Para procurar apenas nomes de arquivos, mantenha o pipe, mas remova a especificação do arquivo:

       find -type f | grep -i "$1" | ...
      
    • Para pesquisar apenas o conteúdo do arquivo, remova o find| :

       grep -ir "$1" * | ...
      
    • É possível combinar ambos, explicitamente dando grep o arquivo "stdin":

       find -type f | grep -i "$1" - * | ...
      
       find -type f | grep -i "$1" /dev/stdin * | ...
      

      ( /dev/stdin funciona com todos os programas Linux, enquanto - é uma convenção usada por alguns programas, incluindo o grep.)

  3. No segundo comando grep , a regex de pesquisa é um pouco ampla demais. (Lembre-se de que é uma regex, não uma string fixa). .svn corresponderia a algo como " not-a-svn-file ".

    Para excluir o diretório .svn , use grep -v "/\.svn/" .

    Se pesquisar o arquivo conteúdo ( grep -ir ... ), é melhor livrar-se do comando grep -v e adicionar --exclude-dir=".svn" ao primeiro.

Você pode parar de ler neste ponto

Os itens abaixo são apenas uma boa prática nos scripts sh .

  1. A palavra-chave function é desnecessária: ffind() { ... é suficiente e funciona em todos os shells do POSIX (enquanto function ffind não seria ).

  2. Se um script, programa ou função falhar, ele deve retornar um status de "falha" para seu pai. Por convenção, os programas Unix consideram 0 como "sucesso" e qualquer outra coisa "falha" (embora haja exceções para o último).

    Para retornar um status explicitamente, use return <num> em uma função (ou exit <num> em um script independente):

    else
        echo "'$1' is not a valid resource" >&2
        return 1
    fi
    
  3. Da mesma forma, as mensagens de erro não devem ser misturadas com stdout normal, mas gravadas em stderr (fd # 2), usando o operador de redirecionamento >& (veja o exemplo acima). Dessa forma, você pode redirecionar a saída normal para um arquivo (por exemplo, ffind intVal > results.txt ) enquanto ainda apresenta erros na tela.

Código fixo

ffind()
{
     if [ "$1" ] ; then
         grep -ir --exclude-dir=".svn" "$1" .
     else
         echo "'$1' is not a valid resource" >&2
         return 1
     fi
}

Melhores ferramentas

ack afirma ser "melhor que o grep". A execução de ack "testing :" pesquisaria seu código-fonte e ignoraria automaticamente .svn e diretórios semelhantes.

    
por 31.08.2012 / 22:01