我们已经学习过两种组合命令的形式:
- 使用逻辑,例如
command1 && command2
的作用为command1
运行成功(返回0)才运行command2
,而command1;command2
的作用是无论command1
是否成功运行都运行command2
。前者的输出是不作为后者的输入的。 - 使用管道
|
,例如command1|command2
,前者的输出是作为后者的输入的。
subshell的威力就是可以结合这两种组合命令的形式发挥更大的作用,并且它的用法也非常简单。通过一个具体例子来说明,试比较下面两条代码输出结果的区别:
$ echo "this line"; echo "that line" | sed 's/line/step/'
this line
that step
$ (echo "this line"; echo "that line") | sed 's/line/step/'
this step
that step
第一条命令的结果比较容易理解,第二条命令的区别只是增加了括号,然而它的运行逻辑是这样的:
- 通过括号使两条
echo
命令在一个单独的subshell中运行 - 两条命令的标准输出被组合起来传递给下游的
sed
-
sed
命令对两行字符串进行替换。
当理解了上面这个例子后,我们考虑一种稍微复杂些的应用:对Mus_musculus.GRCm38.75_chr1.gtf.gz
文件(如下)根据第一列(染色体编号)与第4列(基因开始位置)进行排序:
$ zcat Mus_musculus.GRCm38.75_chr1.gtf.gz | head
#!genome-build GRCm38.p2
#!genome-version GRCm38
#!genome-date 2012-01
#!genome-build-accession NCBI:GCA_000001635.4
#!genebuild-last-updated 2013-09
1 pseudogene gene 3054233 3054733 . + . gene_id "ENSMUSG00000090025"; gene_name "Gm16088"; gene_source "havana"; gene_biotype "pseudogene";
1 unprocessed_pseudogene transcript 3054233 3054733 . + . gene_id "ENSMUSG00000090025"; transcript_id "ENSMUST00000160944"; gene_name "Gm16088"; gene_source "havana"; gene_biotype "pseudogene"; transcript_name "Gm16088-001"; transcript_source "havana"; tag "cds_end_NF"; tag "cds_start_NF"; tag "mRNA_end_NF"; tag "mRNA_start_NF";
1 unprocessed_pseudogene exon 3054233 3054733 . + . gene_id "ENSMUSG00000090025"; transcript_id "ENSMUST00000160944"; exon_number "1"; gene_name "Gm16088"; gene_source "havana"; gene_biotype "pseudogene"; transcript_name "Gm16088-001"; transcript_source "havana"; exon_id "ENSMUSE00000848981"; tag "cds_end_NF"; tag "cds_start_NF"; tag "mRNA_end_NF"; tag "mRNA_start_NF";
1 snRNA gene 3102016 3102125 . + . gene_id "ENSMUSG00000064842"; gene_name "Gm26206"; gene_source "ensembl"; gene_biotype "snRNA";
1 snRNA transcript 3102016 3102125 . + . gene_id "ENSMUSG00000064842"; transcript_id "ENSMUST00000082908"; gene_name "Gm26206"; gene_source "ensembl"; gene_biotype "snRNA"; transcript_name "Gm26206-201"; transcript_source "ensembl";
由于此文件的开头部分是以#
开头的注释,直接使用sort
命令会打乱注释的位置,但是我们又想在排序后的结果文件中保留注释,该怎么做呢?
基于我们上面学习到的内容,我们可以采用subshell的方式:
(zgrep "^#" Mus_musculus.GRCm38.75_chr1.gtf.gz; \
zgrep -v "^#" Mus_musculus.GRCm38.75_chr1.gtf.gz \
| sort -k1,1 -k4,4n) | less
当然,可以使用gzip
对结果进行压缩保存:
(zgrep "^#" Mus_musculus.GRCm38.75_chr1.gtf.gz; zgrep -v "^#" Mus_musculus.GRCm38.75_chr1.gtf.gz | sort -k1,1 -k4,4n) | gzip > Mus_musculus.GRCm38.75_chr1_sorted.gtf.gz