更新 shell 教程的代码和文档

This commit is contained in:
Zhang Peng 2019-03-01 15:27:33 +08:00
parent 100fbc5c85
commit b8b2670aab
4 changed files with 219 additions and 167 deletions

View File

@ -33,7 +33,7 @@
#### [Linux 脚本编程](docs/linux/scripts)
- [Shell](docs/linux/scripts/shell.md)
- [一篇文章让你彻底掌握 shell 语言](docs/linux/scripts/shell.md)
- [Python](docs/linux/scripts/python.md)
#### [Linux 运维](docs/linux/ops)

View File

@ -1,36 +1,23 @@
#!/usr/bin/env bash
runner() {
return 0
}
x=0
if [[ -n $1 ]]; then
echo "第一个参数为:$1"
x=$1
else
echo "第一个参数为空"
fi
y=0
if [[ -n $2 ]]; then
echo "第二个参数为:$2"
y=$2
else
echo "第二个参数为空"
fi
name=zp
paramsFunction(){
echo "函数第一个入参:$1"
echo "函数第二个入参:$2"
echo "传递到脚本的参数个数:$#"
echo "所有参数:"
printf "+ %s\n" "$*"
echo "脚本运行的当前进程 ID 号:$$"
echo "后台运行的最后一个进程的 ID 号:$!"
echo "所有参数:"
printf "+ %s\n" "$@"
echo "Shell 使用的当前选项:$-"
runner
echo "runner 函数的返回值:$?"
}
paramsFunction 1 "abc" "hello, \"zp\""
# Output:
# 函数第一个入参1
# 函数第二个入参abc
# 传递到脚本的参数个数3
# 所有参数:
# + 1 abc hello, "zp"
# 脚本运行的当前进程 ID 号26400
# 后台运行的最后一个进程的 ID 号:
# 所有参数:
# + 1
# + abc
# + hello, "zp"
# Shell 使用的当前选项hB
# runner 函数的返回值0
paramsFunction ${x} ${y}

View File

@ -0,0 +1,36 @@
#!/usr/bin/env bash
runner() {
return 0
}
name=zp
paramsFunction(){
echo "函数第一个入参:$1"
echo "函数第二个入参:$2"
echo "传递到脚本的参数个数:$#"
echo "所有参数:"
printf "+ %s\n" "$*"
echo "脚本运行的当前进程 ID 号:$$"
echo "后台运行的最后一个进程的 ID 号:$!"
echo "所有参数:"
printf "+ %s\n" "$@"
echo "Shell 使用的当前选项:$-"
runner
echo "runner 函数的返回值:$?"
}
paramsFunction 1 "abc" "hello, \"zp\""
# Output:
# 函数第一个入参1
# 函数第二个入参abc
# 传递到脚本的参数个数3
# 所有参数:
# + 1 abc hello, "zp"
# 脚本运行的当前进程 ID 号26400
# 后台运行的最后一个进程的 ID 号:
# 所有参数:
# + 1
# + abc
# + hello, "zp"
# Shell 使用的当前选项hB
# runner 函数的返回值0

View File

@ -17,63 +17,63 @@
<!-- TOC depthFrom:2 depthTo:3 -->
- [简介](#简介)
- [什么是 shell](#什么是-shell)
- [什么是 shell 脚本](#什么是-shell-脚本)
- [Shell 环境](#shell-环境)
- [模式](#模式)
- [基本语法](#基本语法)
- [解释器](#解释器)
- [注释](#注释)
- [echo](#echo)
- [printf](#printf)
- [变量](#变量)
- [变量命名原则](#变量命名原则)
- [声明变量](#声明变量)
- [只读变量](#只读变量)
- [删除变量](#删除变量)
- [变量类型](#变量类型)
- [变量示例源码](#变量示例源码)
- [脚本参数](#脚本参数)
- [字符串](#字符串)
- [单引号和双引号](#单引号和双引号)
- [拼接字符串](#拼接字符串)
- [获取字符串长度](#获取字符串长度)
- [截取子字符串](#截取子字符串)
- [查找子字符串](#查找子字符串)
- [字符串示例源码](#字符串示例源码)
- [数组](#数组)
- [创建数组](#创建数组)
- [访问数组元素](#访问数组元素)
- [访问数组长度](#访问数组长度)
- [向数组中添加元素](#向数组中添加元素)
- [从数组中删除元素](#从数组中删除元素)
- [数组示例源码](#数组示例源码)
- [运算符](#运算符)
- [算术运算符](#算术运算符)
- [关系运算符](#关系运算符)
- [布尔运算符](#布尔运算符)
- [逻辑运算符](#逻辑运算符)
- [字符串运算符](#字符串运算符)
- [文件测试运算符](#文件测试运算符)
- [控制语句](#控制语句)
- [条件语句](#条件语句)
- [循环语句](#循环语句)
- [函数](#函数)
- [函数处理参数](#函数处理参数)
- [Shell 扩展](#shell-扩展)
- [流和重定向](#流和重定向)
- [输入、输出流](#输入输出流)
- [重定向](#重定向)
- [`/dev/null` 文件](#devnull-文件)
- [Debug](#debug)
- [更多内容](#更多内容)
- [1. 简介](#1-简介)
- [1.1. 什么是 shell](#11-什么是-shell)
- [1.2. 什么是 shell 脚本](#12-什么是-shell-脚本)
- [1.3. Shell 环境](#13-shell-环境)
- [1.4. 模式](#14-模式)
- [2. 基本语法](#2-基本语法)
- [2.1. 解释器](#21-解释器)
- [2.2. 注释](#22-注释)
- [2.3. echo](#23-echo)
- [2.4. printf](#24-printf)
- [3. 变量](#3-变量)
- [3.1. 变量命名原则](#31-变量命名原则)
- [3.2. 声明变量](#32-声明变量)
- [3.3. 只读变量](#33-只读变量)
- [3.4. 删除变量](#34-删除变量)
- [3.5. 变量类型](#35-变量类型)
- [3.6. 变量示例源码](#36-变量示例源码)
- [4. 字符串](#4-字符串)
- [4.1. 单引号和双引号](#41-单引号和双引号)
- [4.2. 拼接字符串](#42-拼接字符串)
- [4.3. 获取字符串长度](#43-获取字符串长度)
- [4.4. 截取子字符串](#44-截取子字符串)
- [4.5. 查找子字符串](#45-查找子字符串)
- [4.6. 字符串示例源码](#46-字符串示例源码)
- [5. 数组](#5-数组)
- [5.1. 创建数组](#51-创建数组)
- [5.2. 访问数组元素](#52-访问数组元素)
- [5.3. 访问数组长度](#53-访问数组长度)
- [5.4. 向数组中添加元素](#54-向数组中添加元素)
- [5.5. 从数组中删除元素](#55-从数组中删除元素)
- [5.6. 数组示例源码](#56-数组示例源码)
- [6. 运算符](#6-运算符)
- [6.1. 算术运算符](#61-算术运算符)
- [6.2. 关系运算符](#62-关系运算符)
- [6.3. 布尔运算符](#63-布尔运算符)
- [6.4. 逻辑运算符](#64-逻辑运算符)
- [6.5. 字符串运算符](#65-字符串运算符)
- [6.6. 文件测试运算符](#66-文件测试运算符)
- [7. 控制语句](#7-控制语句)
- [7.1. 条件语句](#71-条件语句)
- [7.2. 循环语句](#72-循环语句)
- [8. 函数](#8-函数)
- [8.1. 位置参数](#81-位置参数)
- [8.2. 函数处理参数](#82-函数处理参数)
- [9. Shell 扩展](#9-shell-扩展)
- [10. 流和重定向](#10-流和重定向)
- [10.1. 输入、输出流](#101-输入输出流)
- [10.2. 重定向](#102-重定向)
- [10.3. `/dev/null` 文件](#103-devnull-文件)
- [11. Debug](#11-debug)
- [12. 更多内容](#12-更多内容)
<!-- /TOC -->
## 简介
## 1. 简介
### 什么是 shell
### 1.1. 什么是 shell
- Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。
- Shell 既是一种命令语言,又是一种程序设计语言。
@ -81,13 +81,13 @@
Ken Thompson 的 sh 是第一种 Unix ShellWindows Explorer 是一个典型的图形界面 Shell。
### 什么是 shell 脚本
### 1.2. 什么是 shell 脚本
Shell 脚本shell script是一种为 shell 编写的脚本程序,一般文件后缀为 `.sh`
业界所说的 shell 通常都是指 shell 脚本,但 shell 和 shell script 是两个不同的概念。
### Shell 环境
### 1.3. Shell 环境
Shell 编程跟 java、php 编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。
@ -127,7 +127,7 @@ Shell 的解释器种类众多,常见的有:
>
> 这样做的好处是,系统会自动在 `PATH` 环境变量中查找你指定的程序(本例中的`bash`)。相比第一种写法,你应该尽量用这种写法,因为程序的路径是不确定的。这样写还有一个好处,操作系统的`PATH`变量有可能被配置为指向程序的另一个版本。比如,安装完新版本的`bash`,我们可能将其路径添加到`PATH`中,来“隐藏”老版本。如果直接用`#!/bin/bash`,那么系统会选择老版本的`bash`来执行脚本,如果用`#!/usr/bin/env bash`,则会使用新版本。
### 模式
### 1.4. 模式
shell 有交互和非交互两种模式。
@ -182,9 +182,9 @@ echo "Hello, world!"
上面的例子中,我们使用了一个很有用的命令`echo`来输出字符串到屏幕上。
## 基本语法
## 2. 基本语法
### 解释器
### 2.1. 解释器
前面虽然两次提到了`#!` ,但是本着重要的事情说三遍的精神,这里再强调一遍:
@ -198,7 +198,7 @@ echo "Hello, world!"
#!/usr/bin/env bash
```
### 注释
### 2.2. 注释
注释可以说明你的代码是什么作用,以及为什么这样写。
@ -226,7 +226,7 @@ echo '这是多行注释'
EOF
```
### echo
### 2.3. echo
echo 用于字符串的输出。
@ -295,7 +295,7 @@ echo `pwd`
**:keyboard: 『示例源码』** [echo-demo.sh](https://github.com/dunwu/os-tutorial/tree/master/codes/shell/demos/echo-demo.sh)
### printf
### 2.4. printf
printf 用于格式化输出字符串。
@ -362,20 +362,20 @@ printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
| `\ddd` | 表示 1 到 3 位数八进制值的字符。仅在格式字符串中有效 |
| `\0ddd` | 表示 1 到 3 位的八进制值字符 |
## 变量
## 3. 变量
跟许多程序设计语言一样,你可以在 bash 中创建变量。
Bash 中没有数据类型bash 中的变量可以保存一个数字、一个字符、一个字符串等等。同时无需提前声明变量,给变量赋值会直接创建变量。
### 变量命名原则
### 3.1. 变量命名原则
- 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
- 中间不能有空格,可以使用下划线(\_
- 不能使用标点符号。
- 不能使用 bash 里的关键字(可用 help 命令查看保留关键字)。
### 声明变量
### 3.2. 声明变量
访问变量的语法形式为:`${var}` 和 `$var`
@ -387,7 +387,7 @@ echo ${word}
# Output: hello
```
### 只读变量
### 3.3. 只读变量
使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
@ -398,7 +398,7 @@ readonly rword
# rword="bye" # 如果放开注释,执行时会报错
```
### 删除变量
### 3.4. 删除变量
使用 unset 命令可以删除变量。变量被删除后不能再次使用。unset 命令不能删除只读变量。
@ -412,7 +412,7 @@ echo ${dword}
# Output: (空)
```
### 变量类型
### 3.5. 变量类型
- **局部变量** - 局部变量是仅在某个脚本内部有效的变量。它们不能被其他的程序和脚本访问。
- **环境变量** - 环境变量是对当前 shell 会话内所有的程序或脚本都可见的变量。创建它们跟创建局部变量类似,但使用的是 `export` 关键字shell 脚本也可以定义环境变量。
@ -431,46 +431,13 @@ echo ${dword}
[这里](http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html###sect_03_02_04) 有一张更全面的 Bash 环境变量列表。
### 变量示例源码
### 3.6. 变量示例源码
**⌨️ 『示例源码』** [variable-demo.sh](https://github.com/dunwu/os-tutorial/tree/master/codes/shell/demos/variable-demo.sh)
## 脚本参数
## 4. 字符串
> **位置参数**是在调用一个函数并传给它参数时创建的变量。
位置参数变量表:
| 变量 | 描述 |
| -------------- | ------------------------------ |
| `$0` | 脚本名称 |
| `$1 … $9` | 第 1 个到第 9 个参数列表 |
| `${10} … ${N}` | 第 10 个到 N 个参数列表 |
| `$*` or `$@` | 除了`$0`外的所有位置参数 |
| `$#` | 不包括`$0`在内的位置参数的个数 |
| `$FUNCNAME` | 函数名称(仅在函数内部有值) |
**:keyboard: 『示例源码』** [variable-demo4.sh](https://github.com/dunwu/os-tutorial/tree/master/codes/shell/demos/variable/variable-demo4.sh)
```bash
if [[ -n $1 ]]; then
echo "第一个参数为:$1"
else
echo "第一个参数为空"
fi
if [[ -n $2 ]]; then
echo "第二个参数为:$2"
else
echo "第二个参数为空"
fi
```
执行 `./variable-demo4.sh hello world` ,然后在脚本中通过 `$1`、`$2` ... 读取第 1 个参数、第 2 个参数。。。
## 字符串
### 单引号和双引号
### 4.1. 单引号和双引号
shell 字符串可以用单引号 `''`,也可以用双引号 `“”`,也可以不用引号。
@ -483,7 +450,7 @@ shell 字符串可以用单引号 `''`,也可以用双引号 `“”`,也可
综上,推荐使用双引号。
### 拼接字符串
### 4.2. 拼接字符串
```bash
# 使用单引号拼接
@ -503,7 +470,7 @@ echo ${str3}_${str4}
# hello, black_hello, black
```
### 获取字符串长度
### 4.3. 获取字符串长度
```bash
text="12345"
@ -512,7 +479,7 @@ echo ${#text}
# 5
```
### 截取子字符串
### 4.4. 截取子字符串
```bash
text="12345"
@ -523,7 +490,7 @@ echo ${text:2:2}
从第 3 个字符开始,截取 2 个字符
### 查找子字符串
### 4.5. 查找子字符串
```bash
#!/usr/bin/env bash
@ -538,17 +505,17 @@ echo `expr index "${text}" ll`
查找 `ll` 子字符在 `hello` 字符串中的起始位置。
### 字符串示例源码
### 4.6. 字符串示例源码
**⌨️ 『示例源码』** [array-demo.sh](https://github.com/dunwu/os-tutorial/tree/master/codes/shell/demos/string-demo.sh)
**⌨️ 『示例源码』** [string-demo.sh](https://github.com/dunwu/os-tutorial/tree/master/codes/shell/demos/string-demo.sh)
## 数组
## 5. 数组
bash 只支持一维数组。
数组下标从 0 开始,下标可以是整数或算术表达式,其值应大于或等于 0。
### 创建数组
### 5.1. 创建数组
```bash
# 创建数组的不同方式
@ -556,7 +523,7 @@ nums=([2]=2 [0]=0 [1]=1)
colors=(red yellow "dark blue")
```
### 访问数组元素
### 5.2. 访问数组元素
- **访问数组的单个元素:**
@ -618,7 +585,7 @@ echo ${nums[@]:0:2}
在上面的例子中,`${array[@]}` 扩展为整个数组,`:0:2`取出了数组中从 0 开始,长度为 2 的元素。
### 访问数组长度
### 5.3. 访问数组长度
```bash
echo ${#nums[*]}
@ -626,7 +593,7 @@ echo ${#nums[*]}
# 3
```
### 向数组中添加元素
### 5.4. 向数组中添加元素
向数组中添加元素也非常简单:
@ -639,7 +606,7 @@ echo ${colors[@]}
上面的例子中,`${colors[@]}` 扩展为整个数组,并被置换到复合赋值语句中,接着,对数组`colors`的赋值覆盖了它原来的值。
### 从数组中删除元素
### 5.5. 从数组中删除元素
用`unset`命令来从数组中删除一个元素:
@ -650,13 +617,13 @@ echo ${nums[@]}
# 1 2
```
### 数组示例源码
### 5.6. 数组示例源码
**:keyboard: 『示例源码』** [array-demo.sh](https://github.com/dunwu/os-tutorial/tree/master/codes/shell/demos/array-demo.sh)
## 运算符
## 6. 运算符
### 算术运算符
### 6.1. 算术运算符
下表列出了常用的算术运算符,假定变量 x 为 10变量 y 为 20
@ -716,7 +683,7 @@ fi
# 10 != 20
```
### 关系运算符
### 6.2. 关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
@ -786,7 +753,7 @@ fi
# 10 -le 20: x 小于或等于 y
```
### 布尔运算符
### 6.3. 布尔运算符
下表列出了常用的布尔运算符,假定变量 x 为 10变量 y 为 20
@ -837,7 +804,7 @@ fi
# 10 小于 5 或 20 大于 100 : 返回 false
```
### 逻辑运算符
### 6.4. 逻辑运算符
以下介绍 Shell 的逻辑运算符,假定变量 x 为 10变量 y 为 20:
@ -875,7 +842,7 @@ fi
# 10 -lt 100 || 20 -gt 100 返回 true
```
### 字符串运算符
### 6.5. 字符串运算符
下表列出了常用的字符串运算符,假定变量 a 为 "abc",变量 b 为 "efg"
@ -936,7 +903,7 @@ fi
# abc : 字符串不为空
```
### 文件测试运算符
### 6.6. 文件测试运算符
文件测试运算符用于检测 Unix 文件的各种属性。
@ -1010,9 +977,9 @@ fi
# /etc/hosts 文件存在
```
## 控制语句
## 7. 控制语句
### 条件语句
### 7.1. 条件语句
跟其它程序设计语言一样Bash 中的条件语句让我们可以决定一个操作是否被执行。结果取决于一个包在`[[ ]]`里的表达式。
@ -1104,7 +1071,7 @@ esac
每种情况都是匹配了某个模式的表达式。`|`用来分割多个模式,`)`用来结束一个模式序列。第一个匹配上的模式对应的命令将会被执行。`*`代表任何不匹配以上给定模式的模式。命令块儿之间要用`;;`分隔。
### 循环语句
### 7.2. 循环语句
循环其实不足为奇。跟其它程序设计语言一样bash 中的循环也是只要控制条件为真就一直迭代执行的代码块。
@ -1295,7 +1262,7 @@ done
# 9
```
## 函数
## 8. 函数
bash 函数定义语法如下:
@ -1313,7 +1280,7 @@ bash 函数定义语法如下:
> 3. 函数返回值在调用该函数后通过 `$?` 来获得。
> 4. 所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至 shell 解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
**:keyboard: 『示例源码』** [function-demo.sh](https://github.com/dunwu/os-tutorial/blob/master/codes/shell/demos/shell/demos/function/function-demo.sh)
**:keyboard: 『示例源码』** [function-demo.sh](https://github.com/dunwu/os-tutorial/tree/master/codes/shell/demos//function/function-demo.sh)
```bash
#!/usr/bin/env bash
@ -1364,7 +1331,69 @@ enter second num: 10
the result is: 100
```
### 函数处理参数
### 8.1. 位置参数
**位置参数**是在调用一个函数并传给它参数时创建的变量。
位置参数变量表:
| 变量 | 描述 |
| -------------- | ------------------------------ |
| `$0` | 脚本名称 |
| `$1 … $9` | 第 1 个到第 9 个参数列表 |
| `${10} … ${N}` | 第 10 个到 N 个参数列表 |
| `$*` or `$@` | 除了`$0`外的所有位置参数 |
| `$#` | 不包括`$0`在内的位置参数的个数 |
| `$FUNCNAME` | 函数名称(仅在函数内部有值) |
**:keyboard: 『示例源码』** [function-demo2.sh](https://github.com/dunwu/os-tutorial/tree/master/codes/shell/demos//function/function-demo2.sh)
```bash
#!/usr/bin/env bash
x=0
if [[ -n $1 ]]; then
echo "第一个参数为:$1"
x=$1
else
echo "第一个参数为空"
fi
y=0
if [[ -n $2 ]]; then
echo "第二个参数为:$2"
y=$2
else
echo "第二个参数为空"
fi
paramsFunction(){
echo "函数第一个入参:$1"
echo "函数第二个入参:$2"
}
paramsFunction ${x} ${y}
```
执行结果:
```bash
$ ./function-demo2.sh
第一个参数为空
第二个参数为空
函数第一个入参0
函数第二个入参0
$ ./function-demo2.sh 10 20
第一个参数为10
第二个参数为20
函数第一个入参10
函数第二个入参20
```
执行 `./variable-demo4.sh hello world` ,然后在脚本中通过 `$1`、`$2` ... 读取第 1 个参数、第 2 个参数。。。
### 8.2. 函数处理参数
另外,还有几个特殊字符用来处理参数:
@ -1378,7 +1407,7 @@ the result is: 100
| `$-` | 返回 Shell 使用的当前选项,与 set 命令功能相同。 |
| `$?` | 函数返回值 |
**:keyboard: 『示例源码』** [function-demo2.sh](https://github.com/dunwu/os-tutorial/blob/master/codes/shell/demos/shell/demos/function/function-demo2.sh)
**:keyboard: 『示例源码』** [function-demo3.sh](https://github.com/dunwu/os-tutorial/tree/master/codes/shell/demos//function/function-demo3.sh)
```bash
runner() {
@ -1417,7 +1446,7 @@ paramsFunction 1 "abc" "hello, \"zp\""
# runner 函数的返回值0
```
## Shell 扩展
## 9. Shell 扩展
_扩展_ 发生在一行命令被分成一个个的 _记号tokens_ 之后。换言之,扩展是一种执行数学运算的机制,还可以用来保存命令的执行结果,等等。
@ -1498,7 +1527,7 @@ cat "$FILE" ### 输出一个文件: `Favorite Things.txt`
尽管这个问题可以通过把 FILE 重命名成`Favorite-Things.txt`来解决,但是,假如这个值来自某个环境变量,来自一个位置参数,或者来自其它命令(`find`, `cat`, 等等)呢。因此,如果输入 _可能_ 包含空格,务必要用引号把表达式包起来。
## 流和重定向
## 10. 流和重定向
Bash 有很强大的工具来处理程序之间的协同工作。使用流,我们能将一个程序的输出发送到另一个程序或文件,因此,我们能方便地记录日志或做一些其它我们想做的事。
@ -1506,7 +1535,7 @@ Bash 有很强大的工具来处理程序之间的协同工作。使用流,我
学习如何使用这些强大的、高级的工具是非常非常重要的。
### 输入、输出流
### 10.1. 输入、输出流
Bash 接收输入,并以字符序列或 **字符流** 的形式产生输出。这些流能被重定向到文件或另一个流中。
@ -1518,7 +1547,7 @@ Bash 接收输入,并以字符序列或 **字符流** 的形式产生输出。
| `1` | `stdout` | 标准输出 |
| `2` | `stderr` | 标准错误输出 |
### 重定向
### 10.2. 重定向
重定向让我们可以控制一个命令的输入来自哪里,输出结果到什么地方。这些运算符在控制流的重定向时会被用到:
@ -1547,7 +1576,7 @@ grep da * 2> errors.txt
less < errors.txt
```
### `/dev/null` 文件
### 10.3. `/dev/null` 文件
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null
@ -1563,7 +1592,7 @@ $ command > /dev/null
$ command > /dev/null 2>&1
```
## Debug
## 11. Debug
shell 提供了用于 debug 脚本的工具。
@ -1645,7 +1674,7 @@ printf "\n"
# Output: 12345
```
## 更多内容
## 12. 更多内容
> :notebook: 本文已归档到:[notes](https://github.com/dunwu/notes)