在 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),那么您需要 bc
或 awk
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 以及关闭命令和管道的所有额外开销。
我正在尝试编写一个 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),那么您需要 bc
或 awk
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 以及关闭命令和管道的所有额外开销。