web-dev-qa-db-fra.com

Moyen le plus rapide de concaténer des fichiers

J'ai 10k + fichiers totalisant plus de 20 Go que je dois concaténer en un seul fichier.

Y a-t-il un moyen plus rapide que

cat input_file* >> out

?

La manière préférée serait une commande bash, Python est également acceptable sinon considérablement plus lent.

26
fsperrle

Non, le chat est sûrement le meilleur moyen de le faire. Pourquoi utiliser python alors qu'il existe un programme déjà écrit en C à cet effet? Cependant, vous pouvez cependant envisager d'utiliser xargs au cas où la longueur de la ligne de commande dépasse ARG_MAX et vous avez besoin de plusieurs cat. En utilisant les outils GNU, cela équivaut à ce que vous avez déjà:

find . -maxdepth 1 -type f -name 'input_file*' -print0 |
  sort -z |
  xargs -0 cat -- >>out
30
Graeme

L'allocation de l'espace pour le fichier de sortie en premier peut améliorer la vitesse globale car le système n'aura pas à mettre à jour l'allocation pour chaque écriture.

Par exemple, si sous Linux:

size=$({ find . -maxdepth 1 -type f -name 'input_file*' -printf '%s+'; echo 0;} | bc)
fallocate -l "$size" out &&
  find . -maxdepth 1 -type f -name 'input_file*' -print0 |
  sort -z | xargs -r0 cat 1<> out

Un autre avantage est que s'il n'y a pas assez d'espace libre, la copie ne sera pas tentée.

Si sur btrfs, vous pourriez copy --reflink=always le premier fichier (qui n'implique aucune copie de données et serait donc quasi instantané), et ajoutez le reste. S'il y a 10000 fichiers, cela ne fera probablement pas beaucoup de différence, sauf si le premier fichier est très volumineux.

Il y a une API pour généraliser cela pour recopier tous les fichiers (le BTRFS_IOC_CLONE_RANGEioctl), mais je n'ai trouvé aucun utilitaire exposant cette API, vous devez donc le faire en C (ou python ou dans d'autres langages à condition qu'ils puissent appeler arbitrairement ioctls).

Si les fichiers source sont clairsemés ou ont de grandes séquences de caractères NUL, vous pouvez créer un fichier de sortie clairsemé (gain de temps et d'espace disque) avec (sur les systèmes GNU):

find . -maxdepth 1 -type f -name 'input_file*' -print0 |
  sort -z | xargs -r0 cat | cp --sparse=always /dev/stdin out
22