Grep para palavra tronco e imprimir somente palavra (e não linha)

0

Estou tentando imprimir apenas uma palavra correspondente encontrada por meio de uma expressão regular. Abaixo, quero todas as opções OPENSSL_NO_* presentes no código-fonte:

$ grep -IR OPENSSL_NO
fuzz/asn1.c:#ifndef OPENSSL_NO_RFC3779
fuzz/asn1.c:#ifndef OPENSSL_NO_RFC3779
fuzz/asn1.c:#ifndef OPENSSL_NO_CMS
fuzz/asn1.c:#ifndef OPENSSL_NO_DH
fuzz/asn1.c:#ifndef OPENSSL_NO_EC
fuzz/asn1.c:#ifndef OPENSSL_NO_RFC3779
fuzz/asn1.c:#ifndef OPENSSL_NO_OCSP
fuzz/asn1.c:#ifndef OPENSSL_NO_TS
fuzz/asn1.c:#ifndef OPENSSL_NO_DH
fuzz/asn1.c:#ifndef OPENSSL_NO_DSA
...

Quando tento cortar a saída apenas imprimindo a palavra completa:

$ grep -oIR "OPENSSL_NO*"
fuzz/asn1.c:OPENSSL_NO
fuzz/asn1.c:OPENSSL_NO
fuzz/asn1.c:OPENSSL_NO
fuzz/asn1.c:OPENSSL_NO
fuzz/asn1.c:OPENSSL_NO
...

E quando tento o awk, imprime toda a linha:

$ grep -IR OPENSSL_NO | awk '/OPENSSL_NO[_A-Z0-9_]/{ print $0 }'
fuzz/asn1.c:#ifndef OPENSSL_NO_RFC3779
fuzz/asn1.c:#ifndef OPENSSL_NO_RFC3779
fuzz/asn1.c:#ifndef OPENSSL_NO_CMS
fuzz/asn1.c:#ifndef OPENSSL_NO_DH
fuzz/asn1.c:#ifndef OPENSSL_NO_EC
...

E:

$ grep -IR OPENSSL_NO | awk '/\<OPENSSL_NO\>'
awk: line 1: runaway regular expression /\<OPENSSL_ ...

E:

$ grep -Eo -IR 'OPENSSL_NO_[A-Z0-9_]'
fuzz/asn1.c:OPENSSL_NO_R
fuzz/asn1.c:OPENSSL_NO_R
fuzz/asn1.c:OPENSSL_NO_C
fuzz/asn1.c:OPENSSL_NO_D
fuzz/asn1.c:OPENSSL_NO_E

E:

$ grep -IR OPENSSL_NO | sed -n 's/.*\(OPENSSL_NO\).*//p'
OPENSSL_NO
OPENSSL_NO
OPENSSL_NO
OPENSSL_NO
OPENSSL_NO
...

Como alguém combina uma palavra e só imprime a palavra?

Esta é aparentemente uma tarefa dolorosa, dada a quantidade de perguntas que existem sobre isso. Aqui estão várias perguntas que não consegui adaptar ao meu problema [simples?]:

por jww 29.01.2018 / 16:52

3 respostas

4

* em expressões regulares significa 0 ou mais do átomo anterior . Você está confundindo-o com o operador de curinga * , onde significa 0 ou mais caracteres .

OPENSSL_NO_* significa OPENSSL_NO seguido por 0 ou mais sublinhados.

Você deseja:

grep -o 'OPENSSL_NO_.*'

Em que . é o operador regexp para corresponder a um único caractere.

Ou:

grep -o 'OPENSSL_NO_[[:alnum:]]*'

para 0 ou mais caracteres alfanuméricos (em qualquer script alfabético suportado pela localidade).

As expressões regulares

estendidas (como em grep -E ) também têm + para 1 ou mais do átomo anterior . Com as expressões regulares Basic (sem -E), você pode usar \{1,\} .

Algumas implementações de grep também têm \w , o que significa qualquer caractere alfanumérico ou sublinhado , embora note que em algumas versões de algumas implementações, é limitado a A-Za-z0-9 one.

Em qualquer caso, observe que -o / -R não são opções padrão. POSIXly, você pode querer:

sed -n 's/.*\(OPENSSL_NO_[[:alnum:]_]\{1,\}\).*//p' < file

(permitindo apenas uma ocorrência por linha; se houver mais de um, apenas o mais à direita será exibido).

Isso não imprime os nomes dos arquivos. Para isso, você poderia usar awk :

find . -name '*.[hc]' -type f -exec awk 'match($0, /OPENSSL_NO_[[:alnum:]_]+/) {
  print FILENAME": "substr($0, RSTART, RLENGTH)}' {} +
    
por 29.01.2018 / 17:00
2

O operador * em expressões regulares significa "zero ou mais", então o grep fica perfeitamente satisfeito em satisfazer essa condição usando caracteres adicionais "zero".

Eu estenderei a expressão regular de alguma forma para que o grep seja forçado a incluir o resto do termo:

grep -o 'OPENSSL_NO_.*$' input

ou

grep -o 'OPENSSL_NO_.*\b' input

(em que, em ambos os casos, adicionei um sublinhado adicional).

    
por 29.01.2018 / 17:00
-1

Eu usei o comando awk para obter o mesmo

for i in {1..2}; do awk -v i="$i" '$i ~/^OPENSSL/ {print $i}' example.txt; done

saída

OPENSSL_NO_RFC3779
OPENSSL_NO_RFC3779
OPENSSL_NO_CMS
OPENSSL_NO_DH
OPENSSL_NO_EC
OPENSSL_NO_RFC3779
OPENSSL_NO_OCSP
OPENSSL_NO_TS
OPENSSL_NO_DH
OPENSSL_NO_DSA
    
por 29.01.2018 / 18:01