divide um arquivo com base em um conjunto predefinido de linhas

5

Eu quero dividir um arquivo de texto de acordo com um conjunto predefinido de linhas. Por exemplo. Eu tenho um arquivo

a
b
c
d
e
f

E então eu tenho os seguintes conjuntos de linhas (estas podem ser armazenadas, no entanto, é mais conveniente, em um arquivo, vários arquivos, ...).

1,2
3,6
5,4

Eu quero dividir meu arquivo para obter 3 arquivos como:

arquivo1

a
b

arquivo2

c
f

arquivo3

e
d
    
por ADJ 19.03.2015 / 17:04

7 respostas

3

Aqui está um script bash supondo que seu arquivo de entrada é chamado de infile e os intervalos são armazenados 1 por linha em um arquivo denominado splits:

i=1
for range in $(< splits); do
  sed -n "$(echo "$range" | cut -f1 -d, )p" infile > "file$i"
  sed -n "$(echo "$range" | cut -f2 -d, )p" infile >> "file$i"
  ((i++))
done

Isso simplesmente usa sed para imprimir as linhas especificadas pelos intervalos e salva cada resultado como um novo arquivo (os arquivos criados são denominados arquivo1 arquivo2 arquivo3 etc). Duas invocações de sed são usadas para preservar a ordem especificada das linhas.

Observe que não há verificação de formato ou erro feita por este script simples e arquivos existentes chamados, por exemplo, file1 será sobrescrito.

Uma alternativa simplificada (cortesia de @muru ) usando while read e permitindo que o bash divida os intervalos em vez de cortar:

i=1
while IFS=',' read n1 n2 
do
    sed -n "$n1 p; $n2 p" infile > "file$i"
    ((i++))
done < splits

Se a ordem das linhas nos arquivos de saída for importante (por exemplo, linhas 5,4! = 4,5), o bit sed precisará ser dividido em duas invocações separadas, semelhantes ao primeiro script.

    
por Pooping 19.03.2015 / 17:26
2

O seguinte script python fará a divisão:

#!/usr/bin/python3

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('file', type=argparse.FileType('r'))
parser.add_argument('lines', type=argparse.FileType('r'))

args = parser.parse_args()

file_lines = list(args.file)

for i, l in enumerate(args.lines):
    r = l.rstrip().split(',')
    with open('file{}'.format(i+1), 'w') as f:
        for k in r:
            try:
                f.write(file_lines[int(k)-1])
            except IndexError: # Ignore lines out of range
                pass

Simplesmente chame assim:

./split.py file lines

Onde <file> é o arquivo abcdef e <lines> o 1,2 ... intervalo de linhas (você pode até ter várias linhas como 1,6,3,18,5)

    
por Sylvain Pineau 19.03.2015 / 17:44
2

Aqui está uma maneira de fazer isso, no awk

awk -F, 'NR==FNR {for (i=1;i<=NF;i++) a[$i]=FNR; next;} {print $0 >> "outfile"a[FNR];}' index file

Ele lê o arquivo de índice e salva seu número de linha ( FNR ) em uma matriz indexada pela lista de valores na linha. Em seguida, ele lê o arquivo de entrada e usa o número da linha its para procurar o número do arquivo output para escrever cada linha.

    
por steeldriver 19.03.2015 / 19:55
2

Outra solução simplesmente awk é:)

awk -F, 'NR==FNR{ X[NR]=$0; next } {print X[$1] RS X[$2]>"out"FNR}' file lines

Explicação:

NR==FNR    - Execute next block for 1st file only (*file*)
X[NR]=$0   - Create an associative array with key as 'NR' (line number) and copy
             whole line ($0) into it as its content.
next       - Jump to reading the next row from *file* (1st file)

print X[$1] RS X[$2]   
           - Print those line from array X that its line-number is the same as 
             value of first field in *lines* file then print a new-line(RS) and 
             print the line that its line-number is the same as value of second 
             field in *lines* file again and redirect the result into out#
    
por αғsнιη 19.03.2015 / 20:13
2

Outra solução bash , assumindo input como entrada, pattern como padrão e output como saída:

#!/bin/bash
i=0 # set the output number to 0
while read row; do # for each line in file 'pattern' as $row
    columns=$(<<< $row tr ',' '\n') # store each line obtained by transforming ',' in '\n' inside $row in an array $columns
    for column in $columns; do # for each member in array $columns as $column
        sed -n "${column}p" input
    done > output$i # write column $column in 'input' to 'output$i'
    ((i++)) # increment the output number
done < pattern
    
por kos 19.03.2015 / 20:47
1

O script python abaixo também fará o trabalho:

#!/usr/bin/env python3
#--- set the paths below
f1 = "/path/to/predifined_rows.txt"; f2 = "/path/to/outtakes.txt"; save_to = "directory/to/save/the/outtakes"
#---

rows = [l.strip() for l in open(f1).readlines()]
outtakes = [eval(l.strip()) for l in open(f2).readlines()]
for i in range(len(outtakes)):
    s = ("\n").join([rows[n-1] for n in outtakes[i]])
    with open(save_to+"/"+str(i+1), "wt") as out:
           out.write(s)
  • Copie-o em um arquivo vazio, salve-o como outtake.py
  • No cabeçalho, defina o (s) caminho (s) para f1 , f2 e o diretório para salvar os arquivos em
  • Execute com o comando

    python3 /path/to/outtake.py
    

O que faz

  1. lê os números do segundo arquivo, lendo as linhas como uma lista de números
  2. para cada uma das linhas, ele coleta os itens correspondentes do primeiro arquivo (por índice) e os grava em arquivos numerados separadamente, no diretório definido em save_to
por Jacob Vlijm 19.03.2015 / 18:37
0

Você pode usar a ferramenta split . Um grupo de exemplos pode ser encontrado, e. aqui

No entanto, no seu caso, algo como

split -l 2 <inputfile>

irá criar um conjunto de arquivos com duas linhas chamadas algo como xaa, xab ...

    
por frlan 19.03.2015 / 17:08