目录

6.1 shell 脚本简介

shell 脚本简介

/images/linux_mt/linux_mt.jpg

本章我们将开始学习 bash shell 编程。bash shell 是一门编程语言,内容庞大,按照课程的设计应该循序渐进逐步深入。但是为便于以后复查参考,会将所有 bash shell 相关的知识放在此章节中。本章我们将学习以下内容:

  1. 变量与逻辑运算
  2. 循环与条件判断
  3. 函数和位置参数

程序的学习不言而喻是为了提高运维的效率,如果我们是管理几台主机不会shell 编程可能无所谓,但是当我们管理的主机达到几百甚至几千台时,如果没有自动化运维工具和编程的基础的化,可能就只能睡在公司了。Python 和 bash shell 都是自动化运维非常常用的脚本语言,希望大家多多学习。有两本书推荐给大家

  • 《Linux命令行和shell编程宝典》
  • 《abs-guide》
  • 《高级bash编程指南》

1. 程序的分类

shell 脚本是一个比较特殊的编程语言,组成脚本的基本内容并不是通常意义上的函数或库而是 Linux 上的所有命令,所以即便只是将一条条 bash 命令堆砌在一起也可以称为 shell 脚本。因此可以将 shell 脚本看作只是在 bash 命令上添加了编程语言的特性而以。本节我们将对 shell 脚本做一个简单概述,让大家对编程能有个概括性的了解。

按照不同的分类标准,程序可以做不同的分类。根据运行方式,程序可以分为:

  1. 编译运行:源代码 –> 由编译器编译成可执行的二进制文件,然后运行;
  2. 解释运行:源代码 –> 运行时启动解释器,由解释器边解释边运行;

程序=指令+数据,按照程序是以指令为中心组织的,还是按照数据为中心组织,将程序分为:

  1. 过程式编程语言:以指令为中心来组织代码,数据是服务于代码;
  2. 面向对象的编程语言:以数据为中心来组织代码,围绕数据来组织指令;

我们的 shell脚本则属于过程式,解释运行的,利用系统上的命令及编程组件进行编程的编程语言。即脚本的基本组件是系统上的所有命令以及用户自定的函数,通过顺序,判断和循环来组织命令按照特定的逻辑运行即可。

2. 如何学习编程

网络上有个很有争议性的人物叫王垠,写过一篇文章叫《如何掌握所有的程序语言》。其核心的观念是学习程序语言重要的是学习语言特性而且是重要的语言特性,而不是语言本身。什么是语言特性,我从中摘录了他列举的示例

  • 变量定义
  • 算术运算
  • for 循环语句,while 循环语句
  • 函数定义,函数调用
  • 递归
  • 静态类型系统
  • 类型推导
  • lambda 函数
  • 面向对象
  • 垃圾回收
  • 指针算术
  • goto 语句

按照他所说重要的语言特性就像是计算机的基本组件,而程序语言则是在选择不同的语言特性的基础上组装起来的计算机。作为初学者,可能很难深刻理解他表述的含义,但是他列举的语言特性,却可以给我们学习程序语言提供一个很好的思路。我们也将按照类似的顺序学习 sell 脚本编程。本节我们来讲解 shell 中的变量,以及如何创建一个简单的 shell 脚本并运行。

1. 变量

1.1 变量的语言特性

在静态的编译语言和动态的脚本语言中,变量的概念并不完全相同。暂时大家可以理解为,变量是命名的内存空间,有类型之分。变量的类型有非常重要的作用,用于确定变量内容的 存储格式、数据范围和能参与的运算 等等。

与变量有关的语言特性包括

  1. 变量在使用前是否需要声明
  2. 强类型变量还是弱类型变量
    • 强类型变量: 变量类型一旦确定不能改变,也不能将不同类型的变量相互运算
    • 弱类型变量: 变量类型转换没有限制,不同类型之间的运算可能发生隐式转换
  3. 变量的作用域,这通常与变量的第一次出现的位置或声明方式有关。
  4. 变量引用,及如何获取变量的值。shell 比较特殊,需要特定的方式才能引用到变量的值
  5. 变量的命名规则,这在所有编程语言中大体是相同的。
    • 变量名只能包含数字、字母和下划线,而且不能以数字开头
    • 不能够使用程序的保留字
    • 变量名最好能见名知义,并遵循某种命名法则,比如驼峰或者下划线;

1.2 shell 变量的语言特性

shell 中的变量则具有以下语言特性

  1. 变量无需声明,可直接使用
  2. 弱类型变量,无特殊声明默认把所有变量统统视作字符型;
  3. 变量引用:${var_name}, $var_name
  4. 有只读变量,只读变量无法重新赋值,无法撤销;存活时间为当前shell进程的生命周期,随shell进程终止而终止
  5. 变量赋值时,"=" 两边不能有空格,否则最左侧的变量名将被当作命令被解释并执行
1
2
3
4
> ls=1
> ls = 1
ls: 无法访问=: 没有那个文件或目录
ls: 无法访问1: 没有那个文件或目录

1.3 shell 变量的作用域

bash 中变量有三种不同的作用域:

  1. 本地变量:作用域仅为当前shell进程;除非特殊声明,所有变量均为本地变量
  2. 环境变量:作用域为当前shell进程及其子进程;需要特殊声明
  3. 局部变量:作用域仅为某代码片断(函数上下文);

1.4 查看与销毁

shell 中变量的查看和销毁有如下几个命令:

  1. 查看环境变量
    1. export
    2. declare -x
    3. printenv, env
  2. 查看所有变量:set
  3. 撤销变量:unset name

1.5 变量声明

bash 中变量声明的命令有 declare, export, readonly,它们都是 bash 的内置命令,用法如下

export [name[=value]

  • 不带参数显示所有变量及其内容
  • 带参数用于声明环境变量

readonly [-aAf] [name[=value] ...]

  • 不带参数显示所有只读变量
  • 带参数用于声明只读变量

declare/typeset [-aixr] [variable=[value]]:

  • 默认:显示所有的变量及其内容,类似于 set
  • -x:与export 一样,将后面的变量转换成环境变量
  • +x:将 - 变成 + 可以进行取消操作
  • -a:声明数组类型 array
  • -i:声明整数类型 interger
  • -r:将变量设置成只读类型
  • -p:后接变量,单独列出变量的类型
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 声明环境变量
export name=value

name=value
export name

declare -x name=value

name=value
declare -x name

# 声明只读变量,声明和赋值可同时进行
declare -r name[=value]
readonly name[=value]

1.5 shell 中的特殊变量

shell 还有一些特殊变量,有特殊公用,列示如下:

  1. 位置参数变量: 保存了传递给 shell 脚本的参数;
  2. shell内置的有特殊功用的环境变量,通常为全大写字符,用于定义bash的工作环境,比如
    • PATH: 命令查找路经
    • HISTFILE, HISTSIZE, HISTFILESIZE, HISTCONTROL: 命令历史的控制参数
    • SHELL, HOME, UID: 当前用户的 shell类型,家目录以及UID 号
    • PWD, OLDPWD: 当前以及之前的工作目录
  3. 特殊变量:
    • $?: 上一条命令的执行状态

2. 如何写shell脚本

2.1 hello world 的 shell 脚本

脚本文件的第一行顶格,给出解释器路径,用于指明解释执行当前脚本的解释器程序文件。常见的解释器包括

  1. #!/bin/bash: bash shell 的解释器
  2. #!/usr/bin/python: python 的解释器
  3. #!/usr/bin/perl: perl 的解释器

一个 hello world 的shell 脚本如下:

1
2
3
#!/bin/bash

echo "hello world"

2.2 运行脚本

bash 中运行脚本有两种方式:

  1. 赋予执行权限,并直接运行此程序文件;
  2. 直接运行解释器,将脚本以命令行参数传递给解释器程序;
1
2
3
4
5
6
# 方法一: 赋予可执行权限,直接运行
> chmod +x /PATH/TO/SCRIPT_FILE
> /PATH/TO/SCRIPT_FILE

# 方法二: 调用 bash 运行
> bash /PATH/TO/SCRIPT_FILE

2.3 bash 调试

  • bash -n script.sh – 检查脚本语法错误
  • bash -x script.sh – 单步执行,显示代码执行的详细过程

3. 练习

1
2
3
4
练习1:写一个脚本,实现如下功能;
(1) 显示/etc目录下所有以大写p或小写p开头的文件或目录本身;
(2) 显示/var目录下的所有文件或目录本身,并将显示结果中的小写字母转换为大写后显示;
(3) 创建临时文件/tmp/myfile.XXXX;