Bash скрипт, използващ gzip и bcftools, изчерпва паметта с големи файлове

Този bash скрипт е предназначен да бъде част от конвейер, който обработва компресиран .vcf файл, който съдържа геноми от множество пациенти (което означава, че файловете са огромни, дори когато са компресирани, като 3-5 GB).

Проблемът ми е, че постоянно ми липсва памет, когато изпълнявам този скрипт. Изпълнява се във виртуална машина с висока памет на GCP.

Надявам се, че има начин да се оптимизира използването на паметта, така че това да не се провали. Разгледах го, но не намерих нищо.

#!/bin/bash

for filename in ./*.vcf.gz; do
    [ -e "$filename" ] || continue 
    name=${filename##*/}
    base=${name%.vcf.gz}
    bcftools query -l "$filename" >> ${base}_list.txt
    for line in `cat ${base}_list.txt`; do 
        bcftools view -s "$line" "$filename" -o ${line}.vcf.gz
        gzip ${line}.vcf 
    done
done

person NHellmann    schedule 21.01.2021    source източник
comment
Можете ли да посочите на кой ред не ви достига памет? Дали е bcftools query/view, gzip или for line in `cat ...` ?   -  person Socowi    schedule 22.01.2021
comment
Без връзка с действителния проблем; [ -e "$filename" ] най-вероятно е безполезен. *.csv.gz изброява само файлове и директории, които съществуват. -e проверява дали $filename съществува. Ако искате да сте сигурни, че $filename е файл, а не директория, използвайте -f.   -  person Socowi    schedule 22.01.2021
comment
@Socowi *.csv.gz изброява само файлове и директории, които съществуват =› не, ако моделът не съответства на нито един файл (освен ако shopt -s nullglob). В този случай ще получите filename=*.csv.gz и това е причината за -e теста IMO. Ако името на файла трябва да сочи към файл, тогава тестът -f би бил по-добър.   -  person xhienne    schedule 22.01.2021


Отговори (2)


Ако паметта ви свърши, когато използвате bcftools query/view или gzip, потърсете опции в ръководството, които могат да намалят отпечатъка от паметта. В случай на gzip можете също да преминете към алтернативно изпълнение. Можете дори да обмислите изцяло превключване на алгоритъма за компресиране (zstd е доста добър).

Имам чувството обаче, че проблемът може да е for line in `cat ${base}_list.txt`;. Целият файл ..._list.txt се зарежда в паметта, преди дори да започне цикълът. Също така, четенето на редове по този начин има всякакви проблеми, като разделяне на редове на интервали, разширяване на глобуси като * и така нататък. Използвайте това вместо това:

while read -r line; do 
    bcftools view -s "$line" "$filename" -o "$line.vcf.gz"
    gzip "$line.vcf"
done < "${base}_list.txt"

Между другото: Сигурни ли сте, че искате bcftools query -l "$filename" >> ${base}_list.txt да добави. Файлът ${base}_list.txt ще продължи да нараства при всяко изпълнение на скрипта. Помислете за презаписване на файла, като използвате > вместо >>.
В такъв случай обаче може изобщо да не ви трябва файлът, тъй като можете да използвате това вместо това:

bcftools query -l "$filename" |
while read -r line; do 
    bcftools view -s "$line" "$filename" -o "$line.vcf.gz"
    gzip "$line.vcf"
done
person Socowi    schedule 21.01.2021

Можете да опитате да използвате split за всеки файл (в постоянен размер) и след това gzip файла да се раздели.

https://man7.org/linux/man-pages/man1/split.1.html

person Community    schedule 21.01.2021