# How to get a file with the number of reads for several fastq.gz files?

I have generated several FASTQ files and I would like to know the amount of reads for each of them. I am planning to run FastQC on the files which I know would give me the number of reads per sample but it would be a lot easier for me if I could get one text file with the file name and number of reads for all samples together.

When I check for one file I normally do zcat filename_R1.fastq.gz | wc -l and then divide by 4. Is there a way to do it for all my files at once and get a text file with the file name and reads at the end?

For example I will have 71 R1.fastq.gz files and I would like to output the amount of reads to a text file. I just meant that I would prefer to use one command instead of having to use the command I mentioned above for each file.

• Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking.
– Community Bot
Feb 3 at 22:26
• Note that fastq files can have more than 1 line of sequence per entry so dividing by 4 is not robust. Make sure to check your file and see if you have cases of multi-line sequences.
– terdon
Feb 6 at 10:26

One way would be to use seqkit stats, which can be installed using conda:

conda install -c bioconda seqkit


It has a number of useful options, including ways to parallelize the counting, using -j, --threads (default 4), and to skip errors using -e, --skip-err. The latter can be especially useful if you have a larger number of FASTQ files with millions of reads and you'd like to run the command overnight for example. There's also an option to produce a machine-friendly tabular output, using -T, --tabular which would allow you to parse the output without much effort. I'd be tempted to just write the output to a file using the -o, --out-file option:

seqkit stats -To stats.tsv *.fastq.gz


Example results:

$cat stats.tsv file format type num_seqs sum_len min_len avg_len max_len PHB_Rep1_R1.fastq.gz FASTQ RNA 118571 11857100 100 100.0 100 PHB_Rep1_R2.fastq.gz FASTQ RNA 118571 11857100 100 100.0 100 PHB_Rep2_R1.fastq.gz FASTQ RNA 144826 14482600 100 100.0 100 PHB_Rep2_R2.fastq.gz FASTQ RNA 144826 14482600 100 100.0 100 PHB_Rep3_R1.fastq.gz FASTQ RNA 129786 12978600 100 100.0 100 PHB_Rep3_R2.fastq.gz FASTQ RNA 129786 12978600 100 100.0 100 PUH_Rep1_R1.fastq.gz FASTQ RNA 227392 22739200 100 100.0 100 PUH_Rep1_R2.fastq.gz FASTQ RNA 227392 22739200 100 100.0 100 PUH_Rep2_R1.fastq.gz FASTQ RNA 162373 16237300 100 100.0 100 PUH_Rep2_R2.fastq.gz FASTQ RNA 162373 16237300 100 100.0 100 PUH_Rep3_R1.fastq.gz FASTQ RNA 185442 18544200 100 100.0 100 PUH_Rep3_R2.fastq.gz FASTQ RNA 185442 18544200 100 100.0 100  And then just parse the output to get what you need, if or when you need it. For your specific requirements, you could just use AWK to skip the header line and print the first and fourth columns: awk 'BEGIN {FS=OFS="\t" } NR>1 { print$1, $4 }' stats.tsv  Results: PHB_Rep1_R1.fastq.gz 118571 PHB_Rep1_R2.fastq.gz 118571 PHB_Rep2_R1.fastq.gz 144826 PHB_Rep2_R2.fastq.gz 144826 PHB_Rep3_R1.fastq.gz 129786 PHB_Rep3_R2.fastq.gz 129786 PUH_Rep1_R1.fastq.gz 227392 PUH_Rep1_R2.fastq.gz 227392 PUH_Rep2_R1.fastq.gz 162373 PUH_Rep2_R2.fastq.gz 162373 PUH_Rep3_R1.fastq.gz 185442 PUH_Rep3_R2.fastq.gz 185442  You can replace gunzip -c with zcat. #USAGE: script.sh /runFolder for i in $$1/*.1.fastq.gz do echo "$$i" $$(gunzip -c$$i | echo wc -l/4 | bc -l) done #OR in the current folder, do this: for i in ./*.1.fastq.gz do echo "$$i"$$(gunzip -c$i | echo wc -l/4 | bc -l)
done

• Backticks are legacy syntax. Subshells can be nested for example using: echo "${i}:$(echo "scale=2; $(zcat "${i}" | wc -l)/4" | bc)" Feb 4 at 2:53
• Also, I think the OP is looking for a summary text file, so you could just have your loop append to an output file (i.e. ... >> counts.txt) Feb 4 at 2:59

zcat, like cat, works with multiple file names as arguments, up to the argument limit (on Linux, that's usually at least a thousand), so counting multiple lines in multiple files still works, assuming the fastq files are formatted in the expected way with only four lines per record:

zcat filename_R1.fastq.gz filename_R2.fastq.gz ... | wc -l


If there's a consistent file pattern, that can be used with file name glob wildcards:

zcat *.fastq.gz | wc -l


If you have a file containing a list of files, this can be processed as shell arguments as well:

zcat $(cat list_of_files.txt) | wc -l  For an unlimited number of files, something like GNU parallel can be used to feed file names from piped input. It's also a bit safer for file names containing weird characters in them: cat list_of_files.txt | parallel zcat {} | wc -l  ... but [after checking the question again] given that you want counts for individual files, the file name needs to be added as well: cat list_of_files.txt | parallel echo {}$(zcat {} | wc -l)


The read function provides an alternate way to do the same thing:

cat list_of_files.txt | while read fileName; do echo $${fileName}$$(zcat ${fileName} | wc -l); done  Assuming you are using bash, dividing by 4 can be done via the arithmetic substitution operator, $((X)):

cat list_of_files.txt | parallel echo {} $$(($$(zcat {} | wc -l) / 4 ))


Or with read:

cat list_of_files.txt | while read fileName; do echo $${fileName}$$(( $$(zcat$${fileName} | wc -l) / 4 )); done


And, finally, this can be output to a file using >:

(cat list_of_files.txt | while read fileName; do echo $${fileName}$$(( $$(zcat$${fileName} | wc -l) / 4 )); done) > fastq_record_counts.txt

• You'll need to wrap your commands in single quotes to prevent the dollar variables from expanding. You can also avoid the Useless Use of Cat (UUoC) by redirecting stdin or using the :::: argfiles syntax with GNU parallel. To be safe, remember to always quote your variables. Feb 6 at 3:17
• For echo? I don't think it'll make any difference
– gringer
Feb 6 at 11:48
• Sorry, I mean the parallel commands need to be singled quoted: parallel 'echo {} $(($(zcat {} | wc -l) / 4 ))' :::: list_of_files.txt Feb 6 at 12:31

If you're looking for a one-liner,

for i in $(ls *.fastq.gz); do count=$(zcat $i | grep "^@" | wc -l); printf "%b\t%b"$i $count; done > counts.tsv  • Note that fastq files can include '@' in the first character of the quality string, so this may produce incorrect results. – gringer Feb 5 at 19:58 • Also, NEVER do for i in$(ls *), that is very bad practice, will break on even slightly odd file names and is really not needed since a simple for i in *.fastq.gz; is safer, more robust and shorter to write.
– terdon
Feb 6 at 10:30