Linux Shell 版本问题
Docker技术给老百姓的生活带来便利可以说非常显著。在过去如果想要安装一个软件,面里的最大问题大概就是相关依赖安装和编译环境,在虚拟化环境下一切就变得井然有序得多,但是Docker技术带来的负面影响就是由于程序是直接封装的,如果非镜像的制作者,难以得知软件运行的细节。这就造成了一系列问题(例如环境变量、shell版本等)。采用Achelous调度系统,可以比较轻松得解决环境变量问题,但是Linux Shell版本的问题,还是需要用户自己留个神。
本文就目前常见的Linux Shell版本进行基础介绍,尤其是对其脚本编写中的语法问题给出一个概要式说明,以资读者放心使用。
1.1 Linux Shell 是个啥?
简单来说Linux Shell是用户与内核交互的渠道。在生物信息现实应用上,Shell 除了命令行交互模式之外,也用做一遍批处理脚本的编写。其可以实现一般程序控制语言常见的顺序执行、条件判断和循环操作。
1.2 Shell 也存在版本差异
与Linux一样,Shell也是存在发行版本差异的。目前相对比较常见的类型有以下几类:
sh
:编写脚本最为常见的就是/usr/bin/sh
其正式名称为 Bourne Shell。完全符合POSIX shell 规范。bash
:日常登录的交互界面,一般是/bin/bash
其全称为Bourne Again Shell。不完全符合POSIX shell 规范,因此语法相对而言更加灵活。csh
: csh是 C shell的简称,也是相对比较古早的shell版本,可以视为Bourne Shell的前身。由于其版本古早,因此在实践上不推荐基于该版本进行编程。感兴趣的同志们可以仔细阅读“不推荐使用csh编程的十个理由”dash
: 正式名称为 Debian Almquist shell,该shell的特点就是相对占用空间较小,但是功能较少,语法也比正常的sh要矫情(严格)一些。其语法规范完全符合POSIX shell。这点与sh
相同。
总得来说,shell的版本其实林林总总,但是共性大于差异性。但是魔鬼总是隐藏在细节之中,虽然语法大体上大差不差,但是在个别地方还是需要使用者留神注意。
2 不同版本之间的shell的语法差异
各个版本的shell的语法之间存在细微的差异。总体上,可以把shell分为两类:POSIX Shell标准语法shell(例如:sh、dash)和附加功能的shell(最典型的就是bash)。两者基本一致,比如对于变量赋值、循环都支持 while
和 for
等。基本语法十分相似。
附加功能的shell语法上,一般是覆盖 POSIX Shell的,因此为了保证脚本的可移植性,最好不要用一些bash独有的表达方式。下面是POSIX shell基础的表达形式。
#! /usr/bin/bash
### 为变量赋值
export VALUE="my_value_here"
### for 循环
array=( "element1" "element2" "element3" )
for i in "${arr[@]}"
do
echo $i
done
3 最大的陷阱——if
判断
在bash中可以实现if判断。首先要注意的是无论是哪种shell,在用if的时候,都必须在等于号前后加空格。
#! /usr/bin/bash
export flag="hello"
if [ $flag = "hello" ];then
echo "world"
fi
#### 上面这个形式,其实等价于下面的 ##
## 下面这种形式较上面的又三处不同
# 1. 变量可以带双引号
# 2. 判断是否相等可以用双等号
# 3. 判断条件可以用双括号
if [[ "$flag" == "hello" ]];then
echo "world"
fi
但是此处可以看到对于bash 而言,是支持双括号的"[[ ... ]]"。但是POSIX shell 理论上不支持该形式。
4. WDL解决方案
对于基于容器化方案的用户而言,其实我们很难控制镜像中到底安装了哪种shell。比如默认的可能就是个dash
。一些基于bash的脚本放进来之后,在容器内测试完全没问题,但是一执行,就会发现判断失效了。
这种情况,建议您尝试通过wdl
语言来规避不同镜像的差异,采用统一的作业编排语言,来对执行顺序进行编排。这样可以有效的规避shell不同版本带来的困扰。