Como extrair sob o linux alguns grupos de captura usando linha de comando em uma forma php / preg?

2

Dado em linux environment existe um monte de pacotes para manipulação de strings (grep, awk, sed, ...), eu gostaria que um software extraísse um grupo de captura em uma sintaxe parecida com o php / preg.

Talvez o mais próximo seja grep -P , mas não entendo como funciona.

Coisas como cat file.txt | grep -P '/something="([\w]+)"/i' parecem não me dar apenas o conteúdo dentro do grupo de captura.

Alguém poderia me fornecer alguns exemplos de trabalho? Muitos por favor, com algumas variantes e limites explicados!

EDIT: Eu vi em algum lugar usado SED para fazer esse propósito, mas eu ainda estou um pouco confuso sobre isso é sintaxe.

    
por user3450548 23.03.2016 / 11:23

2 respostas

4
pcregrep -io1 'something="(\w+)"' myfile.txt

( -i para correspondência insensível a maiúsculas, -o1 para imprimir o primeiro grupo de captura).

O GNU grep suporta um -P (se construído com suporte a regex compatível com perl) e -o . No entanto, seu -o está limitado à impressão de todas as partes correspondentes. Você pode, no entanto, usar operadores de look-around perl para contornar isso:

grep -iPo '(?<=something=")\w+(?=")' myfile.txt

(isto é, um regexp que coincide com a sequência de caracteres do componente da palavra , desde que siga something=" e seja seguido por " ).

Ou com PCRE recente o suficiente:

grep -iPo 'something="\K\w+(?=")' myfile.txt

(onde \K redefine o início da string correspondida ).

Mas se você for usar regexps perl, é melhor usar perl :

perl -C -lne 'print for /something="(\w+)"/ig' myfile.txt

Com GNU ou BSD sed , para retornar apenas a correspondência mais à direita por linha:

sed -nE 's/.*something="(\w+)".*//pi' myfile.txt

Portável (como suporte de regex estendido e correspondência insensível a maiúsculas e minúsculas são extensões não padrão não suportadas por todas as implementações sed ):

sed -n 's/.*[sS][oO][mM][eE][tT][hH][iI][nN][gG]="\([[:alnum:]_]\{1,\}\)".*//p' myfile.txt

Aquele assume% maiúscula i é I . Isso significa que, em locais onde o% bac_de% é i , por exemplo, o comportamento será diferente da solução anterior.

Uma solução padrão / portátil que pode encontrar todas as ocorrências em uma linha:

awk '{while(match(tolower($0), /something="[[:alnum:]_]+"/)) {
    print substr($0, RSTART+11, RLENGTH-12)
    $0 = substr($0, RSTART+RLENGTH-1)}}' myfile.txt

Isso pode não funcionar corretamente se a entrada contiver texto cuja versão minúscula não tenha o mesmo tamanho (em número de caracteres).

Pegadinhas:

  • Haverá algumas variações entre todas essas soluções em que İ (e \w ) corresponde a localidades diferentes da C / POSIX. Em qualquer caso, deve pelo menos incluir sublinhado, todos os algarismos decimais e as letras do alfabeto latino (maiúsculas e minúsculas). Se você quiser apenas isso, corrija a localidade para C.
  • Como já foi mencionado, a correspondência insensível a maiúsculas e minúsculas é muito dependente do local. Se você se importa apenas com [[:alnum:]_] vs a-z letras inglesas, você pode consertar o local para C novamente.
  • O operador A-Z regexp, com implementações GNU de . , pelo menos, nunca corresponderá a seqüências de bytes que não fazem parte de um caractere válido. Em uma localidade UTF-8, por exemplo, isso significa que não corresponderá a caracteres de um conjunto de caracteres de um byte com o conjunto de 8 bits. Ou, em outras palavras, para que a solução sed funcione corretamente, o conjunto de caracteres usado no arquivo de entrada deve ser o mesmo que o da localidade do usuário.
  • Os utilitários sed , perl e GNU geralmente trabalham com linhas de qualquer tamanho e contêm qualquer valor de byte arbitrário (mas observe a advertência acima) e considerarão os dados extras após o último caractere de nova linha como uma linha extra. Outras implementações desses utilitários podem não.
  • Os padrões acima são combinados em cada linha na entrada. Isso significa que eles não podem corresponder a mais de uma linha de entrada. Não é um problema para um padrão como pcregrep que não pode abranger mais de uma linha, mas, no caso geral, se você quiser que seu padrão corresponda a um texto que pode abranger várias linhas como something="\w+" , será necessário para:

    • altera o tipo de registro em que você trabalha. something=".*?" , grep --null (GNU sed -z apenas), sed , perl -0 (GNU awk -v RS='awk' e versões recentes de mawk apenas) podem trabalhar em registros delimitados por NUL em vez de linhas (registros delimitados por nova linha ), GNU awk pode usar qualquer regexp como o separador de registro (com -v RS='regexp'), perl any byte value (with -0ooo ').
    • pcregrep tem um modo -M multiline para isso.
    • use o modo slurp de perl , em que toda a entrada é o único registro (com -0777 )

    Em seguida, para perl e pcre, lembre-se de que . não corresponderá a caracteres de nova linha, a menos que o sinalizador s esteja ativado, por exemplo, com pcregrep -Mio1 '(?s)something="(.*?)"' ou perl -C -l -0777 -ne 'print for /something="(.*?)"/gis'

  • Tenha em atenção que algumas versões de grep e pcregrep tiveram erros com -z ou -M , e os motores regexp em geral podem ter alguns limites incorporados na quantidade de esforço que podem colocar em correspondência com regexp.
por 23.03.2016 / 11:43
2

No linux você tem vários comandos e cada um tem recursos diferentes. - Seu trabalho é encontrar a ferramenta certa para o trabalho determinado. ;)

Você realmente não especificou um problema concreto, então eu preciso ficar em geral.

Talvez a maneira mais fácil seja usar o perl diretamente:

cat file.txt | perl -wne '/([\w]+)/i and print $1'

Leia também man grep para algumas opções do grep:

   -o, --only-matching
          Print only the matched (non-empty) parts of a matching line, with each such part on a separate output line.

Você pode usar, por exemplo:

cat file.txt | grep -o '\w*'

Mas o que é melhor depende do seu problema. Se você gosta de php, você pode até mesmo usar o php na linha de comando.

    
por 23.03.2016 / 11:48