Manipulação de texto: Removendo valores x = $ input da lista classificada e generalizando

2

Meu arquivo de texto de entrada contém um cabeçalho de 1 linha e, em seguida, uma lista classificada de linhas contendo: x y com inteiros x, y de 64 bits separados por um espaço. O arquivo de entrada tem muitos milhões de linhas.

Agora, quero remover das linhas 2 para #end # qualquer linha que comece com um valor < = $ input. Isso remove a linha completa de cada partida. Minha solução deselegante até agora tem sido:

head -1 inputFile > inputFile2   # preserve header
lineNum=$( grep -n $input test | cut -f1 -d ':' ) # find line # match for $input
tail -n +$( $lineNum+1 ) inputFile >> inputFile2  # skip down the input until get to values > $input
rm inputFile
mv inputFile2 inputFile

Exemplo de inputFile

5066314878607:a1:a2:a3:a4:a5
1 27
3 27
7 27
11 27
13 27
17 27
...

[Divisão da GNU Tool] inputFile em inputFile-1 e inputFile-2 (2 núcleos, podem ser z núcleos)

inputFile-1:
5066314878607:a1:a2:a3:a4:a5
1 27
7 27
13 27

inputFile-2
5066314878607:a1:a2:a3:a4:a5
3 27
11 27
17 27

Agora o inputFile-1 processou e concluiu a linha até ' 7 27'. Do principal inputFile eu quero apenas as duas linhas seguintes: (Nota < = no split inputFile-1 MAS isso não é uma simples remoção de < = x do inputFile original devido à divisão round-robin)

1 27
7 27

Isso deixa inputFile com:

5066314878607:a1:a2:a3:a4:a5
3 27
11 27
13 27
17 27

Em execução no Ubuntu 16.04 atual, embora isso seja provavelmente o mesmo para qualquer distribuição Linux moderna.

Pergunta:

  • Meu código existente pode ser melhorado?  
  • Como faço para generalizar isso para lidar com muitos arquivos removidos separados?

Cada inputFile-x processado separadamente será processado sequencialmente. Eu só não sei como lidar com a remoção das linhas processadas do arquivo principal com a divisão round robin. Em particular, uma vez que este é executado em muitos computadores com velocidade diferente, então inputFile-1 pode ser processado para linha 300 enquanto inputFile-2 pode ser processado para a linha 500.

Para explicar a generalização para z núcleos, cada processamento separadamente.    inputFile é dividido em round robin dividido em inputFile-1 inputFile-2 inputFile-3 ... inputFile-z [isto é, split -n r / $ z, para 50 núcleos: split -n r / 50 inputFile]

Core1: inputFile-1 com (valores para as linhas 2 a # end #) < = $ input1 - > armazenar lista / matriz remove1. Agora, remova apenas os valores correspondentes de remove1 do inputFile original. Continue processando para cada núcleo.

    
por StackAbstraction 09.07.2016 / 19:13

2 respostas

1

Podemos evitar a leitura de todo o arquivo? Sim: porque está classificado, podemos fazer uma pesquisa binária para encontrar o byte da linha relevante: Pesquisa binária em um arquivo de texto ordenado e link

Podemos evitar o processamento da maioria das linhas? Sim, assim que encontrarmos a linha relevante, podemos simplesmente copiar o resto.

Com esse byte, você pode fazer um head do seu cabeçalho de 1 linha e um tail do byte encontrado.

    
por 10.07.2016 / 18:30
2

@SatoKatsura já respondeu sua primeira pergunta em um comentário: awk -v cutoff=299851915672 'FNR == 1 || $1+0 > cutoff+0' inputFile

É muito difícil interpretar o que você está fazendo em sua segunda pergunta (você pode atualizar sua pergunta com um algoritmo ou pseudo-código?), mas parece que você está querendo executar muitas instâncias (50?) de seu processo de uma só vez (um por núcleo da CPU no sistema). Em caso afirmativo, você começou corretamente dividindo o arquivo em 50 arquivos menores.

A peça que falta no quebra-cabeça é que você precisa usar o GNU parallel (ou, alternativamente, xargs com a opção -P ) para executar os processos em paralelo. Por exemplo:

find . -type f -name 'inputFile-*' -print0 |
    parallel -n 1 \
    awk -v cutoff=299851915672 \
      \'FNR == 1 \|\| \+0 > cutoff+0 {print \> FILENAME".out"}\'

(Veja as notas 1, 2 e 3 abaixo)

Por padrão,

parallel executará um processo por núcleo no sistema. Você pode substituir isso usando a opção -j para especificar o número de trabalhos simultâneos.

O script awk salva a saída de cada arquivo de entrada de um arquivo com o mesmo nome e uma extensão extra .out - por exemplo, inputFile-1 - > %código%. Para juntá-los todos juntos novamente em um arquivo grande, você pode usar inputFile-1.out :

cat inputFile*.out > complete.output.txt
rm -f *.out

NOTA 1: você precisa escapar de aspas e outros caracteres especiais (por exemplo, cat , | , $ , > , & e mais) com uma barra invertida na linha de comando a ser executada por% código%. É mais fácil salvar o script ; em um arquivo autônomo (com parallel como primeira linha), torná-lo executável com awk e executar esse script em paralelo.

NOTE2: isso provavelmente não fará exatamente o que você quer porque eu não tenho ideia do que você está realmente pedindo. É um exemplo geral de como processar vários arquivos em paralelo. O script #!/usr/bin/awk -f quase certamente terá que ser alterado para atender aos seus requisitos (incompreensíveis).

NOTA 3: Você pode achar que a economia de tempo em potencial de executar vários processos em paralelo é mais do que compensada pelo tempo necessário para dividir a entrada em vários arquivos e pela sobrecarga de iniciar uma nova instância de seu processo (por exemplo,% scriptchmod) para cada arquivo. Isso depende da natureza e tamanho dos arquivos e da natureza do processamento a ser executado em cada arquivo. Correr em paralelo nem sempre significa obter resultados mais rápidos. Ou você pode ter complicado demais o que está fazendo para que seja difícil de entender e / ou replicar com outros dados.

    
por 10.07.2016 / 06:19