Como analisar o arquivo de entrada uma vez e enviar para dois fluxos separados e exibir lado a lado?

3

Eu tenho o seguinte script:

#!/usr/bin/env bash
# Script to generate MD5 hash for each line.
[ $# -eq 0 ] && { echo "Usage: $0 file"; exit 1; }
file=$1
shopt -s expand_aliases
alias calc_md5='while read -r line; do md5sum <<<$line; done'
paste <(sort "$file" | uniq | calc_md5) <(sort "$file" | uniq)
times

que imprime a soma de verificação MD5 para cada linha, lado a lado, exatamente como eu preciso. Por exemplo:

$ ./md5_lines.sh file.dat
5c2ce561e1e263695dbd267271b86fb8  - line 1
83e7cfc83e3d1f45a48d6a2d32b84d69  - line 2
0f2d633163ca585e5fc47a510e60f1ff  - line 3
73bb3632fc91e9d1e1f7f0659da7ec5c  - line 4

O problema com o script acima é que ele precisa ler e analisar o arquivo duas vezes, para cada coluna / fluxo. Idealmente, gostaria de classificar e tornar todas as linhas exclusivas e usá-las como entrada apenas uma vez.

Como posso converter o script acima para analisar o arquivo apenas uma vez ( sort & uniq ), então redirecionar a saída para dois fluxos diferentes e exibir linhas lado a lado, para que ele possa funcionar mais rápido para o arquivos maiores?

Aqui está minha outra tentativa:

tee >(calc_md5) >(cat -) \
      < <(sort "$file" | uniq) \
      >/dev/null
times

mas imprime os fluxos separadamente (não lado a lado).

Idealmente, gostaria de usar paste , da mesma forma que tee , mas isso me dá o erro:

$ paste >(cat -) >(cat -) </etc/hosts
paste: /dev/fd/63: Permission denied
    
por kenorb 04.03.2018 / 15:15

3 respostas

8

Se você quiser exibir duas coisas lado a lado, pode usar printf para impressão formatada.

#!/bin/bash
sort "$1" | uniq | while read line; do
    md5=$(md5sum <<< "$line")
    printf "%s %s\n" "$md5" "$line"
done 
times
    
por 04.03.2018 / 15:46
5

Um par de abordagens Perl:

  1. Use o Perl para obter o md5sum

    $ perl -ne 'BEGIN{  
                    use Digest::MD5  qw(md5_hex)
                } 
                $k{$_}=md5_hex("$_"); 
                END{
                    print "$k{$_} - $_" for sort keys(%k)
                }' file
    5c2ce561e1e263695dbd267271b86fb8 - line 1
    83e7cfc83e3d1f45a48d6a2d32b84d69 - line 2
    0f2d633163ca585e5fc47a510e60f1ff - line 3
    73bb3632fc91e9d1e1f7f0659da7ec5c - line 4
    d82912361d84a675530f5e32aa6eeda1 - line 5
    

    E sim, este é um folheto:

    perl -ne 'BEGIN{use Digest::MD5  qw(md5_hex)} $k{$_}=md5_hex("$_"); END{print "$k{$_} - $_" for sort keys(%k)}' file
    

    Isso deve ser muito mais rápido do que fazer esse tipo de processamento no shell.

  2. Use uma chamada de sistema

    $ perl -lne 'chomp($md='md5sum <<<"$_"'); print "$md $_" if !$seen{$_}++' file
    83e7cfc83e3d1f45a48d6a2d32b84d69  - line 2
    0f2d633163ca585e5fc47a510e60f1ff  - line 3
    d82912361d84a675530f5e32aa6eeda1  - line 5
    73bb3632fc91e9d1e1f7f0659da7ec5c  - line 4
    5c2ce561e1e263695dbd267271b86fb8  - line 1
    
por 04.03.2018 / 16:22
2

Ir para um loop while read tem muitos dos problemas mencionados em Por que usar um loop de shell para processar texto é considerado uma prática ruim?

Aqui, eu usaria perl :

sort -u < "$file" | perl -MDigest::MD5=md5_hex -lpe '
  $_ = md5_hex($_) . " - " . $_'

Sua pergunta mais geral se parece com uma duplicata ou uma variação de tee + cat: use uma saída várias vezes e, em seguida, concatene os resultados

Note que não é porque duas linhas ordenam o mesmo (significando sort -u retém apenas uma) que elas serão idênticas e terão a mesma soma de verificação MD5. Você pode querer usar LC_ALL=C sort -u para ordenar e unicar para ser baseado em uma comparação byte-a-byte ao invés de strcoll() (também cuidado algumas implementações de sort poderiam bloquear na entrada de texto que noC % locale ainda incluiria linhas longas demais, linhas não delimitadas ou linhas contendo caracteres NUL).

    
por 05.03.2018 / 12:55