114 votes

Comment diviser un fichier en parties égales, sans casser les lignes individuelles ?

Je me demandais s'il était possible de diviser un fichier en parties égales ( éditer : \= tous égaux sauf le dernier), sans casser la ligne ? En utilisant la commande split d'Unix, les lignes peuvent être coupées en deux. Existe-t-il un moyen de diviser un fichier en 5 parties égales, par exemple, tout en ne conservant que des lignes entières (ce n'est pas un problème si l'un des fichiers est un peu plus grand ou plus petit) ? Je sais que je pourrais simplement calculer le nombre de lignes, mais je dois le faire pour un grand nombre de fichiers dans un bash script. Merci beaucoup !

154voto

paxdiablo Points 341644

Si vous voulez dire un nombre égal de lignes, split a une option pour cela :

split --lines=75

Si vous avez besoin de savoir ce que 75 devrait vraiment être pour N à parts égales, son :

lines_per_part = int(total_lines + N - 1) / N

où les lignes totales peuvent être obtenues avec wc -l .

Voir le script suivant pour un exemple :

#!/usr/bin/bash

# Configuration stuff

fspec=qq.c
num_files=6

# Work out lines per file.

total_lines=$(wc -l <${fspec})
((lines_per_file = (total_lines + num_files - 1) / num_files))

# Split the actual file, maintaining lines.

split --lines=${lines_per_file} ${fspec} xyzzy.

# Debug information

echo "Total lines     = ${total_lines}"
echo "Lines  per file = ${lines_per_file}"    
wc -l xyzzy.*

Ces sorties :

Total lines     = 70
Lines  per file = 12
  12 xyzzy.aa
  12 xyzzy.ab
  12 xyzzy.ac
  12 xyzzy.ad
  12 xyzzy.ae
  10 xyzzy.af
  70 total

Des versions plus récentes de split vous permettent de spécifier un nombre de CHUNKS avec le -n/--number option. Vous pouvez donc utiliser quelque chose comme :

split --number=l/6 ${fspec} xyzzy.

(c'est ell-slash-six c'est-à-dire lines pas one-slash-six ).

Vous obtiendrez ainsi des fichiers de taille à peu près égale, sans que la ligne médiane ne soit divisée.

Je mentionne ce dernier point parce que cela ne vous donne pas à peu près le même nombre de lignes dans chaque fichier, plus le même nombre de des personnages.

Ainsi, si vous avez une ligne de 20 caractères et 19 lignes de 1 caractère (vingt lignes au total) et que vous les avez divisées en cinq fichiers, il est probable que ne le fera pas obtenir quatre lignes dans chaque fichier.

39voto

jbr Points 3125

Le script n'est même pas nécessaire, split(1) prend en charge la fonctionnalité souhaitée dès le départ :
split -l 75 auth.log auth.log. La commande ci-dessus divise le fichier en morceaux de 75 lignes chacun, et sort le fichier sur le formulaire : auth.log.aa, auth.log.ab, ...

wc -l sur le fichier original et la sortie donne :

  321 auth.log
   75 auth.log.aa
   75 auth.log.ab
   75 auth.log.ac
   75 auth.log.ad
   21 auth.log.ae
  642 total

33voto

Kuf Points 5775

Une solution simple pour une question simple :

split -n l/5 your_file.txt

pas besoin de script ici.

De la homme fichier, CHUNKS may be:

l/N     split into N files without splitting lines

Mise à jour

Toutes les distributions Unix n'incluent pas ce drapeau. Par exemple, il ne fonctionnera pas sous OSX. Pour l'utiliser, vous pouvez considérer remplacer les utilitaires de Mac OS X par des utilitaires de base GNU .

23voto

user3769065 Points 48

Split a été mis à jour dans la version 8.8 de coreutils (annoncée le 22 décembre 2010) avec l'option --number pour générer un nombre spécifique de fichiers. L'option --number=l/n génère n fichiers sans diviser les lignes.

http://www.gnu.org/software/coreutils/manual/html_node/split-invocation.html#split-invocation http://savannah.gnu.org/forum/forum.php?forum_id=6662

5voto

J'ai fait un bash script, qui étant donné un nombre de parties comme entrée, divise un fichier

#!/bin/sh

parts_total="$2";
input="$1";

parts=$((parts_total))
for i in $(seq 0 $((parts_total-2))); do
  lines=$(wc -l "$input" | cut -f 1 -d" ")
  #n is rounded, 1.3 to 2, 1.6 to 2, 1 to 1
  n=$(awk  -v lines=$lines -v parts=$parts 'BEGIN { 
    n = lines/parts;
    rounded = sprintf("%.0f", n);
    if(n>rounded){
      print rounded + 1;
    }else{
      print rounded;
    }
  }');
  head -$n "$input" > split${i}
  tail -$((lines-n)) "$input" > .tmp${i}
  input=".tmp${i}"
  parts=$((parts-1));
done
mv .tmp$((parts_total-2)) split$((parts_total-1))
rm .tmp*

J'ai utilisé head et tail et stocker dans des fichiers tmp, pour diviser les fichiers

#10 means 10 parts
sh mysplitXparts.sh input_file 10

ou avec awk, où 0,1 est 10% => 10 parties, ou 0,334 est 3 parties

awk -v size=$(wc -l < input) -v perc=0.1 '{
  nfile = int(NR/(size*perc)); 
  if(nfile >= 1/perc){
    nfile--;
  } 
  print > "split_"nfile
}' input

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X