Aqui estão dois sed
loops mutuamente exclusivos:
sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn' <<""
YEAR MONTH DAY RES
1971 1 1 245
1971 1 2 587
...
1971 12 31 685
1971 1 1 245
1971 1 2 587
...
1971 12 31 685
1972 1 1 549
1972 1 2 746
...
1972 12 31 999
1972 1 1 933
1972 1 2 837
...
1972 12 31 343
YEAR MONTH DAY RES
1971 1 1 245
1971 1 2 587
...
1971 12 31 685
1972 1 1 549
1972 1 2 746
...
1972 12 31 999
Basicamente sed
tem dois estados - p
rint e eat . No primeiro estado - o estado p
rint - sed
automaticamente p
rints a cada linha de entrada, em seguida, verifica o padrão / 12 * 31 /
. Se o espaço de padrão atual fizer !
não corresponder, ele será d
eleted e sed
obterá a próxima linha de entrada e iniciará o script novamente a partir do topo - no comando p
rint, sem tentar executar nada a seguir o comando d
elete de todo.
Quando uma linha de entrada faz corresponder a / 12 * 31 /
, no entanto, sed
passa para a segunda metade do script - o loop eat . Primeiro, define uma ramificação :
label denominada n
; em seguida, sobrescreve o espaço de padrão atual com a linha de entrada n
ext e, em seguida, compara o espaço de padrão atual com o padrão correspondido //
. Como a linha que correspondia a ela antes foi substituída pela n
ext, a primeira iteração desse loop eat não corresponde e toda vez que ocorre !
not sed
b
classifica novamente para o rótulo :n
para obter a linha de entrada n
ext e mais uma vez para compará-la ao padrão //
da última correspondência.
Quando outra correspondência é finalmente feita - algumas linhas den
ext de 365% mais tarde - sed
faz -n
ot imprimi-la automaticamente quando completa seu script, puxa a próxima linha de entrada e inicia novamente a partir do topo em o comando p
rint em seu primeiro estado. Assim, cada estado de loop passará para o próximo na mesma chave e, no meio tempo, fará o mínimo possível para encontrar a próxima chave.
Observe que o script inteiro é concluído sem invocar uma única rotina de edição e que é necessário apenas compilar o regexp único. O autômato resultante é muito simples - ele entende apenas [123 ]
e [^123 ]
. Além disso, pelo menos metade das comparações provavelmente serão feitas sem nenhuma compilação, porque o único endereço referenciado no loop eat é o //
empty. Por isso, sed
pode concluir esse loop inteiramente com uma única chamada regexec()
por linha de entrada. sed
may faz similar para o loop p
rint também.
cronometrado
Eu estava curioso sobre como as várias respostas aqui podem ser executadas e, assim, criei minha própria tabela:
dash <<""
d=0 D=31 IFS=: set 1970 1
while case "$*:${d#$D}" in (*[!:]) ;;
($(($1^($1%4)|(d=0))):1:)
D=29 set $1 2;;
(*:1:) D=28 set $1 2;;
(*[3580]:)
D=30 set $1 $(($2+1));;
(*:) D=31 set $(($1+!(t<730||(t=0)))) $(($2%12+1))
esac
do printf '%-6d%-4d%-4d%d\n' "$@" $((d+=1)) $((t+=1))
done| head -n1000054 >/tmp/dates
dash <<<'' 6.62s user 6.95s system 166% cpu 8.156 total
Isso coloca um milhão de linhas + em /tmp/dates
e dobra a saída para cada um dos anos 1970 - 3338. O arquivo se parece com:
tail -n1465 </tmp/dates | head; echo; tail </tmp/dates
3336 12 27 728
3336 12 28 729
3336 12 29 730
3336 12 30 731
3336 12 31 732
3337 1 1 1
3337 1 2 2
3337 1 3 3
3337 1 4 4
3337 1 5 5
3338 12 22 721
3338 12 23 722
3338 12 24 723
3338 12 25 724
3338 12 26 725
3338 12 27 726
3338 12 28 727
3338 12 29 728
3338 12 30 729
3338 12 31 730
... algumas delas de qualquer maneira.
E então tentei os diferentes comandos:
for cmd in "sort -uVk1,3" \
"sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn'" \
"awk '"'{u=$1 $2 $3 $4;if (!a[u]++) print;}'\'
do eval "time ($cmd|wc -l)" </tmp/dates
done
500027
( sort -uVk1,3 | wc -l; ) \
1.85s user 0.11s system 280% cpu 0.698 total
500027
( sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn' | wc -l; ) \
0.64s user 0.09s system 110% cpu 0.659 total
500027
( awk '{u=$1 $2 $3 $4;if (!a[u]++) print;}' | wc -l; ) \
1.46s user 0.15s system 104% cpu 1.536 total
Os comandos sort
e sed
foram concluídos em menos da metade do tempo awk
- e esses resultados foram típicos. Eu corri várias vezes. Parece que todos os comandos estão escrevendo o número correto de linhas também - e então provavelmente todos trabalham.
sort
e sed
estavam razoavelmente bem no pescoço e no pescoço - com sed
geralmente à frente - para o tempo de conclusão de cada execução, mas sort
faz mais trabalho real para alcançar seus resultados do que qualquer um dos outros dois comandos. Ele está executando trabalhos paralelos para concluir sua tarefa e beneficia muito da minha CPU multi-core. awk
e sed
atrelam o único núcleo atribuído a eles durante todo o tempo que processam.
Os resultados aqui são de um padrão% GNU sed
, mas tentei outro. Na verdade, tentei todos os três comandos com outros binários, mas apenas o comando sed
realmente funcionou com as minhas ferramentas de herança. Os outros, como eu suponho devido à sintaxe não padrão, simplesmente desistem com erro antes de decolarem.
É bom usar a sintaxe padrão quando possível - você pode usar livremente implementações mais simples, aprimoradas e eficientes em muitos casos dessa maneira:
PATH=/usr/heirloom/bin/posix2001:$PATH; time ...
500027
( sed -ne'p;/ 12 * 31 /!d;:n' -e'n;//!bn' | wc -l; ) \
0.31s user 0.12s system 136% cpu 0.318 total