Shell:脚本知识
非常多的朋友在看我们公众号过往转录组,WES,等流程分享的时候发现很难理解我们的代码,其实就是缺乏shell脚本知识,那么这篇教程你就不容错过。
内容
- 使用多个命令
- 创建脚本文件
- 显示消息
- 使用变量
- 输入输出重定向
- 管道
- 数学运算
- 退出脚本
一个脚本例子
1 | bed=exon_probe.hg38.gene.bed |
使用多个命令
如果多个命令一起使用,可以放在一行并用分号分隔。
1 | $ date; who |
创建脚本文件
在创建脚本文件时,必须在文件的第一行指定要使用的 shell,格式为:
1 |
脚本文件的第一行中 #
后的惊叹号会告诉shell使用哪个 shell 来运行脚本(如果是其他编码语言脚本,像python,第一行类似)。
其他地方的 #
用作注释行。
添加名为 test1 的脚本文件,内容为:
1 |
|
显示消息
在 echo
命令后面加上一个字符串,就能显示出这个文本字符串。这种方式可以添加自己的文本消息来告诉脚本用户脚本正在做什么。
1 | $ echo This is a test |
如果文本本身带有字符串,我们需要用单引号或双引号来划定文本字符串。
1 | $ echo "Let's see if this'll work" |
修改下之前的test1文件,增加消息显示:
1 |
|
运行:
1 | $ ./test1 |
如果想把文本字符串和命令输出显示在同一行中,可以用 echo
语句的 -n
参数。需要在字符串的两侧加上引号,并且保证字符串尾部有一个空格(不然字符串和命令输出就粘连到一起了)。
1 |
|
使用变量
变量允许我们临时性地将信息存储在shell脚本中,以便和脚本中的其他命令一起使用。
环境变量
shell 维护着一组环境变量,用来记录特定的系统信息。比如系统的名称、登录到系统上的用户名、用户的系统 ID (也称为 UID )、用户默认主目录以及 shell 查找程序的搜索路径。
使用 set
命令显示一份完整的当前环境变量列表。 env
与 printenv
命令都可以显示全局变量。(这些命令输出结果比较多,不展示了。之前关于环境变量的笔记有比较详细的描述。)
在环境变量名称之前加上美元符可以使用这些环境变量。
1 | $ cat test2 |
可以想象的到,如果我们想要使用实际的美元符而不是引用变量,肯定会出问题。这时候我们需要在美元符前面加上 \
进行转义,以显示美元符本身。
用户变量
使用等号将值赋给用户变量。注意,在变量、等号和值之间不能出现空格!
1 | $ cat test3 |
命令替换
shell 脚本最有用的特性之一就是可以从命令输出中提取信息,并将其赋给变量。
有两种方法可以将命令输出赋给变量:
- 反引号字符 (`) [数字1前面的那个键]
$()
格式
1 | # 要么用一对反引号把整个命令行命令围起来: |
下面是一个例子,在脚本中通过命令替换获得当前日期并用它来生成唯一文件名:
1 | $ cat test4 |
重定向输入和输出
通过几个操作符进行重定向,我们可以将命令的结果输出到另外的位置(文件)。当然,重定向可以用于输入。
输出重定向
最基本的操作符是 >。比如我们想要输出命令结果到一个指定文件:
1 | $ date > test6 |
如果想要将命令的输出追加到已有文件中,需要用双大于号>>
来追加数据。
输入重定向
输入重定向和输出重定向正好相反。输入重定向将文件的内容重定向到命令,而非将命令的输出重定向到文件。
使用的符号是小于号<
。
一种简单的记忆方法是:在命令行上,命令总是在左侧,而重定向符号“指向”数据流动的方向。小于号说明数据正在从输入文件流向命令。
比如用 wc
命令检查文本的行数、词数和字节数。
1 | $ wc < test6 |
另一种输入重定向的方法是内联输入重定向。它无需使用文件进行重定向,只需要在命令行中指定用于输入重定向的数据即可。它使用的符号是远小于号 <<
,除了这个符号,我们还需要指定一个文本标记用来划分输入数据的开始和结尾。任何字符串都可以作为文本标记,但在数据的开始和结尾文本标记必须一致。
$ wc << EOF
test string1
test string2
test string3
EOF
3 6 39
它的形式为: command << marker
管道
很多生信命令行工具需要提供多个输入和输出参数,这用在管道命令里可能会导致非常低效的情形(管道只接受一个标准输入和输出)。幸好,我们可以使用命令管道来解决此类问题。
命名管道,也称为 FIFO。它是一个特殊的排序文件,命名管道有点像文件,它可以永久保留在你的文件系统上(估计本质就是文件吧~)。
比如我想查看某个文件(test1)的前两行并进行排序,操作如下:
1 | $ cat test1 |
没看懂这个“命令管道”…
进程替换
有些命令需要接受多个管道的输入作为自己的输出,这个时候普通的管道已经无法完成任务了。需要用到进程替换,来避免多次创建中间文件,代码如下:
1 | start=$(date +%s.%N) |
执行数学运算
对 shell 脚本来说,执行数学运算非常麻烦。有两种实现方式。
expr命令
expr
命令允许在命令行上处理数学表达式,但是特别笨拙。(Bourne shell中)
1 | $ exrpr 1 + 5 |
看到没有,那算了。它基本涉及的操作跟我们使用的其他语言是一致的。但是有些问题需要处理,像 *
是通配符,在运算是是做乘号处理的,需要进行转义。
使用方括号
bash shell 提供了一种更简单的方法来执行数学表达式。在 bash 中,在将一个数学运算结果赋给某个变量时,可以用美元符和方括号$[operator]
将数学表达式围起来。
1 | $ var1=$[1+5] |
但 bash shell 计算有一个主要限制:它只支持整数运算!
浮点解决方案
最常见的方案是用内建的 bash 计算器。它实际上是一门编程语言,它允许在命令行中输入浮点表达式,然后解释并计算该表达式,最后返回结果。bash 计算器能够识别:
- 数字(整数和浮点数)
- 变量(简单变量和数组)
- 注释(/* */开始的行)
- 表达式
- 编程语句
- 函数
1 | $ bc |
在脚本中使用 bc
可以用命令替换运行bc命令,并将输出赋给一个变量。基本格式如下:
1 | variable=$(echo "options; expression" | bc) |
options 设置变量,expression参数定义了通过 bc
执行的数学表达式。
看一个简单实例:
1 | $ cat test9 |
这个例子将 scale 变量设置为四位小数,并在 expression 部分指定了特定的运算。
这个方法适用于较短的运算,但有时我们会涉及更多的数字。如果需要进行大量运算,在一个命令行中列出多个表达式就会有点麻烦。
这里有一个解决方法:使用内联输入重定向,将一个文件重定向到 bc
命令来处理。格式为:
1 | variable=$(bc << EOF |
EOF 文本字符串标识了内联重定向数据的起始。注意,仍然需要命令替换符号将 bc
命令的输出赋给变量。
下面是一个例子:
1 | $ cat test10 |
在普通的 shell 脚本中,数字默认当做字符串处理。这也是为什么我们脚本处理计算麻烦和我们需要特定的工具和方法来进行处理。一定要注意区分。
退出脚本
状态码
前面运行的脚本都是命令执行完成,脚本自动结束。其实我们可以用更为优雅的方式告诉 shell 命令运行完成,因为每个命令都使用退出状态码(exit status),它是一个 0-255 的整数值,我们可以捕获这个值并在脚本中使用。
Linux提供了一个专门的变量$?`来保存上个已执行命令的退出状态码。
1 | $ date |
1 | $ asfg |
exit命令
默认,shell 脚本会以脚本最后的一个命令的退出状态码退出。
但是我们可以改变这种默认行为,返回自己的退出状态码。exit命令允许在脚本结束时指定一个状态退出码。
1 | $ cat test13 |
注意最大 255,如果大于它,得到的是求模的结果(余数)。
脚本高级知识
还有一些脚本高级知识,不予讲解,感兴趣的同学可以自行购买相关书籍和专业视频自学:
- 处理信号
- 以后台模式运行脚本
- 禁止挂起
- 作业控制
- 修改脚本优先级
- 脚本执行自动化
强烈推荐马哥 linux 视频。
转自 - 生信技能树:构建shell脚本一文就够