Escapando de meta caracteres em sequências regex básicas / estendidas de posix no grep

5

É possível escapar todos os meta-caracteres de uma string dentro de uma variável antes de passá-la para o grep? Eu sei que pergunta semelhante foi feita antes em SE ( aqui ) e também uma boa explicação aqui , mas eu estava apenas curioso sobre se é possível com padrão de regex posix básico / estendido em vez de padrão perl? (atualmente estou lendo a sintaxe perl regex para entender primeiro em vez de saltar para a solução)

Por que esse requisito: (Meta, não é necessário para resposta)

Eu estava tentando escrever um script pequeno para dividir arquivos grandes onde eu divido os arquivos para file_name.ext.000 , file_name.ext.001 ... etc. que funciona bem. Agora eu não gosto de dividir os arquivos que já estão divididos (ou seja, ter nomes de arquivos com extensão de 3 caracteres que são todos dígitos, e seu tamanho é igual ao tamanho do arquivo original. Agora, se eu usar uma expansão de shell simples como file_name.ext.* também combina arquivos com file_name.ext.ext2 e, portanto, o tamanho total descompassado e dividido ocorre mesmo que não haja necessidade de resplit. Portanto, eu verificaria apenas os arquivos com nome file_name.ext.### , em que ### são dígitos. encontre o tamanho do arquivo dessas partes com esta aparência:

FILE_SIZE_EXISTING=$( (find "$DESTINATION" -type f -regextype posix-extended -regex "^$DESTINATION/$FILE_BASENAME(\.[[:digit:]]{3})?$" -print0 | xargs -0 stat --printf="%s\n" 2>/dev/null || echo 0) | paste -sd+ | bc)

Isso funciona para nomes de arquivos simples. No entanto, não funciona se algum nome fantasia, por ex. contendo [] etc. Existe uma solução alternativa? Eu sou novo em scripts de shell e, portanto, não sei muito perl.

    
por mg007 22.02.2012 / 07:51

1 resposta

6

Como citar caracteres especiais (portáveis)

O snippet a seguir adiciona uma barra invertida antes de cada caractere especial em expressões regulares estendidas, usando sed para substituir qualquer ocorrência de um dos caracteres ][()\.^$?*+ por uma barra invertida seguida por esse caractere:

raw_string='test[string]\.wibble'
quoted_string=$(printf %s "$raw_string" | sed 's/[][()\.^$?*+]/\&/g')

Isso removerá as novas linhas iniciais em $raw_string ; Se isso for um problema, certifique-se de que a string não termine com uma nova linha, adicionando um caractere inerte no final e, em seguida, tire esse caractere.

quoted_string=$(printf %sa "$raw_string" | sed 's/[][()\.^$?*+]/\&/g')
quoted_string=${quoted_string%?}

Como citar caracteres especiais (em bash ou zsh)

Bash e zsh têm um recurso de substituição de padrão, que pode ser mais rápido se a string não for muito longa. É complicado aqui porque a substituição deve ser uma string, então cada personagem precisa ser substituído separadamente. Note que você deve escapar as barras invertidas primeiro.

quoted_string=${raw_string//\//\\}
for c in \[ \] \( \) \. \^ \$ \? \* \+; do
  quoted_string=${quoted_string//"$c"/"\$c"}
done

Como citar caracteres especiais (em ksh93)

A construção de substituição de string do Ksh é mais poderosa do que a versão diluída em bash e zsh. Suporta referências a grupos no padrão.

quoted_string=${raw_string//@([][()\.^$?*+])/\}

O que você realmente quer

Você não precisa de find aqui: os padrões de shell são suficientes para corresponder aos arquivos que terminam com três dígitos. Se não houver nenhum arquivo de peça, o padrão glob será deixado sem expansão. Há também uma maneira mais simples de adicionar os tamanhos de arquivo: em vez de usar stat (que existe em muitas variantes unix, mas tem uma sintaxe diferente em cada) e fazer pipelining complexo para somar os valores, você pode chamar wc -c arquivos, na maioria dos sistemas, wc examinará o tamanho do arquivo e não se incomodará em abrir o arquivo e ler os bytes).

set -- "$DESTINATION/$FILE_BASENAME".[0-9][0-9][0-9]
case $1 in
  *\]) # The glob was left intact, so no part exists
    do_split …;;
  *) # The glob was expanded, so at least one part exists
    FILE_SIZE_EXISTING=$(wc -c "$@" | sed -n '$s/[^0-9]//gp')
    if [ "$FILE_SIZE_EXISTING" -ne "$(wc -c <"$DESTINATION/$FILE_BASENAME")" ]; then
      do_split …
    fi

Observe que o seu teste no tamanho total não é muito confiável: se o arquivo foi alterado, mas permaneceu do mesmo tamanho, você acabará com peças obsoletas. Tudo bem se os arquivos nunca mudarem e o único risco é que as partes possam estar truncadas ou ausentes.

    
por 22.02.2012 / 14:44