10000개 숫자(라인 아님)마다 파일 분할

10000개 숫자(라인 아님)마다 파일 분할

다음과 같은 파일이 있습니다.

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    

이 파일을 두 번째 필드의 10000 간격(행이 아닌 숫자 간격)마다 분할하고 싶습니다. 따라서 이 파일의 경우 첫 번째 줄(61336212를 포함하는 줄)에서 최대 61346211(61336212+9999)을 포함하는 줄로 분할한 다음 61346212에서 61356211까지 분할하고 싶습니다. 보시다시피 두 번째 필드/열의 숫자는 "채워져" 있지 않습니다.

이를 수행할 수 있는 방법이 있습니까?

답변1

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

, ... 라고 쓸 것입니다 file.0000(여기서 file.0001숫자는 첫 번째 줄의 숫자입니다).int(($2-n)/10000)n$2

파일 쓰기를 중지하자마자 파일을 닫습니다. 그렇지 않으면 수백 개의 파일 이후에 동시에 열리는 파일 수 제한에 도달하게 됩니다(GNU는 awk이 제한을 해결할 수 있지만 성능은 빠르게 저하됩니다).

우리는 이 숫자가 항상 증가한다고 가정합니다.

답변2

한 줄 버전을 크랙하세요. 어쩌면 더 적합할지도코드 골프하지만 이 포럼보다. 이렇게 하면 분할1, 분할2, 분할3 등과 같은 파일 이름이 생성됩니다.

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

출력 파일 이름을 분할001, 분할002, 분할003으로 만들려면 추가 작업이 필요합니다 sprintf.

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

@Stéphane Chazelas가 발견한 gawk 속도 저하 문제를 피하려면 perl을 사용하십시오.

perl -ne '(undef,$a)=split(/\s+/,$_);if($a>$b+9999){$c++;$b=$a}open(D,sprintf(">>ysplit%03d",$c));print D' <file.txt

답변3

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

테스트를 위해 간격을 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  

참고: 공간 간격에 대해 빈 파일이 생성됩니다. 빈 파일을 제거하려면 다음을 추가하세요.

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

파일은 루프의 각 단계에 대해 실행되므로 for가장 효율적이지는 않습니다.

답변4

행 개수가 아닌 개수만 계산하는 것을 의미하는 경우:

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

관련 정보