毫无疑问,UNIX/Linux 下最重要的软件之一就是 shell,目前最流行的 shell 被称为 Bash(Bourne Again Shell)。作为系统和用户之间的交互接口,shell 几乎是你在 UNIX 工作平台上最亲密的朋友。学好 shell 是学习 Linux/UNIX 的开始。
想真正学习脚本编程的唯一途径就是编写脚本。
shell 是什么
shell 是一种命令解释器,是介于操作系统 kernel 和用户之间的一个接口层。一个 shell 程序被称为一个脚本,它可以调用所有 Unix 命令、实用程序以及工具软件。除此之外,shell 还提供内建命令,比如 test 与循环结构等,可以让脚本更加灵活强大。
为什么使用 shell 编程
1 | 没有程序语言是完美的,甚至没有一个唯一的最好的语言。只有在特定环境下适合的语言。 |
只要你想熟悉 Linux 系统管理,学习掌握 shell 脚本编程是必不可少的。Linux 系统本身也存在大量 shell 脚本,比如系统启动时会执行 /etc/rc.d/
目录下的脚本来配置系统环境。
shell 脚本的语法简单直观,编写脚本就像是在命令行中把一些相关命令、工具连接起来,而你只需要遵循很少一部分规则即可。shell 脚本遵循典型的 UNIX 哲学:将大的、复杂的工程划分成简单的子任务,再将这些子任务连接起来。
使用 shell 脚本来构建一个复杂应用的原型是可行的。在使用高级编程语言编写最终代码之前,使用 shell 脚本可以快速测试方案的可行性,提前发现重大缺陷。
那什么时候不应该使用 shell 脚本呢?对于以下场景,不推荐使用 shell 脚本,而应该使用高级编程语言:
- 资源密集型的任务,尤其在需要考虑运行效率
- 要处理大量数学运算,尤其是浮点运算、高精度运算、复数运算等
- 跨平台移植需求
- 必须使用结构化编程的复杂应用
- 影响系统全局的关键应用
- 对于安全有高要求的任务
- 项目包含有连锁依赖关系的组件
- 需要大量的文件操作
- 需要使用多维数组、链表、树等数据结构
- 需要提供或操作图形化界面 GUI
- 需要直接操作系统硬件或外部设备
- 需要 I/O 或 socket 编程接口
- 需要使用库或者旧代码接口
- 私有的、非开源应用(shell 脚本直接将源代码公开)
Bash 是 Bourne-Again shell
的缩写,它是对 Bourne shell
(称为 sh
)的改进。现在 Bash 已经成为了绝大多数 Unix-Like
操作系统中 shell 事实上的标准了。
带着一个Sha-Bang 出发
最简单的 shell 脚本其实就是将一堆系统命令存放在一个文件中,它至少有一个好处:减少重复输入这一系列命令。如下 cleanup.sh
就是一个简单的脚本文件。
1 | cd /var/log |
根据惯例,用户编写的 Bash 脚本应该以 .sh
作为文件扩展名,而一些系统脚本,例如 /etc/rc.d
中的脚本,则通常不遵循这种命名规范。
Linux 系统下的脚本文件一般以 #!/bin/bash
作为起始行,这其实是告诉操作系统以指定的解释器程序 /bin/bash
来执行该文件:
#!
读作Sha-Bang
或者She-Bang
,是一个两个字节的魔数字,是Unix-Like
系统下指定解释器程序的一种特殊语法#!
后面紧跟着的是解释器程序的路径名,操作系统会以该路径指定的程序来执行该文件- 指定的程序可以是某种 shell,也可以是其他任意程序,例如 Python 等等
#!
是*nix
操作系统提供的通用特性,用来为脚本文件指定解释器程序
如下 cat.me
文件指定 cat
程序来执行这个输入文件的内容,其最终结果就是输出整个文件的内容:
1 | #!/usr/bin/cat |
1 | # chmod +x cat.me |
对于 bash 脚本,我们就是通过 #!/bin/bash
来指定脚本文件的解释器程序。bash 执行脚本文件时,首先解释第一行 #!/bin/bash
,由于它是以 #
开头,bash 会认为该行是注释行而直接忽略该行。
需要注意,如果使用的是 #!/bin/sh
,则调用的是系统的默认 shell 解释器,它并不一定总是 bash
。
1 | # ls -l /bin/sh |
所以如下是一个改进后的 cleaup.sh
:
1 |
|
使用 #!
来显式指定解释器程序总是一种好的编程习惯,如果脚本的起始行没有使用 #!
,此时到底使用哪个解释器是比较复杂的,这就可能会导致一些莫名奇妙的问题。另外,如果没有正确指定解释器的路径,会出现如下错误:
1 | ./cleanup.sh: /bash: bad interpreter: No such file or directory |
所以有时候也会使用 #!/bin/env bash
来避免硬编码 bash
解释器的路径。
调用一个脚本
编写完脚本之后,可以使用 bash scriptname
来执行该脚本。更方便的方法是使用 chmod a+x
命令给脚本文件增加可执行权限,然后再通过 ./scriptname
的方式来执行它。
当脚本文件测试成功后,可以将其移动到 PATH
环境变量包含的目录中,例如 /usr/local/bin
目录,这样就可以直接在命令行中以 scriptname
的方式执行脚本了,无需指定脚本的路径。
一个更复杂的例子
最后再来看一个更复杂的 shell 脚本,后续会详细介绍该脚本所涉及的知识。
1 |
|