web-dev-qa-db-fra.com

Comment puis-je répéter le contenu d'un fichier n fois?

J'essaie de comparer deux manières différentes de traiter un fichier. J'ai peu de données d'entrée, mais pour obtenir de bonnes comparaisons, je dois répéter les tests plusieurs fois.

Plutôt que de simplement répéter les tests, j'aimerais dupliquer les données d'entrée un certain nombre de fois (par exemple, 1000) afin qu'un fichier de 3 lignes devienne 3000 lignes et que je puisse exécuter un test beaucoup plus enrichissant.

Je transmets les données d'entrée via un nom de fichier:

mycommand input-data.txt
18
Oli

Vous n'avez pas besoin de input-duplicated.txt.

Essayer:

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

Explication

  • 0777: -0 définit le séparateur d'enregistrement d'entrée (variable spéciale Perl $/ qui est une nouvelle ligne par défaut). Si vous définissez une valeur supérieure à 0400, Perl bloquera le fichier d'entrée entier en mémoire.
  • pe: le -p signifie "affiche chaque ligne d'entrée après l'application du script donné par -e".
  • $_=$_ x 1000: $_ est la ligne de saisie actuelle. Puisque nous lisons le fichier entier en même temps à cause de -0700, cela signifie le fichier entier. Le x 1000 entraînera l’impression de 1 000 copies de l’ensemble du fichier.
21
cuonglm

Au départ, je pensais que je devrais générer un fichier secondaire, mais je pouvais simplement boucler le fichier d'origine dans Bash et utiliser une redirection pour le faire apparaître sous forme de fichier.

Il y a probablement une douzaine de façons différentes de faire la boucle, mais en voici quatre:

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

La troisième méthode est improvisée à partir du commentaire de maru ci-dessous et construit une grande liste de noms de fichiers d'entrée pour cat. xargs divisera cela en autant d’arguments que le système le permettra. C'est beaucoup plus rapide que n chats séparés.

La méthode awk (inspirée par réponse de terdon ) est probablement la plus optimisée, mais elle duplique chaque ligne à la fois. Cela peut ne pas convenir à une application particulière, mais il est rapide et efficace.


Mais cela génère à la volée. La sortie Bash est susceptible d'être beaucoup plus lente que ce que vous pouvez lire, vous devez donc générer un nouveau fichier à tester. Heureusement, ce n'est qu'une extension très simple:

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

Voici une solution awk:

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

C'est essentiellement aussi rapide que @ Gnuc's Perl (j'ai couru les deux fois et j'ai obtenu le temps moyen):

$ 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

Je voudrais juste utiliser un éditeur de texte.

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)

Si vous devez absolument le faire via la ligne de commande (vous devez avoir installé vim, car vi ne dispose pas de la commande :normal), vous pouvez utiliser:

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

Ici, -es (ou -e -s) permet à vim de fonctionner en silence, il ne devrait donc pas prendre en charge la fenêtre de votre terminal, et -u NONE l’empêche de regarder votre vimrc, ce qui devrait le faire courir un peu plus vite qu’autrement (sinon beaucoup plus vite). si vous utilisez beaucoup de plugins vim).

4
evilsoup

Voici une simple ligne, sans script:

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

Explication

  • `yes input-data.txt | head -1000 | paste -s` produit le texte input-data.txt 1000 fois séparés par des espaces
  • Le texte est ensuite transmis à cat sous forme de liste de fichiers.
4
roeeb

Tout en travaillant sur un script complètement différent, j'ai appris qu'avec 29 millions de lignes de texte, utiliser seek() et exploiter des données octet par seconde est souvent plus rapide que ligne par ligne. La même idée est appliquée dans le script ci-dessous: nous ouvrons le fichier et, au lieu de boucler l'ouverture et la fermeture du fichier (ce qui peut augmenter le temps système, même s'il n'est pas significatif), nous gardons le fichier ouvert et cherchons à revenir au début.

#!/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()

Le script lui-même est assez simple d'utilisation:

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

Pour un fichier texte de 3 lignes et 1000 itérations, tout se passe bien, environ 0,1 seconde:

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

Le script lui-même n'est pas très élégant, pourrait probablement être raccourci, mais fait le travail. Bien sûr, j’ai ajouté quelques bits supplémentaires ici et là, comme la fonction error_out(), qui n’est pas nécessaire - c’est juste une petite touche conviviale.

2

Nous pouvons résoudre ce problème sans fichier supplémentaire ni programme spécial, pur Bash (enfin, cat est une commande standard).

Basé sur une fonctionnalité de printf dans bash, nous pouvons générer une chaîne répétée):

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

Ensuite, nous pouvons envoyer cette liste de 1000 noms de fichiers (répétés) et appeler cat:

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

Et enfin, nous pouvons donner le résultat à la commande à exécuter:

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

Ou, si la commande doit recevoir l'entrée dans le stdin:

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

Oui, le double <est nécessaire.

1
user379914

Je générerais un nouveau fichier en utilisant Unix pour loop:

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