在 bash 的 for 循环中将 wc-l 的输出除以 4?

Divide output of wc-l by 4 in a for loop in bash?

我正在尝试编写一个 for 循环来解压缩文件名中包含 R1 的 fastq.gz 文件,确定每个文件中的行数,并将行数除以 4。理想情况下我也可以将其写入包含两列的 txt 文件(文件名和 lines/4 的编号)。

此循环解压缩 R1 fastq 文件并确定每个文件中的行数但不除以 4(或将输出保存到 txt 文件中)。

for i in $(ls ./*R1*);
do
gzcat ./$i | wc -l
done;

这里的其他帖子建议使用 bc 划分 bash,但我无法将其集成到循环中。

如果您允许 5 / 4 = 1(因此四舍五入为最接近的整数),这将有效。如果您想使用小数 (5 / 4 = 1.25),那么您需要 bcawk

for i in $(ls ./R1); do 
  nb_lines=$(gzcat ./$i | wc -l)
  echo $((nb_lines / 4))
done;

您从不使用 for i in $(ls anything),请参阅 Bash Pitfalls #1。对于带有空格或任何其他特殊字符的文件名,您的循环将失败。在大多数情况下,您只需使用 for i in path/*; do ... 遍历文件,但请理解,如果文件名包含 '\n' 字符作为名称的一部分,则可能会失败。处理所有文件名的最佳方法是使用 find 作为 while read -r name; do ... done < <(find path -type f -name "*.gz")(注意 进程替换 < <(...) 是一个仅 bash 的结构,如果使用 POSIX shell)

则管道到循环

接下来,要将名称和行数 / 4 写入新文件,将整个循环包装在 { .... } 之间的新范围内,然后立即将所有输出重定向到新文件。

您还应该添加验证以检查文件是否是以 gz 结尾的目录并跳过任何找到的文件,以及跳过任何空文件(零文件大小)

如果你完全这样做,你可以这样做:

{
for i in R1/*.gz; do
  [ -d "$i" ] && continue                 ## skip any directories
  [ -s "" ] && continue                 ## skip empty files
  nlines=$(gzcat "$i" | wc -l)            ## get number of lines
  printf "%s\t%s\n" "$i" $((nlines / 4))  ## output name, nlines / 4
done
} > newfile         ## redirect all output to newfile

(输出用 tab 字符 "\t" 分隔名称和数字 / 4 -- 根据需要调整)

检查一下,如果您有任何问题,请告诉我。

进行整数运算的最简单方法是使用 $((...)) 表示法,您可以从这些简单示例中看出:

Prompt> echo $((2*6))
12
Prompt> echo $((20/4))
5
Prompt> echo $((21/4))
5

它也可以与其他命令结合使用,例如wc -l:

Prompt> cat .viminfo | wc -l
287
Prompt> echo $(($(cat .viminfo | wc -l) / 4))
71

我知道那些生物信息学数据集是巨大的,但在 ultra off-chance 中你会遇到一个少于 1024 行的数据集,然后简单地:

% [g/n/m]awk 'BEGIN{ 
                    _ = system("exit $(( 977 / 4 )) ")
           print _ }'
    244

这是一种极其懒惰的做事方式,它利用了 256 典型的退出代码可供选择的事实,这是唯一 system() 函数愿意 return -

这样,您就不需要涉及 getline 以及关闭命令和管道的所有额外开销。