Como posso repetir o conteúdo de um arquivo n vezes?

18

Estou tentando comparar as duas maneiras diferentes de processar um arquivo. Eu tenho uma pequena quantidade de dados de entrada, mas para obter boas comparações, preciso repetir os testes várias vezes.

Em vez de apenas repetir os testes, eu gostaria de duplicar os dados de entrada várias vezes (por exemplo, 1000) para que um arquivo de 3 linhas se torne 3000 linhas e eu possa executar um teste muito mais satisfatório.

Estou passando os dados de entrada por meio de um nome de arquivo:

mycommand input-data.txt
    
por Oli 08.09.2014 / 12:39

8 respostas

21

Você não precisa de input-duplicated.txt .

Tente:

mycommand <(perl -0777pe '$_=$_ x 1000' input-data.txt)

Explicação

  • 0777 : -0 sets define o separador de registro de entrada (variável especial perl $/ , que é uma nova linha por padrão). Configurar isso para um valor maior que 0400 fará com que Perl use o arquivo de entrada inteiro na memória.
  • pe : o -p significa "imprimir cada linha de entrada depois de aplicar o script dado por -e a ele".
  • $_=$_ x 1000 : $_ é a linha de entrada atual. Como estamos lendo o arquivo inteiro de uma vez por causa de -0700 , isso significa o arquivo inteiro. O x 1000 resultará em 1000 cópias de todo o arquivo sendo impresso.
por cuonglm 08.09.2014 / 13:36
10

Eu estava pensando originalmente que eu teria que gerar um arquivo secundário, mas eu poderia apenas fazer o loop do arquivo original no Bash e usar algum redirecionamento para fazê-lo aparecer como um arquivo.

Existem provavelmente uma dúzia de maneiras diferentes de fazer o loop, mas aqui estão quatro:

mycommand <( seq 1000 | xargs -i -- cat input-data.txt )
mycommand <( for _ in {1..1000}; do cat input-data.txt; done )
mycommand <((for _ in {1..1000}; do echo input-data.txt; done) | xargs cat )
mycommand <(awk '{for(i=0; i<1000; i++)print}' input-data.txt)  #*

O terceiro método lá é improvisado a partir do comentário de maru abaixo e constrói uma grande lista de nomes de arquivos para cat. xargs dividirá isso em tantos argumentos quanto o sistema permitir. É muito mais rápido que n gatos separados.

A maneira awk (inspirada na resposta de terdon ) é provavelmente a mais otimizada, mas duplica cada linha em um Tempo. Isso pode ou não se adequar a um aplicativo específico, mas é muito rápido e eficiente.

Mas isso está gerando na hora. A produção de Bash provavelmente será muito mais lenta do que algo pode ler, portanto você deve gerar um novo arquivo para teste. Felizmente, essa é apenas uma extensão muito simples:

(for _ in {1..1000}; do echo input-data.txt; done) | xargs cat > input-duplicated.txt
mycommand input-duplicated.txt
    
por Oli 08.09.2014 / 12:39
6

Aqui está uma solução awk :

awk '{a[NR]=$0}END{for (i=0; i<1000; i++){for(k in a){print a[k]}}}' file 

É essencialmente tão rápido quanto o @ Gnuc's Perl (eu corri ambas as mil vezes e obtive o tempo médio):

$ for i in {1..1000}; do 
 (time awk '{a[NR]=$0}END{for (i=0;i<1000;i++){for(k in a){print a[k]}}}' file > a) 2>&1 | 
    grep -oP 'real.*?m\K[\d\.]+'; done | awk '{k+=$1}END{print k/1000}'; 
0.00426

$ for i in {1..1000}; do 
  (time perl -0777pe '$_=$_ x 1000' file > a ) 2>&1 | 
    grep -oP 'real.*?m\K[\d\.]+'; done | awk '{k+=$1}END{print k/1000}'; 
0.004076
    
por terdon 08.09.2014 / 15:37
4

Gostaria apenas de usar um editor de texto.

vi input-data.txt
gg (move cursor to the beginning of the file)
yG (yank til the end of the file)
G (move the cursor to the last line of the file)
999p (paste the yanked text 999 times)
:wq (save the file and exit)

Se você realmente precisar fazer isso por meio da linha de comando (isso requer que você tenha vim instalado, pois vi não possui o comando :normal ), você poderia usar:

vim -es -u NONE "+normal ggyGG999p" +wq input-data.txt

Aqui, -es (ou -e -s ) faz o vim operar silenciosamente, então ele não deve assumir a janela do terminal, e -u NONE impede que ele olhe para o seu vimrc, o que deve fazê-lo rodar um pouco mais rápido do que de outra forma (talvez muito mais rápido, se você usar muitos plugins vim).

    
por evilsoup 08.09.2014 / 15:07
4

Aqui está uma simples linha direta, sem script envolvido:

mycommand <(cat 'yes input-data.txt | head -1000 | paste -s')

Explicação

  • 'yes input-data.txt | head -1000 | paste -s' produz o texto input-data.txt 1000 vezes separado por espaço em branco
  • O texto é passado para cat como uma lista de arquivos
por roeeb 10.03.2016 / 19:19
2

Enquanto trabalhei em um script completamente diferente, aprendi que, com 29 milhões de linhas de texto, usar seek() e operar em dados bytewise geralmente é mais rápido do que linha a linha. A mesma ideia é aplicada no script abaixo: nós abrimos o arquivo e, em vez de fazer um loop por meio de abrir e fechar o arquivo (o que pode adicionar sobrecarga, mesmo que não seja significativo), mantemos o arquivo aberto e procuramos voltar ao início.

#!/usr/bin/env python3
from __future__ import print_function
import sys,os

def error_out(string):
    sys.stderr.write(string+"\n")
    sys.exit(1)

def read_bytewise(fp):
    data = fp.read(1024)
    print(data.decode(),end="",flush=True)
    while data:
        data = fp.read(1024)
        print(data.decode(),end="",flush=True)
    #fp.seek(0,1)

def main():
    howmany = int(sys.argv[1]) + 1
    if not os.path.isfile(sys.argv[2]):
       error_out("Needs a valid file") 

    fp = open(sys.argv[2],'rb')
    for i in range(1,howmany):
        #print(i)
        fp.seek(0)
        read_bytewise(fp)
    fp.close()

if __name__ == '__main__': main()

O script em si é bastante simples de usar:

./repeat_text.py <INT> <TEXT.txt>

Para arquivos de texto de 3 linhas e 1000 iterações, tudo fica bem por volta de 0,1 segundos:

$ /usr/bin/time ./repeat_text.py 1000 input.txt  > /dev/null                                                             
0.10user 0.00system 0:00.23elapsed 45%CPU (0avgtext+0avgdata 9172maxresident)k
0inputs+0outputs (0major+1033minor)pagefaults 0swaps

O script em si não é mais elegante, provavelmente poderia ser encurtado, mas funciona. É claro que adicionei alguns bits extras aqui e ali, como error_out() function, o que não é necessário - é apenas um pequeno toque amigável.

    
por Sergiy Kolodyazhnyy 07.01.2017 / 10:40
1

Podemos resolver isso sem um arquivo adicional, nem programas especiais, puro Bash (bem, o gato é um comando padrão).

Baseado em um recurso do printf dentro do bash, podemos gerar uma string repetida):

printf "test.file.txt %.0s\n" {1..1000}

Em seguida, podemos enviar essa lista de 1000 nomes de arquivos (repetidos) e chamar cat:

printf "test.file.txt %.0s" {1..1000} | xargs cat 

E, finalmente, podemos fornecer a saída ao comando para executar:

mycommand "$( printf "%.0sinput.txt\n" {1..1000} | xargs cat )"

Ou, se o comando precisar receber a entrada no stdin:

mycommand < <( printf "%.0sinput.txt\n" {1..1000} | xargs cat )

Sim, o dobro < é necessário.

    
por user379914 30.07.2015 / 08:11
0

Eu geraria um novo arquivo usando o Unix para loop:

content=$(cat Alex.pgn); for i in {1..900000}; do echo "$content" >> new_file; done 
    
por SmallChess 12.12.2016 / 04:47