Fast processing of fastq data
Knowing a high-performance language will make a huge difference here. See the example below in C. I haven't tested, but it should be easy to modify for your purpose.

C++, Rust, Go, Nim and Julia can be as fast.

// Download https://raw.githubusercontent.com/lh3/minimap2/master/kseq.h
// Compile with: gcc -O2 -o myprog this-file.c -lz
#include &lt;zlib.h&gt;  
#include &lt;stdio.h&gt;  
#include "kseq.h"
// STEP 1: declare the type of file handler and the read() function  
KSEQ_INIT(gzFile, gzread)  

int main(int argc, char *argv[])  
{  
    gzFile fp;  
    kseq_t *seq;  
    if (argc == 1) {  
        fprintf(stderr, "Usage: %s &lt;in.seq&gt;\n", argv[0]);  
        return 1;  
    }  
    fp = gzopen(argv[1], "r"); // STEP 2: open the file handler  
    seq = kseq_init(fp); // STEP 3: initialize seq  
    while (kseq_read(seq) &gt;= 0) { // STEP 4: read sequence  
        int i, sum, low;
        if (seq-&gt;qual.l == 0) continue; // no quality
        if (seq-&gt;seq.l &lt; 15 || seq-&gt;seq.l &gt; 30) continue;
        for (i = sum = low = 0; i &lt; seq-&gt;seq.l; ++i) {
            int qual = seq-&gt;qual.s[i] - 33; // assuming Sanger quality
            sum += qual;
            if (qual &lt; 20) ++low;
        }
        if (sum &lt; seq-&gt;seq.l * 30.0 || low &gt; 2) continue;
        printf("@%s\n", seq-&gt;name.s); // output can be made faster
        puts(seq-&gt;seq.s);
        printf("+\n");
        puts(seq-&gt;qual.s);
    }  
    kseq_destroy(seq); // STEP 5: destroy seq  
    gzclose(fp); // STEP 6: close the file handler  
    return 0;  
}  


To apply the program to all *.fq in a directory (again, not tested):

mkdir -p filtered
ls *.fq | xargs -i echo ./myprog {} \&gt; filtered/{} | sh


You can also replace the final sh with parallel, but the above should be fast enough.