다음과 같은 파일이 있습니다.
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