it-swarm.com.de

Wie kann ich den Inhalt einer Datei n-mal wiederholen?

Ich versuche ein Benchmarking durchzuführen, um zwei verschiedene Arten der Dateiverarbeitung zu vergleichen. Ich habe eine kleine Menge von Eingabedaten, aber um gute Vergleiche zu erhalten, muss ich die Tests einige Male wiederholen.

Anstatt nur die Tests zu wiederholen, möchte ich die Eingabedaten mehrmals duplizieren (z. B. 1000), damit aus einer 3-zeiligen Datei 3000 Zeilen werden und ich einen viel ausführlicheren Test durchführen kann.

Ich übergebe die Eingabedaten über einen Dateinamen:

mycommand input-data.txt
18
Oli

Du brauchst input-duplicated.txt nicht.

Versuchen:

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

Erläuterung

  • 0777: -0 sets legt das Trennzeichen für Eingabedatensätze fest (Perl-Spezialvariable $/, die standardmäßig ein Zeilenumbruch ist). Wenn Sie diesen Wert auf einen Wert größer als 0400 setzen, wird Perl die gesamte Eingabedatei in den Speicher verschieben.
  • pe: Der -p bedeutet "jede Eingabezeile nach Anwendung des von -e vorgegebenen Skripts drucken".
  • $_=$_ x 1000: $_ ist die aktuelle Eingabezeile. Da wir aufgrund von -0700 die gesamte Datei auf einmal lesen, bedeutet dies die gesamte Datei. Der x 1000 führt dazu, dass 1000 Kopien der gesamten Datei gedruckt werden.
21
cuonglm

Ich dachte ursprünglich, dass ich eine sekundäre Datei generieren müsste, aber ich könnte einfach die ursprüngliche Datei in Bash schleifen und eine Umleitung verwenden, um sie als Datei erscheinen zu lassen.

Es gibt wahrscheinlich ein Dutzend verschiedene Möglichkeiten, die Schleife auszuführen, aber hier sind vier:

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)  #*

Die dritte Methode, die es gibt, ist aus Marus Kommentar unten improvisiert und erstellt eine große Liste von Eingabedateinamen für cat. xargs teilt dies in so viele Argumente auf, wie das System zulässt. Es ist viel schneller als n getrennte Katzen.

Der Weg awk (inspiriert von terdons Antwort ) ist wahrscheinlich der am besten optimierte, dupliziert jedoch jede Zeile gleichzeitig. Dies kann für eine bestimmte Anwendung geeignet sein oder auch nicht, ist jedoch blitzschnell und effizient.


Dies wird jedoch im laufenden Betrieb generiert. Die Bash-Ausgabe ist wahrscheinlich sehr viel langsamer als irgendetwas zu lesen ist. Sie sollten daher eine neue Datei zum Testen generieren. Zum Glück ist das nur eine sehr einfache Erweiterung:

(for _ in {1..1000}; do echo input-data.txt; done) | xargs cat > input-duplicated.txt
mycommand input-duplicated.txt
10
Oli

Hier ist eine awk Lösung:

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

Es ist im Wesentlichen so schnell wie @ Gnucs Perl (ich habe beide 1000-mal ausgeführt und die durchschnittliche Zeit erhalten):

$ 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
6
terdon

Ich würde nur einen Texteditor verwenden.

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)

Wenn Sie dies unbedingt über die Befehlszeile tun müssen (hierfür muss vim installiert sein, da vi den Befehl :normal nicht enthält), können Sie Folgendes verwenden:

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

Hier sorgt -es (oder -e -s) dafür, dass VIM im Hintergrund ausgeführt wird. Daher sollte es nicht Ihr Terminalfenster übernehmen, und -u NONE hindert es daran, auf Ihren VIMRC zu schauen, wodurch es ausgeführt werden sollte etwas schneller als sonst (vielleicht viel schneller, wenn Sie viele vim-Plugins verwenden).

4
evilsoup

Hier ist ein einfacher Einzeiler ohne Scripting:

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

Erläuterung

  • `yes input-data.txt | head -1000 | paste -s` erzeugt den Text input-data.txt 1000-mal durch Leerzeichen getrennt
  • Der Text wird dann als Dateiliste an cat übergeben
4
roeeb

Während ich an einem völlig anderen Skript gearbeitet habe, habe ich gelernt, dass es mit 29 Millionen Textzeilen oft schneller ist, seek() zu verwenden und Daten byteweise zu verarbeiten als zeilenweise. Dieselbe Idee wird im folgenden Skript angewendet: Wir öffnen eine Datei, und anstatt die Datei in einer Schleife zu öffnen und zu schließen (was zu zusätzlichem Aufwand führen kann, auch wenn dieser nicht signifikant ist), lassen wir die Datei geöffnet und versuchen, zum Anfang zurückzukehren.

#!/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 __== '__main__': main()

Das Skript selbst ist recht einfach zu bedienen:

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

Für eine 3-zeilige Textdatei und 1000 Iterationen geht es ganz gut, ungefähr 0,1 Sekunden:

$ /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

Das Drehbuch selbst ist nicht besonders elegant, könnte wahrscheinlich gekürzt werden, erledigt aber den Job. Natürlich habe ich hier und da ein paar zusätzliche Bits hinzugefügt, wie die Funktion error_out(), die nicht erforderlich ist - es ist nur eine kleine benutzerfreundliche Berührung.

2

Wir können dies ohne eine zusätzliche Datei oder spezielle Programme lösen, reine Bash (na ja, cat ist ein Standardbefehl).

Basierend auf einer Funktion von printf in bash können wir einen wiederholten String erzeugen.

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

Dann können wir eine solche Liste mit 1000 Dateinamen (wiederholt) senden und cat anrufen:

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

Und schließlich können wir die Ausgabe an den Befehl übergeben, der ausgeführt werden soll:

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

Oder, wenn der Befehl die Eingabe in der Standardeingabe erhalten muss:

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

Ja, das double <wird benötigt.

1
user379914

Ich würde eine neue Datei mit Unix for loop erzeugen:

content=$(cat Alex.pgn); for i in {1..900000}; do echo "$content" >> new_file; done 
0
SmallChess