Diviser le file pour chaque tranche de 10000 nombres (pas de lignes)

J'ai un file qui ressemble à ce qui suit:

chr19 61336212 + 0 0 CG CGT chr19 61336213 - 0 0 CG CGG chr19 61336218 + 0 0 CG CGG chr19 61336219 - 0 0 CG CGC chr19 61336268 + 0 0 CG CGG chr19 61336269 - 0 0 CG CGA chr19 61336402 + 0 0 CG CGG chr19 61336403 - 0 0 CG CGT 

Je veux split ce file pour chaque intervalle de 10000 du 2ème champ (pas lignes, mais l'intervalle de nombre). Donc, pour ce file, je voudrais séparer de la première ligne (la ligne avec 61336212) à la ligne qui a ou jusqu'à 61346211 (61336212 + 9999), puis de 61346212 à 61356211, et ainsi de suite et ainsi de suite. Comme vous pouvez voir les numbers dans le 2ème champ / colonne n'est pas "rempli".

Y a-t-il un moyen de faire cela?

 awk 'NR==1 {n=$2} { file = sprintf("file.%.4d", ($2-n)/10000) if (file != last_file) { close(last_file) last_file = file } print > file }' 

Écrire dans le file.0000 , file.0001 … (le numéro étant int(($2-n)/10000)n est $2 pour la première ligne).

Notez que nous fermons les files une fois que nous avons cessé d'écrire, sinon vous atteindriez la limite du nombre de files ouverts simultanément après quelques centaines de files (GNU awk peut contourner cette limite, mais les performances se dégradent rapidement) .

Nous supposons que ces numbers augmentent toujours.

Hack version one-liner. Peut-être plus approprié pour Code Golf que ce forum cependant. Cela génère split1, split2, split3 et ainsi de suite, comme noms de files.

 awk '{if($2>b+9999){a++;b=$2}print >"split" a}' file.txt 

Pour avoir des files de sortie nommés split001, split002, split003, implique ce sprintf supplémentaire:

 awk '{if($2>b+9999){a++;b=$2}print >sprintf("split%03d",a)}' file.txt 

Pour éviter le problème de ralentissement gawk identifié par @ Stéphane Chazelas, utilisez perl:

 perl -ne '(undef,$a)=split(/\s+/,$_);if($a>$b+9999){$c++;$b=$a}open(D,sprintf(">>ysplit%03d",$c));print D' <file.txt 
 #!/bin/bash first=$( head -n1 file | awk -F" +" '{print $2}' ) last=$( tail -n1 file | awk -F" +" '{print $2}' ) for (( i=$first ; i<=$last ; i=i+10000 )) ; do awk -v start=$i -v end=$(($i+10000)) 'BEGIN { FS == " +" } { if ( $2 >= start && $2 < end ) print $0 }' file \ >> interval_"$i"_to_"$(( $i+10000 ))" done 

Test avec intervalle réglé sur 100:

 more inter* :::::::::::::: interval_61336212_to_61346212 :::::::::::::: chr19 61336212 + 0 0 CG CGT chr19 61336213 - 0 0 CG CGG chr19 61336218 + 0 0 CG CGG chr19 61336219 - 0 0 CG CGC chr19 61336268 + 0 0 CG CGG chr19 61336269 - 0 0 CG CGA :::::::::::::: interval_61336312_to_61346312 :::::::::::::: chr19 61336402 + 0 0 CG CGG chr19 61336403 - 0 0 CG CGT 

Note: produira des files vides pour les intervalles vides; pour supprimer des files vides, ajoutez:

 for file in interval* ; do if [ ! -s "$file" ] ; then rm "$file" fi done 

Courra sur file pour chaque étape de la boucle for , donc pas le plus efficace.

Si vous voulez simplement calculer, pas countr en ligne:

 awk 'NR==1 || n+10000<$2{n=$2; portion++}{print > FILENAME "." portion}' file