grep (/ sed / awk) intervalo de meses

2

Procurei soluções para isso, mas é difícil procurar a solução para um problema tão específico (muitas palavras para colocar no Google).

Então, eu tenho um arquivo com dados parecidos com isto (dados irrelevantes omitidos):

... ... 2014 Jan 01 ... ... ...
... ... 2014 Jan 02 ... ... ...
(...)
... ... 2014 Mar 01 ... ... ...
(...)

Podemos supor que os dados são classificados por data. Então, uma coisa que quero fazer é capturar as linhas que se ajustam a um intervalo de meses. Por exemplo, se eu tiver um intervalo de fevereiro a abril, quero ver todas as linhas com Feb , Mar e Apr na coluna do mês.

Para desespecificar o problema e abstrair um pouco o meu pedido, quero apenas obter na primeira linha as correspondências de um determinado padrão e capturar todas as linhas a partir de então até chegar à última ocorrência de outro padrão (e todas as linhas que correspondem a um determinado padrão são agrupadas).

Encontrei soluções para problemas semelhantes, e eles usam grep , sed ou awk . Não tenho certeza qual seria o melhor nesse caso.

Isso tudo está sendo feito em um script bash, então pensei em ter um array com todos os "códigos" do mês [ Jan , Feb , Mar , ...], e de alguma forma verificar se estou dentro do intervalo que quero, mas sinto que provavelmente há uma solução mais elegante para esse problema.

Editar: @jasonwryan Bem, eu estou realmente fornecendo o script de um intervalo, por isso './script.sh - month "Jan, abr"' classifica o arquivo de dados por mês, então deve ser capaz de grep o intervalo arbitrário (de janeiro a abril de este caso). Então, awk '/ Feb | Abr | May /' funciona para esse caso, mas se eu quiser de fevereiro a novembro, seria diferente (awk '/ fev | mar | abr | mai | jun | jul | ago | set | Nov / 'file). Então, seria difícil automatizar com base em um intervalo arbitrário. Além disso, deve haver uma linha para cada mês pelo menos uma vez (e provavelmente todos os dias também). Esqueceu de especificar isso.

    
por grish 08.02.2015 / 05:10

1 resposta

1

mrng(){ sed "$(set -f;unset IFS rng l;n=' 
';[ -n "$ZSH_VERSION" ] && emulate sh                
prng()  for m do rng=${r%%"$m"*}${r##*"$m"} _l=$((_l+1))
                 printf "\n\n%s\n/$pat/{\n\t:$l.$_l\n\tn" $m
                 printf "\n\n%s\n\t/$pat/b$l.$_l.0" $rng
                 printf "\n\tb$l.$_l\n\t:$l.$_l.0\n}"
        done                                        
pat=$(  printf %s "${1:-%m}$n"| sed -n 's/%/&&/g;l'|
        sed ":n$n\$!N;s/\\\n//;tn${n}s/\$$//"); shift
r=$(    locale -c LC_TIME|sed '4!d;y/;/ /')       
for m do case   $m      in      (-)     rng=$r  ;;
        (-*)    rng=${r%%"${m#-}"*}${m#-}       ;;
        (*-)    rng=${m%-}${r##*"${m%-}"}       ;;     
        (*-*)   rng=${m%%-*}${r##*"${m%%-*}"}   ;        
                case    $rng    in      (*${m##*-}*)     
                rng=${rng%%"${m##*-}"*}${m##*-}  ;;(*)   
                rng=$rng\ ${r%%"${m##*-}"*}${m##*-};;esac
        ;;esac; : $((l+=1))                                                
        prng    ${rng:="$m"}; unset rng                                    
done|   sed "   1d;s/.*\(...\)\(\n\)\(.*[^%]\(%%\)*\)%m//
                /./!{N;N$n};/\n/D"
);d"       
}

Esta é uma função de shell - você precisará adaptá-la em um script de shell se é assim que você planeja chamá-lo ou avaliá-lo no shell atual. Você pode chamá-lo assim:

mrng "$pat" Jan-Mar Jun Sep-Nov <infile

Ele também aceita intervalos abertos, como - para significar todos ou Mar- , de março a dezembro. E os argumentos não têm como intervalos - como acima, Jun está bem.

Na verdade, no entanto, ele não interpreta os nomes dos meses - ele coleta aqueles do locale utility (que é uma dependência) e trabalha com o que a localidade atual diz os nomes dos meses de 3 caracteres são.

Ele pode fazer intervalos aleatórios e, na verdade, quase todos eles realmente fazem fazer quebra, ou, talvez seja melhor dizer, acumular, de qualquer maneira.

Ele espera que seu primeiro argumento seja um padrão BRE compatível com sed - com a exceção de que, onde quer que você espere encontrar o nome do mês, você deve usar %m . Você também pode inserir vários %m s - o que você pode fazer se quiser apenas corresponder linhas como Mar...Mar - Jun...Jun . Provavelmente isso não é incrivelmente útil, mas talvez ...?

Embora pareça complicado, mais da metade disso é dedicado à análise de argumentos - o sed é relativamente simples, afinal. Por exemplo, se eu fizer:

mrng %m Dec-Jan

... ele gerará um script sed semelhante a:

/Dec/{
        :1.1
        n
        /Jan/b1.1.0
        /Feb/b1.1.0
        /Mar/b1.1.0
        /Apr/b1.1.0
        /May/b1.1.0
        /Jun/b1.1.0
        /Jul/b1.1.0
        /Aug/b1.1.0
        /Sep/b1.1.0
        /Oct/b1.1.0
        /Nov/b1.1.0
        b1.1
        :1.1.0
}
/Jan/{
        :1.2
        n
        /Feb/b1.2.0
        /Mar/b1.2.0
        /Apr/b1.2.0
        /May/b1.2.0
        /Jun/b1.2.0
        /Jul/b1.2.0
        /Aug/b1.2.0
        /Sep/b1.2.0
        /Oct/b1.2.0
        /Nov/b1.2.0
        /Dec/b1.2.0
        b1.2
        :1.2.0
};d

... que acaba sendo muito código, mas a maioria não é avaliada na maioria das vezes. Em uma linha típica, ele verifica se corresponde a Dec e, se não, se corresponde a Jan e, se não, a exclui da saída.

Mas uma vez que corresponda a um desses padrões, começará um loop de ramificação simples. Portanto, dado o exemplo acima, se uma linha corresponder a Dec, ela será impressa e sobrescrita com a linha de entrada n ext. Se a nova linha corresponder a qualquer mês mas Dez, sed b será ranqueado com o :1.1.0 label - o que significa que a linha ainda precisa ser avaliada em relação a Jan - onde obterá semelhante tratamento - mas não será avaliado em qualquer mês anterior Dez. Se não corresponder a nenhum mês, mas a Dez, então sed b terá até o rótulo :1.1 , que recebe a linha impresso e o n ext puxado e etc.

Se eu fizesse - , então seria uma função semelhante à anterior - cada uma contendo seu próprio rótulo : exclusivo - para cada mês dentro do intervalo. Isso significa que os argumentos da linha de comando têm efeito cumulativo. Alguns exemplos:

printf %s\n 'not a month' May 'not a month' 'also not a month' Apr |
m_rng %m Apr May

As impressões acima:

May
not a month
also not a month

Porque May vem antes de Apr na entrada, mas depois de Apr na linha de comando. No entanto, é uma heurística bastante bruta. A entrada é processada na ordem dos argumentos da linha de comando, mas assim que um ciclo completo é feito, o processamento recomeça, então ...

printf %s\n 'not a month' May 'not a month' 'also not a month' Jun Apr |
m_rng %m Apr May

... imprime ...

May
not a month
also not a month
Apr

Como o ciclo é interrompido em Jun , a linha é excluída e o processamento é iniciado novamente do topo com a próxima linha de entrada - Apr .

De qualquer forma, para o seu padrão, você deve usar:

mrng '^\([^ ]\{1,\}  *\)\{3\}%m' [month args]
    
por 08.02.2015 / 05:46