0%

Shell 脚本 101(1):开启 Shell 编程之旅

当今,Unix-Like 操作系统比以往任何时候都使用地更为广泛。的确,如果把 Android 智能手机也计算在内的话,那么它也许就是世界上最普遍的操作系统了。尽管 Unix-Like 操作系统发生了很多变化,但是 Bourne-again shell,或者称为 bash,始终都是 Unix 用户最喜欢的 shell。

在系统管理员、工程师或者业余爱好者的技能箱中,火力全开地使用 bash 永远都是一项必备技能。

本系列文章不是 bash 的入门知识。相反,我将通过编写一系列简短精炼的脚本,来扎扎实实地提高自己的 bash 编程功底。

Shell 脚本简介

Bash 已经存在很长时间了,每天都会有新同学了解 shell 脚本编程以及通过 bash 实现系统自动化。随着 Microsoft 在 Windows 10 中发布交互式 bash 以及 Unix 子系统,现在的确是学习 shell 的最佳时间。

我们所说的 shell 就是操作系统提供的一个命令行接口,在 shell 中可以执行一系列命令。而 shell 脚本其实就是文本文件,其会包含一系列命令,命令的执行顺序和其在脚本中的顺序一致。

有多种命令行 shell,包括 tcsh、zsh 以及最流行的 bash。本系列文章主要专注于 bash。

运行命令

Bash 的核心功能就是在你的系统中运行命令。以经典的输出 Hello World 为例:

1
2
$ echo "Hello World"
Hello World

当执行这些命令时,bash 会按顺序搜索 PATH 环境变量中所指定的目录,从而找到该命令对应的应用程序进行执行。

1
2
$ echo $PATH
/opt/rh/rh-ruby23/root/usr/local/bin:/opt/rh/rh-ruby23/root/usr/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/fuchencong/.local/bin:/home/fuchencong/bin

如果你想知道某个命令对应的应用程序,可以通过 which 命令实现:

1
2
$ which echo
/bin/echo

配置你的登录 shell

当你打开一个命令行 shell 时,第一件事就是读取你 home 目录下的登录脚本。不同的系统,其登录脚本文件可能是 .login.profile.bashrc 或者 .bash_profile

在 Linux 中,登录脚本文件是 .bash_profile,所以无论是通过终端或远程 ssh 登录时,.bash_profile 都会执行。而在启动非登录 shell 时 .bash_rc 才会执行,例如登录完成后在 shell 中调用 /bin/bash 创建一个新的 bash,此时 .bash_rc 文件才会执行。通常将个人配置放置在 .bashrc 文件中,而在 .bash_profile 中调用 .bashrc,这样就可以确保无论何时你的 bash 行为都将一致。

可以在配置脚本中修改各种 bash 配置,包括设置 PATH 环境变量。有时候我们编写的脚本可能需要被其他脚本被调用,为了能够方便地调用新脚本,可以在 home 目录中创建一个目录,用于保存新开发的脚本,然后在 PATH 环境变量中添加该目录。

运行 shell 脚本

以下是一个示例 shell 脚本:

1
2
3
echo "Hello World"
echo $(which neqn)
cat $(which neqn)

可以看到该 shell 脚本其实就是一系列命令的集合,第一个命令简单地打印 Hello World,第二个命令输出 neqn 命令的路径,第三个命令输出 neqn 命令的内容。后两个命令使用到了 bash 中的 subshell 技巧,通过 $(command) 的形式在一个子 shell 中执行 command 命令并保存其结果,同时又将其命令结果作为其他命令的参数,在这个例子中,将 neqn 命令的路径分别作为 echo 和 cat 命令的参数。

将该脚本文件保存为 intro.sh 并通过如下方式执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ sh intro.sh
Hello World
/bin/neqn
#! /bin/sh
# Provision of this shell script should not be taken to imply that use of
# GNU eqn with groff -Tascii|-Tlatin1|-Tutf8|-Tcp1047 is supported.

GROFF_RUNTIME="${GROFF_BIN_PATH=/usr/bin}:"
PATH="$GROFF_RUNTIME$PATH"
export PATH
exec eqn -Tascii ${1+"$@"}

# eof

让 shell 脚本更直观

并不一定要通过 sh script 的方式来执行 script。可以在 shell 脚本文件的首行添加解释器,对于 bash 脚本即为 #! /bin/bash,同时为该脚本文件设置可执行权限,之后就可以直接执行该脚本了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ cat intro.sh
#!/bin/bash

echo "Hello World"
echo $(which neqn)
cat $(which neqn)

$ chmod +x intro.sh
$ ./intro.sh
Hello World
/bin/neqn
#! /bin/sh
# Provision of this shell script should not be taken to imply that use of
# GNU eqn with groff -Tascii|-Tlatin1|-Tutf8|-Tcp1047 is supported.

GROFF_RUNTIME="${GROFF_BIN_PATH=/usr/bin}:"
PATH="$GROFF_RUNTIME$PATH"
export PATH
exec eqn -Tascii ${1+"$@"}

# eof

添加的 #!/bin/bash 称为 shebang,它是一种机制,允许为你的脚本指定解释器。对于 bash 脚本就是指定 /bin/bash 作为解释器,对于 Python 脚本,则指定 python 程序作为解释器。

对于编写 bash 脚本而言,指定 shebang 是一个好的编程习惯。

Why Shell Scripts

相比于 Ruby、Python、Go 等编程语言,为什么要使用 bash 脚本编程呢?虽然这些编程语言都为跨平台提供了可移植性,但是这些语言在不同系统中并不一定安装了,而每个 Unix-Like 操作系统都有一个基本 shell,而使用最为广泛的 shell 就是 bash 了。尽管 bash 在某些方面仍有不足,但是通过本系列文章你可以学习如何消除其中一些缺点。

Let’s Get Cracking

现在应该对 shell 脚本有了基本的了解。创建脚本以完成特定的任务是 Unix 哲学的核心之一,接下来将开始本系列文章的核心内容:通过 101 个非常酷的 shell 脚本来提高 bash 编程能力。