好久没有更新生信相关的文章了,差点都以为自己不会生信了。接下来,准备和大家更新一波shell bash编程的系列。bash编程是我最早接触的一门编程语言,毕竟生信都离不开shell嘛。虽然是接触最久,但是也是学得最糙,到现在有一些bash编程的小细节也还是做得不够好。所以决定和大家从头系统的学一波bash编程。
什么是shell编程?什么是bash脚本?
根据维基百科的定义:
Shell编程是一种计算机程序,由Unix Shell(命令行解释器)运行。Shell脚本执行的操作包括文件操作,程序执行和打印文本。另外shell脚本也包括,设置环境的脚本将运行该程序,并执行所有必要的清除,日志记录等工作。
bash脚本是通过shell编程执行的纯文本文件。这是其中一个例子:
Bash脚本通常是相对较短的文本文件(10-100行),其中包含一系列命令。当然我们也可以直接在终端中键入这些命令,进行运行,但这就不是脚本的形式了。shell文件的扩展名是
.
sh
,并可以用以下命令在shell中运行:
bash myscript.sh
在哪编写脚本?
对于编写bash脚本,一般来说使用“纯文本编辑器”就足够了:
当然也可以使用更加专业的编程编辑器:
如果直接运行脚本吗?
小伙伴们应该或多或少有直接运行过bash脚本的经历,那这又是怎样实现的?首先你要在脚本的第一行添加shebang的行开头:
#!/bin/bash
另外脚本要被标记为可直接执行:
chmod +x myscript.sh
有了这两步之后,我们就可以直接通过
myscript
.
sh
来执行该代码了。有一些编程者喜欢将
.
sh
的后续删掉,让脚本看起来更加简洁。但是一般来说我们不推荐这样的做法,第一,它会引起误解,无法传递该文本是一个脚本的信息。第二,删去后续后,文本编辑器不能自动判断你文本的类型,然后无法执行其语法高亮等的附加功能,不利于我们编写代码。因此,大家在编写脚本时,还是一定要使用后续(这个原则对于其他编程语言来说同样适用)。
写脚本的重要性?
为什么要写脚本呢:
一般来说咱们在脚本写注释不宜过长,也不宜过短。我们需要将信息简单明了的进行记录,我们会在不断练习中变得熟练。另外请记住,写注释其实不是为了别人,是为了未来的自己。千万不要相信你的记忆。注释可以帮助我们记住做出个人决定的原因。这极大地提高了生产率,使我们能够在更短的时间内完成更多工作。
下面是一个不错的脚本注释例子,在每一行之前加一个简短的注释。在代码行之间留空格:
#
# This script downloads a dataset identified by an SRR run id.
#
# Limit download to 1000 reads during testing.
fastq-dump -X 10000 --split-files SRR519926
# Run FastQC on the resulting datasets.
fastqc SRR519926_1.fastq SRR519926_2.fastq
怎样学习bash编程?
和其它所有编程一样,在学习完bash编程语言的基本语法之后,最终熟练掌握该编程语言的秘诀就是:不断的练习,不断的犯错。编程需要通过练习来熟记于心,犯错能让我们获得经验,通过
阅读报错,找出程序无法运行的原因,并修正。
下面列举三个bash编程最常见的错误:
-
指定的文件不存在
-
文件存在,但是不在当前目录
-
你打错了文件名
确保你了解变量的替换
如果一个变量没有使用任何的引用号,或者使用了双引号。那么这个变量就会被它所对应的值替换掉。但是如果这个字符串,是使用单引号的,那么该变量是一个字符串,而不会看作为变量被替换。
具体例子:
NAME=John
echo Hello $NAME
echo "Hello $NAME"
echo 'Hello $NAME'
对应的输出
Hello John
Hello John
Hello $NAME
怎样写更好的bash脚本?
在了解完最基本的知识,如何编写一个脚本后。接下来我们更加关心的是,如何写一个更好的脚本?
其中一个技巧就是,将会发生变化的信息存到“变量”中。在编程中使用变量可帮助我们将需要更改的部分,与应始终相同的部分分开(比如一些固定的命令参数等)。
让我们使用上面用到的代码:
# Limit download to 1000 reads.
fastq-dump -X 10000
--split-files SRR519926
# Run FastQC on the resulting datasets.
fastqc SRR519926_1.fastq SRR519926_2.fastq
基于我们上面的原则,我们将需要改变的部分存到变量中,并将变量放到开头:
# The selected SRR number.
RUN=SRR519926
# The number of reads to convert.
LIMIT=10000
# Get and convert the data.
fastq-dump -X ${LIMIT} --split-files ${RUN}
# Run FastQC on the resulting datasets.
fastqc ${RUN}_1.fastq ${RUN}_2.fastq
整个脚本看起来显得更加简洁易懂,并且我们也需要继续遵循一行代码,一行注释,隔空一行的习惯。
报错的程序不会说谎
报错的程序不会说谎,是编程领域内一句很经典的名言。如果你写的程序报错了,最好的办法就是马上将它停下来,而不是明知道错还继续跑。另外我们不希望累积错误,或者将报错忽略。
以下面的代码为例子,如果你比对过程中,比对了一段时间后,由于某种原因出现了报错,这不完整的sam文件将会被继续用于一系列的下游分析,尽管所有命令都跑完了,但是结果是错的。如果你没有发现,进而很有可能能会导致你错误地解析你的数据。
###map the reads to the ref genome
bowtie2 -I 0 -X 1000 -x DM -1 R1.fastq -2 R2.fastq --end-to-end --sensitive --threads 4 -S sample1.sam
#get forward reads and get reverse read
samtools view -bS -f 68 sample1.sam> sample1_forwards.bam
samtools view -bS -f 132 sample1.samm> sample1_reverse.bam
#merge forward reads and reverse read file
samtools merge sample.merged.bam sample1_forwards.bam sample1_reverse.bam
解决的办法也很简单在你编写的每个脚本前加上一行:
set -ue
这样只要你的程序报错了,你的脚本就会停下来,不会继续执行。另外如果你想追踪你脚本中运行的每行代码,可以多加一个
-
x
参数:
set -uex
如何在运行命令时将变量变成参数添加?
这里我们可以通过使用position变量来传递外部变量。
$1