Como dividir o arquivo de texto em registros pelo primeiro caractere não em branco usando awk e imprimir somente os registros que contenham string

1

Eu tenho um arquivo de texto com informações separadas em blocos. Eu gostaria de separar esses blocos em registros para que eu possa lidar com eles separadamente.

Este é o meu arquivo de texto (file.txt):

Alarm Stats:
com.android.calendar
     38ms running, 0 wakeups
     1 alarms: act=com.android.calendar.APPWIDGET_SCHEDULED_UPDATE dat=content://com.android.calendar typ=vnd.
com.android.providers.calendar
     2ms running, 2 wakeups
     2 alarms: act=com.android.providers.calendar.intent.CalendarProvider2 flg=0x14
android
     35563ms running, 11 wakeups
     1 alarms: act=android.intent.action.DATE_CHANGED flg=0x20000014
     1210 alarms: act=android.intent.action.TIME_TICK flg=0x40000014
     120 alarms: act=com.android.server.ThrottleManager.action.POLL flg=0x14
     1 alarms: act=android.net.wifi.DHCP_RENEW flg=0x14
     10 alarms: act=android.content.syncmanager.SYNC_ALARM flg=0x14
com.apollo.apollonetworkcheck
     1026ms running, 88 wakeups
     88 alarms: flg=0x14
com.android.phone
     4ms running, 0 wakeups
     2 alarms: act=com.android.phone.UPDATE_CALLER_INFO_CACHE flg=0x14

Usando "apollo" como minha string de pesquisa, a saída deve ser:

com.apollo.apollonetworkcheck
     1026ms running, 88 wakeups
     88 alarms: flg=0x14

Até agora eu tenho isso para minha linha de comando, mas não sei onde colocar a string de pesquisa "apollo". Além disso, não sei como especificar "não em branco" para o separador de registro.

$ awk 'BEGIN { RS = "^RTC" } { print $1 }' file.txt

Archemar, Wurtel, Steeldriver, Terdon, suas respostas são ótimas e adicionam muitas substâncias e resolução de recursos à questão. Eu votei em cima de cada resposta pelo grande valor. Aquele com "gawk", claro, foi mais preciso para a questão e objetivo de usar o comando unix / linux.

    
por L. D. James 12.09.2014 / 11:55

4 respostas

2

Sua idéia de usar um separador de registro regex é elegante, mas lembre-se de que o awk consumirá o texto correspondente, que no seu caso será o primeiro caractere que não seja espaço em branco do registro seguinte .

Se o seu sistema tiver a versão GNU do awk, você poderá acessar o RS mais recentemente correspondido através da variável RT , no entanto, você ainda precisará salvar o resultado para voltar a colocá-lo no início do seguinte registro. - talvez algo como

gawk 'BEGIN{RS="\n[^[:blank:]]"}; {lastRT=RT}; /apollo/ {$0=substr(lastRT,2)""$0; print}' file.txt

O substr(lastRT,2) apenas remove a nova linha da correspondência para que apenas o caractere que não é espaço em branco seja prefixado.

    
por 12.09.2014 / 14:36
3

Eu sempre jogo perl em tudo: -)

perl -ne 'if (/^\s/) { $x.=$_ }else{print $x if $x=~/apollo/; $x=$_} END {print $x if $x=~/apollo/}' file.txt

Editar: explicação do one-liner:

  • -n implica um loop sobre a entrada, não imprimindo por padrão no final de cada loop (-p é o mesmo, mas imprime a linha por padrão).
  • -e especifica uma expressão ou fragmento de código. Isso é executado dentro do loop implícito.
  • Eu uso uma variável $ x para armazenar em buffer o texto de entrada até que um "registro" completo seja encontrado.
  • / ^ \ s / corresponde aos espaços em branco no início de uma linha. Se isso for encontrado, a linha de entrada será adicionada ao buffer $ x. Caso contrário, o "registro" será concluído e inspecionado para a string de pesquisa "apollo". Se isso for encontrado, o registro será impresso. O buffer é limpo depois de ser manipulado.
  • A parte END {} é executada depois que o loop é concluído para manipular o caso do último registro na entrada.
por 12.09.2014 / 12:02
1

com um awk e um parâmetro

(file filter.awk)

BEGIN { p=0 ; } # no printing
 { if ( (substr($0,1,1) != " ") && (substr($0,1,1) != "\t" ) ) p=0 ; # if no blank stop printing
   if ( index($0,name) > 1 ) p=1 ; # pattern found ?
  if (p) print ;
 }
  • $ 0 é a linha inteira
  • substr ($ 0,1,1) é o primeiro caracter da linha

e o resultado

awk -v name=apollo -f filter.awk a.txt

    com.apollo.apollonetworkcheck
     1026ms running, 88 wakeups
     88 alarms: flg=0x14
    
por 12.09.2014 / 12:08
1

Eu também jogo perl em tudo, mas às vezes adiciono sed para o sabor:

$ sed  's/^\w/\n&/' file | perl -000ne 'print if /apollo/'
com.apollo.apollonetworkcheck
    1026ms running, 88 wakeups
    88 alarms: flg=0x14

O sed adicionará uma nova linha extra entre cada registro. Ele simplesmente procura linhas que começam com um caractere alfanumérico ( ^\w ) e substitui esse caractere por uma nova linha e seguido pelo próprio caractere ( \n& , o & significa "o que você acabou de corresponder"). O resultado é um arquivo onde os registros se parecem com parágrafos, eles têm uma linha em branco antes deles.

Agora podemos usar o "modo de parágrafo" do perl, ativado por -000 . Combinado com -n (ler registro de entrada por registro), isso nos permite ler o registro inteiro de uma só vez. Então, tudo o que precisamos fazer é imprimir a "linha" atual (registro) se ela corresponder ao padrão desejado. Nesse caso, apollo .

Não sei ao certo como é portável a notação \w . Se o seu sed não conseguir lidar com isso, use isso:

sed  's/^[^ \t]/\n&/' file | perl -000ne 'print if /apollo/'

Você também pode usar a mesma abordagem com awk :

$ sed  's/^[^ \t]/\n&/' file | awk -v RS="\n\n" '/apollo/'
com.apollo.apollonetworkcheck
     1026ms running, 88 wakeups
     88 alarms: flg=0x14
    
por 12.09.2014 / 14:39

Tags