上传文件至 /
This commit is contained in:
parent
94429534f1
commit
3c2f1a0fa3
225
shell-括号.md
Normal file
225
shell-括号.md
Normal file
@ -0,0 +1,225 @@
|
||||
<h2><center>Shell 括号</center></h2>
|
||||
|
||||
------
|
||||
|
||||
## 一:括号介绍
|
||||
|
||||
### 1. 圆括号
|
||||
|
||||
| 特性 | () | (()) |
|
||||
| :----------: | :-------------------------------------: | :--------------------------------------: |
|
||||
| **用途** | 子 Shell 执行、命令替换、数组定义 | 整数算术运算、循环条件 |
|
||||
| **执行环境** | 在子 Shell 中运行,不共享当前变量 | 在当前 Shell 中执行算术操作 |
|
||||
| **变量影响** | 子 Shell 内变量修改不影响父 Shell | 直接修改当前 Shell 的变量 |
|
||||
| **示例** | `(a=1; echo $a)`(退出后 `a` 恢复原值) | `((a=5+3))`(直接赋值给 `a`) |
|
||||
| **逻辑运算** | 不适用 | 支持 `&&`, ` |
|
||||
| **返回值** | 返回子 Shell 中最后一条命令的退出码 | 返回算术表达式是否为真(0为真,非0为假) |
|
||||
|
||||
### 2. 方括号
|
||||
|
||||
| 特性 | `[ ]`(test 命令) | `[[ ]]`(Bash 增强条件) |
|
||||
| :------------: | :----------------------------: | :-------------------------------------: |
|
||||
| **兼容性** | POSIX 标准,所有 Shell 支持 | Bash/Ksh/Zsh 特有,非 POSIX |
|
||||
| **字符串比较** | 用 `=` 或 `!=`,需严格转义空格 | 直接支持 `==`、`!=`,无需转义 |
|
||||
| **模式匹配** | 不支持通配符(需用 `case`) | 支持通配符(如 `[[ $file == *.txt ]]`) |
|
||||
| **正则匹配** | 不支持 | 支持 `=~`(如 `[[ "abc" =~ ^a ]]`) |
|
||||
| **逻辑运算符** | 必须用 `-a`(AND)、`-o`(OR) | 直接使用 `&&`、` |
|
||||
| **变量处理** | 变量未加引号可能导致语法错误 | 自动处理变量中的空格和特殊字符 |
|
||||
| **数值比较** | 用 `-eq`、`-lt` 等 | 也可用 `-eq`,但推荐 `(( ))` |
|
||||
| **示例** | `[ "$a" -eq 5 -a "$b" != "" ]` | `[[ $str == "yes" && $num > 10 ]]` |
|
||||
|
||||
### 3. 花括号
|
||||
|
||||
| 特性 | 用途 | 示例 |
|
||||
| :------------: | :-------------------------: | :------------------------------------------: |
|
||||
| **代码块** | 组合命令(当前 Shell 执行) | `{ cmd1; cmd2; } > log.txt` |
|
||||
| **序列生成** | 生成数字/字母序列 | `echo {1..5}` → `1 2 3 4 5` |
|
||||
| **文件名扩展** | 快速枚举文件或路径 | `cp file.{txt,bak}` → `cp file.txt file.bak` |
|
||||
| **变量操作** | 字符串处理、默认值替换等 | `${var:-default}`(空值时替换) |
|
||||
| **注意** | 代码块首尾必须有空格和分号 | `{ echo "Start"; ls; }` |
|
||||
|
||||
### 4. 总对比
|
||||
|
||||
| 括号类型 | 核心用途 | 关键区别 | 示例 |
|
||||
| :------: | :------------------------: | :---------------------------: | :----------------------: |
|
||||
| `()` | 子 Shell、数组、命令替换 | 子 Shell 中运行,不共享变量 | `(cd /tmp; ls)` |
|
||||
| `(( ))` | 整数算术运算、循环条件 | 直接操作变量,支持 C 风格语法 | `for ((i=0; i<10; i++))` |
|
||||
| `[ ]` | 基础条件测试(兼容 POSIX) | 严格空格,需转义符号 | `[ -f file ]` |
|
||||
| `[[ ]]` | 增强条件测试(Bash 特有) | 支持通配符、正则、逻辑运算符 | `[[ $str =~ ^[0-9]+$ ]]` |
|
||||
| `{}` | 代码块、序列生成、变量扩展 | 不创建子 Shell,直接扩展内容 | `echo {A..C}` → `A B C` |
|
||||
|
||||
### 5. 常见误区
|
||||
|
||||
1. 空格要求:
|
||||
- `[ ]` 和 `[[ ]]` 内部必须保留空格:`[ "$a" = 5 ]` ✔️,`[$a=5]` ❌。
|
||||
- `(( ))` 和 `[[ ]]` 中的变量无需 `$` 符号:`((a++))` ✔️,`(($a++))` ❌。
|
||||
2. 字符串比较:
|
||||
- `[ ]` 中必须用引号包裹变量,避免空值错误:`[ "$str" = "hello" ]`。
|
||||
- `[[ ]]` 中可不加引号(自动处理):`[[ $str == hello ]]`。
|
||||
3. 数值比较:
|
||||
- `[ ]` 必须用 `-eq`、`-lt` 等操作符:`[ $a -gt 10 ]`。
|
||||
- `[[ ]]` 和 `(( ))` 中可直接用 `>`、`<`:`[[ $a > 10 ]]` 或 `((a > 10))`。
|
||||
4. 逻辑运算符:
|
||||
- `[ ]` 中用 `-a`(AND)、`-o`(OR):`[ $a -eq 1 -o $b -eq 2 ]`。
|
||||
- `[[ ]]` 中用 `&&`、`||`:`[[ $a == 1 && $b == 2 ]]`。
|
||||
|
||||
## 二:示例
|
||||
|
||||
### 1. 圆括号
|
||||
|
||||
**用途**
|
||||
|
||||
- **子 Shell 执行**:在独立环境中运行命令,不影响当前 Shell。
|
||||
- **令替换**:捕获命令输出。
|
||||
- **数组定义**:初始化数组。
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 子 Shell 示例:临时切换目录,不影响当前 Shell
|
||||
[root@wxin ~]# (current_dir=$(pwd); cd /tmp; ls; cd "$current_dir")
|
||||
|
||||
# 命令替换:获取当前日期
|
||||
[root@wxin ~]# today=$(date +"%Y-%m-%d")
|
||||
[root@wxin ~]# echo "今天是$today"
|
||||
今天是2025-03-26
|
||||
|
||||
# 数组定义
|
||||
[root@wxin ~]# fruits=("apple" "banana" "cherry")
|
||||
[root@wxin ~]# echo "第二个水果是 ${fruits[1]}"
|
||||
第二个水果是 banana
|
||||
```
|
||||
|
||||
### 2. 双括号
|
||||
|
||||
**用途**
|
||||
|
||||
- **整数算术运算**:替代 `expr`,支持 C 语言风格语法。
|
||||
- **循环条件**:简化 `for`/`while` 循环。
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 算术运算
|
||||
[root@wxin ~]# (( sum = 3+5*2 ))
|
||||
[root@wxin ~]# echo "结果是 $sum"
|
||||
结果是 13
|
||||
|
||||
# 自增操作
|
||||
[root@wxin ~]# count=0
|
||||
[root@wxin ~]# (( count++ ))
|
||||
[root@wxin ~]# echo "计数: $count"
|
||||
计数: 1
|
||||
|
||||
# 循环条件
|
||||
[root@wxin ~]# for (( i=0;i<3;i++ ));do
|
||||
> echo "循环第 $i 次"
|
||||
> done
|
||||
循环第 0 次
|
||||
循环第 1 次
|
||||
循环第 2 次
|
||||
```
|
||||
|
||||
### 3. 方括号
|
||||
|
||||
**用途**
|
||||
|
||||
- **条件测试**:兼容 POSIX 的基础条件判断。
|
||||
- **文件检查**:测试文件属性(如是否存在、是否可读)。
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 数值比较
|
||||
[root@wxin ~]# if [ "$a" -eq 10 ];then echo "a 等于 10"; fi
|
||||
a 等于 10
|
||||
|
||||
# 文件存在性检查
|
||||
[root@wxin ~]# if [ -f "/etc/passwd" ];then
|
||||
> echo "文件存在"
|
||||
> fi
|
||||
文件存在
|
||||
|
||||
# 逻辑组合(使用 -a/-o)
|
||||
[root@wxin ~]# if [ "$a" -gt 5 -a "$a" -lt 15 ];then
|
||||
> echo "a 在 5 到 15 之间"
|
||||
> fi
|
||||
a 在 5 到 15 之间
|
||||
```
|
||||
|
||||
### 4. 双括号
|
||||
|
||||
**用途**
|
||||
|
||||
- **增强条件测试**:支持通配符、正则表达式和逻辑运算符。
|
||||
- **模式匹配**:直接使用 `==` 或 `=~`。
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 字符串匹配(支持通配符)
|
||||
[root@wxin ~]# file="image.jpg"
|
||||
[root@wxin ~]# if [[ "$file" == *.jpg ]];then
|
||||
> echo "这是一个 JPEG 文件"
|
||||
> fi
|
||||
这是一个 JPEG 文件
|
||||
|
||||
# 正则表达式匹配
|
||||
[root@wxin ~]# phone="123-456-7890"
|
||||
[root@wxin ~]# if [[ "$phone" =~ ^[0-9]{3}-[0-9]{3}-[0-9]{4}$ ]];then
|
||||
> echo "有效的电话号码"
|
||||
> fi
|
||||
有效的电话号码
|
||||
|
||||
# 逻辑运算符(直接使用 &&/||)
|
||||
[root@wxin ~]# str="hello"
|
||||
[root@wxin ~]# num=8
|
||||
[root@wxin ~]# if [[ "$str" == "hello" && $num -gt 5 ]];then
|
||||
> echo "条件满足"
|
||||
> fi
|
||||
条件满足
|
||||
```
|
||||
|
||||
### 5. 花括号
|
||||
|
||||
**用途**
|
||||
|
||||
- **代码块**:组合命令(当前 Shell 执行)。
|
||||
- **序列生成**:快速生成数字或字母序列。
|
||||
- **文件名扩展**:批量操作文件。
|
||||
- **变量操作**:字符串处理和默认值替换。
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 代码块(重定向所有输出)
|
||||
[root@wxin ~]# {
|
||||
> echo "操作开始于 $(date)"
|
||||
> ls -l
|
||||
> echo "操作结束"
|
||||
> } > log.txt
|
||||
|
||||
# 序列生成
|
||||
[root@wxin ~]# echo {1..5}
|
||||
1 2 3 4 5
|
||||
[root@wxin ~]# echo {A..C}
|
||||
A B C
|
||||
[root@wxin ~]# echo file{1..3}.txt
|
||||
file1.txt file2.txt file3.txt
|
||||
|
||||
# 文件名扩展(快速复制备份)
|
||||
[root@wxin ~]# cp config.txt{,.bak}
|
||||
|
||||
# 变量默认值替换
|
||||
[root@wxin ~]# unset var
|
||||
[root@wxin ~]# echo "${var:-默认值}"
|
||||
默认值
|
||||
|
||||
# 字符串截取
|
||||
[root@wxin ~]# filename="photo.jpg"
|
||||
[root@wxin ~]# echo "${filename%.*}"
|
||||
photo
|
||||
[root@wxin ~]# echo "${filename##*.}"
|
||||
jpg
|
||||
```
|
||||
|
331
shell-数组.md
Normal file
331
shell-数组.md
Normal file
@ -0,0 +1,331 @@
|
||||
<h2><center>Shell 数组</center></h2>
|
||||
|
||||
------
|
||||
|
||||
## 一:数组
|
||||
|
||||
### 1. 介绍
|
||||
|
||||
Shell 的数组就是把有限个元素(变量或字符内容)用一个名字命名,然后用编号对它们进行区分的元素集合。这个名字就称为数组名,用于区分不同内容的编号就称为数组下标。组成数组的各个元素(变量)称为数组的元素,有时也称为下标变量。有了Shell数组后,就可以用相同名字引用一系列变量及变量值,并通过数字(索引)来识别使用它们。在许多场合,使用数组可以缩短和简化程序开发。数组的本质还是变量,是特殊的变量形式。
|
||||
|
||||
### 2. 数组分类
|
||||
|
||||
- **索引数组:**只能使用整数作为数组索引/下标(从0开始)
|
||||
- **关联数组:**可以使用字符串作为数组索引/下标
|
||||
|
||||
### 3. 索引数组
|
||||
|
||||
#### 数组定义
|
||||
|
||||
**方法一:**用小括号将变量值括起来赋值给数组变量,每个变量值之间要用空格分隔
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# array=(value1 value2 value3 ... )
|
||||
示例:
|
||||
[root@wxin ~]# array=(1 2 3) # 用小括号将数组内容赋值给数组变量,数组元素用“空格”分隔开。
|
||||
[root@wxin ~]# echo ${array[*]} # 输出上面定义的数组的所有元素值,注意语法。
|
||||
1 2 3
|
||||
```
|
||||
|
||||
**方法二:**用小括号将变量值括起来,同时采用键值对的形式赋值
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# array=([0]=one [1]=two [2]=three [3]=four)
|
||||
```
|
||||
|
||||
**方法三:**通过分别定义数组变量的方法来定义
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# array[0]=one
|
||||
[root@wxin ~]# array[1]=two
|
||||
[root@wxin ~]# array[2]=three
|
||||
[root@wxin ~]# array[3]=four
|
||||
[root@wxin ~]# echo ${array[@]} # 查看所有数组的值
|
||||
one two three four
|
||||
```
|
||||
|
||||
**方法四:**动态地定义数组变量,并使用命令的输出结果作为数组的内容
|
||||
|
||||
```bash
|
||||
array=($(命令))
|
||||
array=(`命令`)
|
||||
示例:
|
||||
[root@wxin ~]# array=(`ls ./`)
|
||||
[root@wxin ~]# echo ${array[@]}
|
||||
anaconda-ks.cfg initial-setup-ks.cfg 公共 模板 视频 图片 文档 下载 音乐 桌面
|
||||
```
|
||||
|
||||
#### 常用变量
|
||||
|
||||
```shell
|
||||
${ARRAY_NAME[INDEX]} # 引用数组中的元素 注意:引用时,只给数组名,表示引用下标为0的元素
|
||||
|
||||
${#ARRAY_NAME[*]} # 数组中元素的个数
|
||||
${#ARRAY_NAME[@]} # 数组中元素的个数
|
||||
|
||||
${ARRAY_NAME[*]} # 引用数组中的所有元素
|
||||
${ARRAY_NAME[@]} # 引用数组中的所有元素
|
||||
|
||||
${#ARRAY_NAME} # 数组中下标为 0 的字符个数
|
||||
```
|
||||
|
||||
#### 访问数组
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# array=(one two three four)
|
||||
|
||||
[root@wxin ~]# echo ${array} 打印单个数组元素用${数组名[下标]},当未指定数组下标时,数组的下标是从0开始。
|
||||
one
|
||||
[root@wxin ~]# echo ${array[0]}
|
||||
one
|
||||
|
||||
[root@wxin ~]# echo ${array[*]} # 使用*或者@可以得到整个数组内容。
|
||||
one two three four
|
||||
[root@wxin ~]# echo ${array[@]}
|
||||
one two three four
|
||||
|
||||
[root@wxin ~]# echo ${#array[*]} # 用${#数组名[@或*]}可以得到数组长度,这和前文讲解的变量子串知识是一样的,因为数组也是变量,只不过是特殊的变量,因此也适合变量的子串替换等知识。
|
||||
4
|
||||
[root@wxin ~]# echo ${#array[@]}
|
||||
4
|
||||
```
|
||||
|
||||
#### 数组赋值
|
||||
|
||||
直接通过`数组名[下标]`对数组进行引用赋值,如果下标不存在,自动添加新一个数组元素,如果下标存在就覆盖原来的值
|
||||
|
||||
数组中元素的赋值方式主要有几个方式:
|
||||
|
||||
```bash
|
||||
# 一次只能赋值一个元素
|
||||
[root@wxin ~]# array[index]=value
|
||||
|
||||
# 一次赋值全部元素
|
||||
[root@wxin ~]# array=("value1" "value2" "value3" ...)
|
||||
|
||||
# 只赋值特定元素
|
||||
[root@wxin ~]# array=([0]="value1" [3]="value2" ...)
|
||||
|
||||
# 交互式赋值
|
||||
[root@wxin ~]# read -a array
|
||||
```
|
||||
|
||||
#### 删除数组
|
||||
|
||||
因为数组本质上还是变量,因此可通过`unset 数组[下标]`清除相应的数组元素,如果不带下标,表示清除整个数组的所有数据
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# echo ${array[@]}
|
||||
anaconda-ks.cfg initial-setup-ks.cfg 公共 模板 视频 图片 文档 下载 音乐 桌面
|
||||
[root@wxin ~]# echo ${array[*]}
|
||||
anaconda-ks.cfg initial-setup-ks.cfg 公共 模板 视频 图片 文档 下载 音乐 桌面
|
||||
[root@wxin ~]# unset array[0]
|
||||
[root@wxin ~]# echo ${array[*]}
|
||||
initial-setup-ks.cfg 公共 模板 视频 图片 文档 下载 音乐 桌面
|
||||
[root@wxin ~]# unset array
|
||||
[root@wxin ~]# echo ${array[*]}
|
||||
```
|
||||
|
||||
#### 数组元素删除
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# array=(one two three four five)
|
||||
[root@wxin ~]# echo ${array[@]}
|
||||
one two three four five
|
||||
[root@wxin ~]# echo ${array[@]#*o} # 从每个元素的左边开始匹配最短的,并删除。
|
||||
ne three ur five
|
||||
[root@wxin ~]# echo ${array[@]##*e} # 从每个元素的左边左边开始匹配最长的,并删除。
|
||||
two four
|
||||
[root@wxin ~]# echo ${array[@]%f*} # 从每个元素的右边开始匹配最短的,并删除。
|
||||
one two three
|
||||
[root@wxin ~]# echo ${array[@]%%f*} # 从每个元素的右边开始匹配最长的,并删除。
|
||||
one two three
|
||||
```
|
||||
|
||||
#### 数组截取/切片
|
||||
|
||||
```bash
|
||||
命令格式:
|
||||
${array_name[@]:offset:number}
|
||||
# offset: 要路过的元素个数
|
||||
# number: 要取出的元素个数;省略number时,表示取偏移量之后的所有元素
|
||||
|
||||
|
||||
[root@wxin ~]# array=(1 2 3 4 5)
|
||||
[root@wxin ~]# echo ${array[@]:1:3} # 从下标为1的元素开始截取,共取3个数组元素
|
||||
2 3 4
|
||||
|
||||
[root@wxin ~]# array=({a..z})
|
||||
[root@wxin ~]# echo ${array[@]}
|
||||
a b c d e f g h i j k l m n o p q r s t u v w x y z
|
||||
[root@wxin ~]# echo ${array[@]:3:3} # 从下标为3的元素开始截取,共取3个数组元素。
|
||||
d e f
|
||||
[root@wxin ~]# echo ${array[@]:0:3} # 从下标为0的元素开始截取,共取3个数组元素
|
||||
a b c
|
||||
```
|
||||
|
||||
#### 数组替换
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# array=(1 2 3 4)
|
||||
[root@wxin ~]# echo ${array[@]}
|
||||
1 2 3 4
|
||||
[root@wxin ~]# echo ${array[@]/2/baby} # 把数组中的2替换成baby,原数组未被修改。
|
||||
1 baby 3 4
|
||||
```
|
||||
|
||||
### 4. 遍历数组
|
||||
|
||||
**`for`循环**
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# array=(one two three four five)
|
||||
[root@wxin ~]# for item in "${array[@]}"; do
|
||||
> echo "$item"
|
||||
> done
|
||||
one
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
|
||||
[root@wxin ~]# for((i=0;i<${#array[@]};i++)); do
|
||||
> echo ${array[i]}
|
||||
> done
|
||||
one
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
```
|
||||
|
||||
**`while`循环**
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# i=0
|
||||
[root@wxin ~]# while [ $i -lt ${#array[@]} ]; do
|
||||
> echo ${array[$i]}
|
||||
> let i++
|
||||
> done
|
||||
one
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
|
||||
[root@wxin ~]# i=0
|
||||
[root@wxin ~]# while [[ i -lt ${#array[@]} ]]; do
|
||||
> echo ${array[i]}
|
||||
> let i++
|
||||
> done
|
||||
```
|
||||
|
||||
### 4. 关联数组
|
||||
|
||||
Bash 支持关联数组,它可以使用字符串作为数组索引,关联数组一定要事先声明才行,不然会按照索引数组进行执行
|
||||
|
||||
#### 定义关联数组
|
||||
|
||||
**使用声明语句将一个变量声明为关联数组**。
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# declare -A assArray
|
||||
```
|
||||
|
||||
利用内嵌索引-值列表的方法
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# assArray=([lucy]=beijing [yoona]=shanghai)
|
||||
[root@wxin ~]# echo ${assArray[lucy]}
|
||||
beijing
|
||||
```
|
||||
|
||||
使用独立的索引-值进行赋值
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# assArray[lily]=shandong
|
||||
[root@wxin ~]# assArray[sunny]=xian
|
||||
[root@wxin ~]# echo ${assArray[sunny]}
|
||||
xian
|
||||
[root@wxin ~]# echo ${assArray[lily]}
|
||||
shandong
|
||||
```
|
||||
|
||||
#### 列出数组索引
|
||||
|
||||
每一个数组都有一个索引用于查找。使用${!数组名[@或者*]}获取数组的索引列表
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# echo ${!assArray[*]}
|
||||
lily yoona sunny lucy
|
||||
[root@wxin ~]# echo ${!assArray[@]}
|
||||
lily yoona sunny lucy
|
||||
```
|
||||
|
||||
#### 获取所有键值对
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# vim assArray.sh
|
||||
#!/bin/bash
|
||||
declare -A cityArray
|
||||
cityArray=([yoona]=beijing [lucy]=shanghai [lily]=shandong)
|
||||
for key in ${!cityArray[@]}; do
|
||||
echo "$key come from ${cityArray[$key]}"
|
||||
done
|
||||
|
||||
|
||||
[root@wxin ~]# bash assArray.sh
|
||||
lily come from shandong
|
||||
yoona come from beijing
|
||||
lucy come from shanghai
|
||||
```
|
||||
|
||||
**示例1:**使用循环批量输出数组的元素
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
array=(1 2 3 4 5)
|
||||
for i in ${array[@]}; do
|
||||
echo $i
|
||||
done
|
||||
|
||||
array1=(1 2 3 4 5)
|
||||
for ((i=0;i<${#array1[@]};i++)); do
|
||||
echo ${array[i]}
|
||||
done
|
||||
|
||||
array2=(1 2 3 4 5)
|
||||
i=0
|
||||
while ((i<${#array2[@]})); do
|
||||
echo ${array2[i]}
|
||||
let i++
|
||||
done
|
||||
```
|
||||
|
||||
**示例2:**通过竖向列举法定义数组元素并批量打印
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
array=(one two three four five)
|
||||
for ((i=0;i<${#array[@]};i++)); do
|
||||
echo "This is num $i, then content is ${array[i]}"
|
||||
done
|
||||
```
|
||||
|
||||
**示例3:**把命令结果作为数组元素定义并打印
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
array=(`ls ./`)
|
||||
for ((i=0;i<${#array[@]};i++)); do
|
||||
echo "This is num $i, then content is ${array[i]}"
|
||||
done
|
||||
echo ==========================================
|
||||
array2=(`ls ./`)
|
||||
num=1
|
||||
for i in ${array[@]}; do
|
||||
echo "This is num $num, then content is $i"
|
||||
let num++
|
||||
done
|
||||
```
|
||||
|
89
shell-正则.md
Normal file
89
shell-正则.md
Normal file
@ -0,0 +1,89 @@
|
||||
<h2><center>Shell 正则</center></h2>
|
||||
|
||||
------
|
||||
|
||||
## 一:正则表达式
|
||||
|
||||
### 1. 正则应用环境
|
||||
|
||||
重要的文本处理工具:vim、sed、awk、grep
|
||||
|
||||
各种语言和应用程序: mysql、oracle、php、python、Apache、Nginx ...
|
||||
|
||||
### 2. 正则介绍
|
||||
|
||||
正则表达式(Regular Expression,通常简称为 regex 或 RE)是一种字符表达方式,可以用它来查找匹配特定准则的文本。在许多编程语言中都有用到正则表达式,常用它来实现一些复杂的匹配。这里简单介绍一下 shell 中常用到的一些正则表达式。
|
||||
|
||||
正则表达式是对字符串进行操作的一种逻辑公式,即用事先定义好的的一些特定字符以及这些特定字符的组合,组成一个有一定规则的字符串(Regular Expression),使用这个有一定规则的字符串来表达对字符串的一种过滤逻辑。正则表达式被广泛应用于Linux和许多其他编程语言中,而且不论在哪里,其基本原理都是一样的。
|
||||
|
||||
正则表达式是由两个基本组成部分所建立:一般字符与特殊字符。一般字符是指没有任何特殊意义的字符;特殊字符,常称为元字符 (metacharacter),或 meta 字符,正则表达式将匹配被查找行中任何位置出现的相同模式。在正则表达式中,元字符是最重要的概念。在某些情况下,特殊字符也可被视为一般字符(使用转义符 \ 进行转义)。
|
||||
|
||||
POSIX 有两种风格的正则表达式,基本正则表达式(BRE)和扩展正则表达式(ERE)。这两种风格的正则表达式在一些字符含义上有细微的差距。以常用的 grep 指令来说,grep 指令默认支持的是 BRE,若要使用 ERE 进行匹配,可以使用 -E 选项,接下来的例子中均使用 grep 指令来演示正则表达式的使用。
|
||||
|
||||
案例:
|
||||
|
||||
```shell
|
||||
匹配数字: ^[0-9]+$
|
||||
匹配Mail: [a-z0-9_]+@[a-z0-9]+\.[a-z]+
|
||||
匹配IP: [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}
|
||||
|
||||
[root@wxin ~]# egrep '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' /etc/sysconfig/network-scripts/ifcfg-ens33
|
||||
IPADDR=192.168.159.130
|
||||
GATEWAY=192.168.159.2
|
||||
NETMASK=255.255.255.0
|
||||
DNS1=192.168.159.2
|
||||
```
|
||||
|
||||
### 3. 元字符
|
||||
|
||||
- shell 元字符(也称为通配符)
|
||||
- 正则表达式元字符
|
||||
|
||||
案例:
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# rm -rf *.pdf
|
||||
|
||||
|
||||
[root@wxin ~]# grep 'abc*' /etc/passwd
|
||||
abrt:x:173:173::/etc/abrt:/sbin/nologin
|
||||
```
|
||||
|
||||
### 4. 正则表达式元字符
|
||||
|
||||
#### 1. 基础元字符
|
||||
|
||||
| 元字符 | 作用 | 示例 |
|
||||
| :----: | :------------------------------------------------------: | -------------------------- |
|
||||
| `.` | 匹配任意单个字符 | `a.c`→`"abc","a1c"` |
|
||||
| `^` | 匹配行开头 | `^start`→行以`"start"`开头 |
|
||||
| `$` | 匹配行结尾 | `end$` → 行以 "end" 结尾 |
|
||||
| `[]` | 匹配字符组中的任意一个字符 | `[aeiou]` → 任意元音字母 |
|
||||
| `[^]` | 匹配不在字符组中的任意一个字符 | `[^0-9]` → 非数字字符 |
|
||||
| `\` | 转义字符,将元字符转为普通字符,或将普通字符转为特殊功能 | `a\.b` → 字面量 `"a.b"` |
|
||||
|
||||
#### 2. 量词元字符
|
||||
|
||||
| 元字符 | 作用 | 示例 |
|
||||
| :-----: | :------------------------: | :---------------------------: |
|
||||
| `*` | 前字符出现 **0 次或多次** | `a*b` → "b", "ab", "aaab" |
|
||||
| `+` | 前字符出现 **1 次或多次** | `a+b` → "ab", "aaab" |
|
||||
| `?` | 前字符出现 **0 次或 1 次** | `colou?r` → "color", "colour" |
|
||||
| `{n,m}` | 前字符出现 **n 到 m 次** | `a{2}` → "aa" |
|
||||
|
||||
#### 3. 分组与逻辑
|
||||
|
||||
| 元字符 | 作用 | 示例 |
|
||||
| :----: | :------------------: | :-----------------------: |
|
||||
| `()` | **分组**,捕获子模式 | `(ab)+` → "ab", "abab" |
|
||||
| `|` | **逻辑或** | `at|dog` → "cat" 或 "dog" |
|
||||
|
||||
#### 4. 预定义字符类
|
||||
|
||||
| 元字符 | 作用 | 示例 |
|
||||
| :----: | :------------------------: | :-------------------: |
|
||||
| `\d` | 数字(部分工具支持) | `\d+` → "123" |
|
||||
| `\w` | 单词字符(字母数字下划线) | `\w+` → "hello123" |
|
||||
| `\s` | 空白字符(空格、制表符等) | `\s+` → 匹配空白 |
|
||||
| `\b` | 单词边界 | `\bword\b` → 独立单词 |
|
||||
|
536
shell-流程控制.md
Normal file
536
shell-流程控制.md
Normal file
@ -0,0 +1,536 @@
|
||||
<h2><center>Shell 流程控制</center></h2>
|
||||
|
||||
------
|
||||
|
||||
## 一:条件测试
|
||||
|
||||
### 1. 测试方法
|
||||
|
||||
| 方法 | 描述 |
|
||||
| :--------------: | :----------------------------------------------------------: |
|
||||
| test 测试表达式 | 利用test命令进行条件测试表达式,test命令与测试表达式之间至少有一个空格 |
|
||||
| [ 测试表达式 ] | 通过[ ]中括号进行条件测试表达式,[]中括号边界与测试表达式之间至少有一个空格 |
|
||||
| [[ 测试表达式 ]] | 通过[[ ]]双中括号进行条件测试表达式,[[ ]]双中括号与测试表达式之间至少有一个空格 |
|
||||
| ((测试表达式)) | 通过(( ))双小括号进行条件测试表达式,( ))双小括号两端不需要空格,常用于整数对比 |
|
||||
|
||||
### 2. 文件测试
|
||||
|
||||
**语法**
|
||||
|
||||
```shell
|
||||
# test 操作符 文件或目录
|
||||
|
||||
# [ 操作符 文件或目录]
|
||||
```
|
||||
|
||||
| 操作符 | 描述 |
|
||||
| ----------- | -------------------------------------------------- |
|
||||
| **-d 文件** | 文件存在且为目录则为真 |
|
||||
| **-f 文件** | 文件存在且为普通文件则为真 |
|
||||
| **-e 文件** | 文件存在则为真,不辩别是目录还是文件 |
|
||||
| **-s 文件** | 文件存在且文件大小不为0则为真 |
|
||||
| **-r 文件** | 文件存在且可读则为真,与执行脚本的用户权限也有关 |
|
||||
| **-w 文件** | 文件存在且可写则为真,与执行脚本的用户权限也有关 |
|
||||
| **-x 文件** | 文件存在且可执行则为真,与执行脚本的用户权限也有关 |
|
||||
| -L 文件 | 文件存在且为链接文件则为真 |
|
||||
| f1 -nt f2 | 文件f1比文件f2新则为真,根据文件的修改时间计算 |
|
||||
| f1 -ot f2 | 文件f1比文件f2旧则为真,根据文件的修改时间计算 |
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# test -d /home
|
||||
[root@wxin ~]# echo $?
|
||||
0
|
||||
[root@wxin ~]# test -d /home111
|
||||
[root@wxin ~]# echo $?
|
||||
1
|
||||
|
||||
[root@wxin ~]# [ ! -d /ccc ]
|
||||
[root@wxin ~]# [ ! -d /ccc ] && mkdir /ccc
|
||||
[root@wxin ~]# [ -d /ccc ] || mkdir /ccc
|
||||
```
|
||||
|
||||
### 3. 数字比较
|
||||
|
||||
**语法**
|
||||
|
||||
```shell
|
||||
# test 数字 操作符 数字
|
||||
# test $((变量)) 操作符 $((变量))
|
||||
|
||||
# [ 数字 操作符 数字 ]
|
||||
# [ $((变量)) 操作符 $((变量)) ]
|
||||
|
||||
# [[ 数字 操作符 数字 ]]
|
||||
# [[ $"变量" 操作符 $"变量" ]]
|
||||
# [[ $((变量)) 操作符 $((变量)) ]]
|
||||
|
||||
# ((数字 操作符 数字))
|
||||
# (($"变量" 操作符 $"变量"))
|
||||
```
|
||||
|
||||
| **在[]、[[ ]]和test中使用** | **在(( ))中使用** | **说明** |
|
||||
| --------------------------- | ----------------- | -------------------------------- |
|
||||
| -eq | ==或= | 等于,全拼为equal |
|
||||
| -ne | != | 不等于,全拼为not equal |
|
||||
| -gt | > | 大于,全拼为greater than |
|
||||
| -ge | >= | 大于等于,全拼为greater or equal |
|
||||
| -lt | < | 小于,全拼为less than |
|
||||
| -le | <= | 小于等于,全拼为less or equa |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# test 1 -gt 0;echo $?
|
||||
0
|
||||
[root@wxin ~]# test $((num1)) -gt $((num2));echo $?
|
||||
1
|
||||
|
||||
|
||||
[root@wxin ~]# [ 1 -gt 0 ];echo $?
|
||||
0
|
||||
[root@wxin ~]# [ $((num1)) -gt $((num2)) ];echo $?
|
||||
1
|
||||
|
||||
[root@wxin ~]# [[ 1 -lt 2 ]];echo $?
|
||||
0
|
||||
[root@wxin ~]# [[ $"num1" != $"num2" ]];echo $?
|
||||
0
|
||||
[root@wxin ~]# [[ $((num1)) -eq $((num2)) ]];echo $?
|
||||
1
|
||||
|
||||
[root@wxin ~]# ((1 == 2));echo $?
|
||||
1
|
||||
[root@wxin ~]# (($"num1"<$"num2"));echo $?
|
||||
0
|
||||
```
|
||||
|
||||
### 4. 字符串比较
|
||||
|
||||
语法:
|
||||
|
||||
```shell
|
||||
# test 参数 字符串
|
||||
# test 字符串1 参数 字符串
|
||||
|
||||
# [ 参数 字符串 ]
|
||||
# [ 字符串1 参数 字符串2 ]
|
||||
```
|
||||
|
||||
| 参数 | 示例 | 功能 |
|
||||
| --------- | ------ | -------------------------------------------- |
|
||||
| **-z** | s1 | 如果字符串s1的长度为0,则测试条件为真 |
|
||||
| **-n** | s1 | 如果字符串s1的长度大于0,则测试条件为真 |
|
||||
| | s1 | 如果字符串s1不是空字符串,则测试条件为真 |
|
||||
| **=或==** | s1=s2 | 如果s1等于s2,则测试条件为真,“=”前后应有空格 |
|
||||
| **!=** | s1!=s2 | 如果s1不等于s2,则测试条件为真 |
|
||||
| < | s1<s2 | 如果按字典顺序s1在s2之前,则测试条件为真 |
|
||||
| > | s1>s2 | 如果按自定顺序s1在s2之后,则测试条件为真 |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# str="12345"
|
||||
[root@wxin ~]# str2="namespace"
|
||||
|
||||
[root@wxin ~]# test -z "$str";echo $?
|
||||
1
|
||||
[root@wxin ~]# test "$str";echo $?
|
||||
0
|
||||
|
||||
[root@wxin ~]# [ -z "$str" ];echo $?
|
||||
1
|
||||
[root@wxin ~]# [ "$str" != "$str2" ];echo $?
|
||||
0
|
||||
```
|
||||
|
||||
### 5. 逻辑操作符
|
||||
|
||||
| **在[]和test中使用** | **在[[ ]]和(( ))中使用** | **说明** |
|
||||
| -------------------- | ------------------------ | ---------------------------------- |
|
||||
| -a | && | and,与,两端都为真,则结果为真 |
|
||||
| -o | \|\| | or,或,两端有一个为真,则结果为真 |
|
||||
| ! | ! | not,非,两端相反,则结果为真 |
|
||||
|
||||
## 二:条件判断
|
||||
|
||||
### 1. 流程控制
|
||||
|
||||
在一个shell脚本中的命令执行顺序称作脚本的流;大多数脚本会根据一个或多个条件来改变它们的流。
|
||||
|
||||
流控制命令:能让脚本的流根据条件而改变的命令称为条件流控制命令。
|
||||
|
||||
exit语句:退出程序的执行,并返回一个返回码,返回码为0正常退出,非0为非正常退出。
|
||||
|
||||
条件判断:if代码返回0表示真,非0为假。
|
||||
|
||||
### 2. 语法结构
|
||||
|
||||
**单分支结构**
|
||||
|
||||
```shell
|
||||
if [ 条件判断式 ]; then
|
||||
条件成立时,执行的程序
|
||||
fi
|
||||
```
|
||||
|
||||
**双分支结构**
|
||||
|
||||
```shell
|
||||
if [ 条件判断式 ]; then
|
||||
条件成立时,执行的程序
|
||||
else
|
||||
条件不成立时,执行的另一个程序
|
||||
fi
|
||||
```
|
||||
|
||||
**多分支结构**
|
||||
|
||||
```bash
|
||||
if [ 条件判断式1 ]; then
|
||||
当条件判断式1成立时,执行程序1
|
||||
elif [ 条件判断式2 ]; then
|
||||
当条件判断式2成立时,执行程序2
|
||||
...省略更多条件....
|
||||
else
|
||||
当所有条件都不成立,最后执行此程序
|
||||
fi
|
||||
```
|
||||
|
||||
**case**
|
||||
|
||||
```shell
|
||||
case $变量名 in
|
||||
值1)
|
||||
如果变量的值等于值1则执行指令1
|
||||
;;
|
||||
值2)
|
||||
如果变量的值等于值2则执行指令2
|
||||
;;
|
||||
值3)
|
||||
如果变量的值等于值3则执行指令3
|
||||
;;
|
||||
*)
|
||||
如果变量的值不等于以上列出的任何值则执行默认指令
|
||||
esac
|
||||
```
|
||||
|
||||
## 三:循环结构
|
||||
|
||||
### 1. for 循环
|
||||
|
||||
**语法结构**
|
||||
|
||||
```bash
|
||||
for 变量 in 值集合
|
||||
do
|
||||
执行命令
|
||||
done
|
||||
```
|
||||
|
||||
- for 每次从值集合中取一个值赋值给变量
|
||||
- do - done 将赋值后的变量带入执行的命令得到执行结果
|
||||
- 重复以上两个步骤,直到值集合中的值被一一获取赋值给变量的到所有结果,循环结束
|
||||
|
||||
### 2. while 循环
|
||||
|
||||
**语法结构**
|
||||
|
||||
```shell
|
||||
while 条件测试
|
||||
do
|
||||
执行命令
|
||||
done
|
||||
```
|
||||
|
||||
- while 首先进行条件测试,如果传回值为0(条件测试为真),则进入循环,执行命令区域,否则不进入循环。
|
||||
- 满足 while 测试条件,执行命令区域,直到 while 的测试条件不满足结束执行while循环(如果条件一直满足执行无穷循环)。
|
||||
|
||||
### 3. until 循环
|
||||
|
||||
**语法结构**
|
||||
|
||||
```shell
|
||||
until 条件测试
|
||||
do
|
||||
执行命令
|
||||
done
|
||||
```
|
||||
|
||||
- until 条件测试结果为假(传回值不为0),就进入循环。
|
||||
- 条件测试不满足,执行命令区域。直到 until 条件满足,结束执行until 循环(如果条件一直不满足则执行无穷循环)。
|
||||
|
||||
## 四:循环控制
|
||||
|
||||
### 1. break
|
||||
|
||||
**语法结构**
|
||||
|
||||
```shell
|
||||
break n # n 表示跳出循环的次数, 如果省略n表示跳出单个循环
|
||||
```
|
||||
|
||||
**示例**
|
||||
|
||||
```bash
|
||||
for i in {1..3}; do
|
||||
for j in {1..3}; do
|
||||
for k in {1..3}; do
|
||||
if [ $k -eq 2 ]; then
|
||||
break 2
|
||||
fi
|
||||
echo "i=$i,j=$j,k=$k"
|
||||
done
|
||||
done
|
||||
done
|
||||
|
||||
输出:
|
||||
i=1, j=1, k=1
|
||||
i=2, j=1, k=1
|
||||
i=3, j=1, k=1
|
||||
|
||||
break 2 # 跳出2次循环
|
||||
|
||||
for i in {1..3}; do
|
||||
for j in {1..3}; do
|
||||
for k in {1..3}; do
|
||||
if [ $k -eq 2 ]; then
|
||||
break
|
||||
fi
|
||||
echo "i=$i,j=$j,k=$k"
|
||||
done
|
||||
done
|
||||
done
|
||||
|
||||
输出:
|
||||
i=1, j=1, k=1
|
||||
i=1, j=2, k=1
|
||||
i=1, j=3, k=1
|
||||
i=2, j=1, k=1
|
||||
i=2, j=2, k=1
|
||||
i=2, j=3, k=1
|
||||
i=3, j=1, k=1
|
||||
i=3, j=2, k=1
|
||||
i=3, j=3, k=1
|
||||
```
|
||||
|
||||
### 2. continue
|
||||
|
||||
**语法结构**
|
||||
|
||||
```shell
|
||||
continue n # 跳转到第n层封闭循环的下一个迭代
|
||||
```
|
||||
|
||||
**示例**
|
||||
|
||||
```bash
|
||||
for i in {1..3}; do
|
||||
for j in {1..3}; do
|
||||
if [ $j -eq 2]; then
|
||||
continue
|
||||
fi
|
||||
echo "i=$i, j=$j"
|
||||
done
|
||||
done
|
||||
|
||||
输出:
|
||||
i=1, j=1
|
||||
i=1, j=3
|
||||
i=2, j=1
|
||||
i=2, j=3
|
||||
i=3, j=1
|
||||
i=3, j=3
|
||||
|
||||
for i in {1..3}; do
|
||||
for j in {1..3}; do
|
||||
if [ $j -eq 2]; then
|
||||
continue 2
|
||||
fi
|
||||
echo "i=$i, j=$j"
|
||||
done
|
||||
done
|
||||
|
||||
输出:
|
||||
i=1, j=1
|
||||
i=2, j=1
|
||||
i=3, j=1
|
||||
```
|
||||
|
||||
### 3. exit
|
||||
|
||||
**语法格式**
|
||||
|
||||
```shell
|
||||
exit # 退出循环
|
||||
```
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# vim exit.sh
|
||||
#!/bin/bash
|
||||
for i in {1..5}; do
|
||||
for j in {1..5}; do
|
||||
if [ $j -eq 3 ]; then
|
||||
exit
|
||||
fi
|
||||
echo "$i,$j"
|
||||
done
|
||||
done
|
||||
[root@wxin ~]# bash exit.sh
|
||||
1,1
|
||||
1,2
|
||||
```
|
||||
|
||||
## 五:shift 指令
|
||||
|
||||
shift 命令用于将参数列表 list 左移指定次数,最左端的那个参数就从列表中删除,其后边的参数继续进入循环。
|
||||
|
||||
shift[N]:用于将参量列表 list 左移指定次数,缺省为左移一次。
|
||||
|
||||
参量列表 list 一旦被移动,最左端的那个参数就从列表中删 除。while 循环遍历位置参量列表时,常用到 shift
|
||||
|
||||
**语法格式**
|
||||
|
||||
```shell
|
||||
shift [n] # 默认 n=1,即左移 1 位;n 可以指定移动的位数
|
||||
```
|
||||
|
||||
**示例**
|
||||
|
||||
```bash
|
||||
# vim demo.sh
|
||||
#!/bin/bash
|
||||
while [ $# -gt 0 ]
|
||||
do
|
||||
echo $*
|
||||
shift
|
||||
done
|
||||
|
||||
# ./demo.sh a b c d e f g h
|
||||
a b c d e f g h
|
||||
b c d e f g h
|
||||
c d e f g h
|
||||
d e f g h
|
||||
e f g h
|
||||
f g h
|
||||
g h
|
||||
h
|
||||
```
|
||||
|
||||
## 六:格式化打印
|
||||
|
||||
### 1. 语法格式
|
||||
|
||||
```shell
|
||||
printf "格式字符串" 参数1 参数2 ...
|
||||
```
|
||||
|
||||
- **格式字符串**:包含格式说明符(如 `%s`, `%d`, `%f`),用于定义输出样式。
|
||||
- **参数**:按顺序替换格式字符串中的说明符。
|
||||
- **自动换行**:`printf` **不会自动换行**,需手动添加 `\n`。
|
||||
|
||||
### 2. 格式说明符
|
||||
|
||||
| 说明符 | 用途 | 示例 |
|
||||
| ------ | ------------- | --------------------- |
|
||||
| `%s` | 字符串 | `printf "%s" "Hello"` |
|
||||
| `%d` | 整数 | `printf "%d" 42` |
|
||||
| `%f` | 浮点数 | `printf "%f" 3.14` |
|
||||
| `%%` | 输出 `%` 符号 | `printf "%%"` |
|
||||
| `\n` | 换行 | `printf "%d\n" 42` |
|
||||
| `\f` | 换页 | `printf "%d\f" 42` |
|
||||
| `\r` | 回车 | `printf "%d\r" 42` |
|
||||
|
||||
### 3. 格式控制
|
||||
|
||||
**字段宽度与对齐**
|
||||
|
||||
- **右对齐**:默认行为,指定宽度(如 `%10s`)。
|
||||
- **左对齐**:用 `-` 符号(如 `%-10s`)。
|
||||
- **填充字符**:默认用空格,可以用 `0` 填充数字(如 `%05d`)。
|
||||
|
||||
```bash
|
||||
printf "右对齐: |%10s|\n" "Text" # | Text|
|
||||
printf "左对齐: |%-10s|\n" "Text" # |Text |
|
||||
printf "零填充: %05d\n" 7 # 00007
|
||||
```
|
||||
|
||||
**浮点数精度**
|
||||
|
||||
- 控制小数位数:`%.Nf`(`N` 为保留的小数位数)。
|
||||
|
||||
```bash
|
||||
printf "保留两位小数: %.2f\n" 3.1415 # 3.14
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 七:颜色
|
||||
|
||||
### 1. 语法格式
|
||||
|
||||
使用 `\033[` 或 `\e[` 作为转义序列的起始符,格式为:
|
||||
|
||||
```shell
|
||||
echo -e "\033[样式代码;前景色;背景色m文本内容\033[0m"
|
||||
```
|
||||
|
||||
- **`-e` 选项**:允许 `echo` 解析转义字符(必须加上)。
|
||||
- **`\033[0m`**:重置所有样式(避免后续文本也应用颜色)。
|
||||
|
||||
### 2. 颜色代码表
|
||||
|
||||
**文本样式**
|
||||
|
||||
| 代码 | 样式 |
|
||||
| ---- | -------------------------- |
|
||||
| `0` | 重置所有样式 |
|
||||
| `1` | 粗体 |
|
||||
| `4` | 下划线 |
|
||||
| `7` | 反显(反转前景色和背景色) |
|
||||
|
||||
**前景色(文字颜色)**
|
||||
|
||||
| 代码 | 颜色 | 代码(亮色) |
|
||||
| ---- | ------ | ------------ |
|
||||
| `30` | 黑色 | `90` |
|
||||
| `31` | 红色 | `91` |
|
||||
| `32` | 绿色 | `92` |
|
||||
| `33` | 黄色 | `93` |
|
||||
| `34` | 蓝色 | `94` |
|
||||
| `35` | 品红色 | `95` |
|
||||
| `36` | 青色 | `96` |
|
||||
| `37` | 白色 | `97` |
|
||||
|
||||
**背景色**
|
||||
|
||||
| 代码 | 颜色 | 代码(亮色) |
|
||||
| ---- | ------ | ------------ |
|
||||
| `40` | 黑色 | `100` |
|
||||
| `41` | 红色 | `101` |
|
||||
| `42` | 绿色 | `102` |
|
||||
| `43` | 黄色 | `103` |
|
||||
| `44` | 蓝色 | `104` |
|
||||
| `45` | 品红色 | `105` |
|
||||
| `46` | 青色 | `106` |
|
||||
| `47` | 白色 | `107` |
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
# 红色文字
|
||||
echo -e "\033[31m这是红色文字\033[0m"
|
||||
|
||||
# 绿色背景 + 黄色文字
|
||||
echo -e "\033[42;33m绿色背景上的黄色文字\033[0m"
|
||||
|
||||
# 粗体 + 蓝色文字
|
||||
echo -e "\033[1;34m粗体蓝色文字\033[0m"
|
||||
|
||||
# 下划线 + 亮红色文字
|
||||
echo -e "\033[4;91m下划线亮红色文字\033[0m"
|
||||
```
|
||||
|
289
shell-脚本.md
Normal file
289
shell-脚本.md
Normal file
@ -0,0 +1,289 @@
|
||||
<h2><center>Shell 脚本</center></h2>
|
||||
|
||||
------
|
||||
|
||||
## 一:脚本规范
|
||||
|
||||
### 1. 风格规范
|
||||
|
||||
开头有“蛇棒”
|
||||
所谓shebang其实就是在很多脚本的第一行出现的以”#!”开头的注释,他指明了当我们没有指定解释器的时候默认的解释器,一般可能是下面这样:
|
||||
|
||||
```shell
|
||||
#!bin/sh
|
||||
```
|
||||
|
||||
除了 bash 之外,可以用下面的命令查看本机支持的解释器:
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# cat /etc/shells
|
||||
/bin/sh
|
||||
/bin/bash
|
||||
/usr/bin/sh
|
||||
/usr/bin/bash
|
||||
```
|
||||
|
||||
- 直接使用`./a.sh`来执行这个脚本的时候,如果没有`shebang`,就会默认用`$Shell`指定的解释器,否则就会用`shebang`指定的解释器。
|
||||
- 上面这种写法可能不太具备适应性,一般我们会用下面的方式来指定:
|
||||
|
||||
```shell
|
||||
#!/usr/bin/env bash
|
||||
```
|
||||
|
||||
### 2. 注释
|
||||
|
||||
注释的意义不仅在于解释用途,而在于告诉我们注意事项,就像是一个 README。
|
||||
|
||||
具体的来说,对于Shell脚本,注释一般包括下面几个部分:
|
||||
|
||||
- shebang
|
||||
- 脚本的参数
|
||||
- 脚本的用途
|
||||
- 脚本的注意事项
|
||||
- 脚本的写作时间,作者,版权等
|
||||
- 各个函数前的说明注释
|
||||
- 一些较复杂的单行命令注释
|
||||
|
||||
### 3. 参数规范
|
||||
|
||||
这一点很重要,当脚本需要接受参数的时候,一定要先判断参数是否合乎规范,并给出合适的回显,方便使用者了解参数的使用。
|
||||
|
||||
至少得判断下参数的个数
|
||||
|
||||
```shell
|
||||
if [[ $# != 2 ]];then
|
||||
echo "Parameter incorrect."
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### 4. 变量
|
||||
|
||||
一般情况下会将一些重要的环境变量定义在开头,确保这些变量的存在。
|
||||
|
||||
```shell
|
||||
source /etc/profile
|
||||
export PATH=”/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/apps/bin/”
|
||||
```
|
||||
|
||||
- 这种定义方式有一个很常见的用途,最典型的应用就是,当本地安装了很多`java`版本时,可能需要指定一个`java`来用。这时就会在脚本开头重新定义`JAVA_HOME`以及`PATH`变量来进行控制。
|
||||
- 一段好的代码通常是不会有很多硬编码在代码里的“魔数”的。如果一定要有,通常是用一个变量的形式定义在开头,然后调用的时候直接调用这个变量,这样方便日后的修改。
|
||||
|
||||
### 5. 缩进
|
||||
|
||||
- 正确的缩进非常重要,尤其是在写函数时,否则在阅读时很容易把函数体跟直接执行的命令搞混。
|
||||
- 常见的缩进方法主要有`soft tab`和`hard tab`两种:
|
||||
1. 所谓`soft tab`就是使用n个空格进行缩进(n通常是2或4)
|
||||
2. 所谓`hard tab`当然就是指真实的`\t`字符
|
||||
|
||||
- 对于`if`和`for`语句之类的,最好不要把`then`,`do`这些关键字单独写一行,这样看上去比较丑。
|
||||
|
||||
### 6. 命名标准
|
||||
|
||||
所谓命名规范,基本包含下面这几点:
|
||||
|
||||
- 文件名规范,以`.sh`结尾,方便识别
|
||||
- 变量名字要有含义,不要拼错
|
||||
- 统一命名风格,写`Shell`一般用小写字母加下划线
|
||||
|
||||
### 7. 编码统一
|
||||
|
||||
在写脚本的时候尽量使用`UTF-8`编码,能够支持中文等一些奇奇怪怪的字符。不过虽然能写中文,但是在写注释以及打log的时候还是尽量英文,毕竟很多机器还是没有直接支持中文的,打出来可能会有乱码。
|
||||
|
||||
### 8. 日志和回显
|
||||
|
||||
- 日志的重要性不必多说,能够方便回头纠错,在大型的项目里是非常重要的。
|
||||
- 如果这个脚本是供用户直接在命令行使用的,那么最好还要能够在执行时实时回显执行过程,方便用户掌控。
|
||||
- 为了提高用户体验,会在回显中添加一些特效,比如颜色啊,闪烁啊之类的。
|
||||
|
||||
### 9. 密码移除
|
||||
|
||||
不要把密码硬编写在脚本里,尤其是当脚本托管在类似 Github 这类平台中时。
|
||||
|
||||
### 10. 分行
|
||||
|
||||
在调用某些程序的时候,参数可能会很长,这时候为了保证较好的阅读体验,我们可以用反斜杠(续行符)来分行:
|
||||
|
||||
```shell
|
||||
./configure \
|
||||
–prefix=/usr \
|
||||
–sbin-path=/usr/sbin/nginx \
|
||||
–conf-path=/etc/nginx/nginx.conf
|
||||
```
|
||||
|
||||
### 11. 代码有效率
|
||||
|
||||
在使用命令的时候要了解命令的具体做法,尤其当数据处理量大的时候,要时刻考虑该命令是否会影响效率。
|
||||
|
||||
比如下面的两个sed命令:
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# sed -n '1p' file
|
||||
[root@wxin ~]# sed -n '1p;1q' file
|
||||
```
|
||||
|
||||
作用一样,都是获取文件的第一行。但是第一条命令会读取整个文件,而第二条命令只读取第一行。当文件很大的时候,仅仅是这样一条命令不一样就会造成巨大的效率差异。
|
||||
|
||||
当然,这里只是为了举一个例子,这个例子真正正确的用法应该是使用`head -n1 file`命令
|
||||
|
||||
**勤用双引号**
|
||||
|
||||
- 几乎所有的大佬都推荐在使用”$”来获取变量的时候最好加上双引号。
|
||||
- 不加上双引号在很多情况下都会造成很大的麻烦。
|
||||
|
||||
```shell
|
||||
#!/bin/sh
|
||||
#已知当前文件夹有一个a.sh的文件
|
||||
var="*.sh"
|
||||
echo $var
|
||||
echo "$var"
|
||||
```
|
||||
|
||||
运行结果如下:
|
||||
|
||||
```shell
|
||||
a.sh
|
||||
*.sh
|
||||
```
|
||||
|
||||
可以解释为它执行了下面的命令
|
||||
|
||||
```shell
|
||||
echo *.sh
|
||||
echo "*.sh"
|
||||
```
|
||||
|
||||
在很多情况下,在将变量作为参数的时候,一定要注意上面这一点,仔细体会其中的差异。上面只是一个非常小的例子,实际应用的时候由于这个细节导致的问题实在是太多了。
|
||||
|
||||
### 12. 学会查路径
|
||||
|
||||
- 很多情况下,会先获取当前脚本的路径,然后以这个路径为基准,去找其他的路径。通常我们是直接用`pwd`以期获得脚本的路径。 不过其实这样是不严谨的,`pwd`获得的是当前`Shell`的执行路径,而不是当前脚本的执行路径。
|
||||
|
||||
正确的做法应该是下面这两种:
|
||||
|
||||
```shell
|
||||
script_dir=$(cd $(dirname $0) && pwd)
|
||||
script_dir=$(dirname $(readlink -f $0 ))
|
||||
```
|
||||
|
||||
应当先`cd`进当前脚本的目录然后再`pwd`,或者直接读取当前脚本的所在路径。
|
||||
|
||||
### 13. 代码要简短
|
||||
|
||||
这里的简短不单单是指代码长度,而是只用到的命令数。原则上我们应当做到,能一条命令解决的问题绝不用两条命令解决。这不仅牵涉到代码的可读性,而且也关乎代码的执行效率。
|
||||
|
||||
最经典的例子如下:
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# cat /etc/passwd | grep root
|
||||
[root@wxin ~]# grep root /etc/passwd
|
||||
```
|
||||
|
||||
cat 命令最为人不齿的用法就是这样,用的没有任何意义,明明一条命令可以解决,非得加根管道
|
||||
|
||||
### 14. 使用新写法
|
||||
|
||||
- 尽量使用[[ ]]来代替[ ]
|
||||
- 尽量使用\$()将命令的结果赋给变量,而不是反引号。
|
||||
- 在复杂的场景下尽量使用printf代替echo进行回显
|
||||
|
||||
### 15. 其他小技巧
|
||||
|
||||
- 路径尽量保持绝对路径,不容易出错,如果非要用相对路径,最好用./修饰
|
||||
- 优先使用bash的变量替换代替awk sed,这样更加简短
|
||||
- 简单的if尽量使用&& ||,写成单行。比如[[ x > 2]] && echo x
|
||||
- 当export变量时,尽量加上子脚本的namespace,保证变量不冲突
|
||||
- 会使用trap捕获信号,并在接受到终止信号时执行一些收尾工作
|
||||
- 使用mktemp生成临时文件或文件夹
|
||||
- 利用/dev/null过滤不友好的输出信息
|
||||
- 会利用命令的返回值判断命令的执行情况
|
||||
- 使用文件前要判断文件是否存在,否则做好异常处理
|
||||
- 不要处理ls后的数据(比如ls -l | awk '{ print $8 }'),
|
||||
- ls的结果非常不确定,并且平台有关
|
||||
- 读取文件时不要使用for loop而要使用while read
|
||||
|
||||
## 二:脚本调试
|
||||
|
||||
Shell脚本的语法调试,使用bash的相关参数进行调试
|
||||
|
||||
```shell
|
||||
sh [参数] 文件名.sh
|
||||
```
|
||||
|
||||
- -n 不要执行script,仅查询语法的问题
|
||||
- -v 在执行script之前,先将script的内容输出到屏幕上
|
||||
- **-x** 将使用的脚本的内容输出到屏幕,该参数经常被使用
|
||||
|
||||
```bash
|
||||
#-v的示例:
|
||||
[root@wxin ~]# sh -v demo.sh
|
||||
module () { eval `/usr/bin/modulecmd bash $*`
|
||||
}
|
||||
#!/bin/bash
|
||||
case $1 in
|
||||
"one")
|
||||
echo "you input number is one"
|
||||
;;
|
||||
"two")
|
||||
echo "you input number is twp"
|
||||
;;
|
||||
*)
|
||||
echo "you input number is other"
|
||||
;;
|
||||
esac
|
||||
you input number is other
|
||||
```
|
||||
|
||||
```bash
|
||||
#-x的示例:
|
||||
[root@wxin ~]# sh -x demo.sh
|
||||
+ case $1 in
|
||||
+ echo 'you input number is other'
|
||||
you input number is other
|
||||
|
||||
[root@wxin ~]# sh -vx demo.sh
|
||||
```
|
||||
|
||||
## 三:脚本运行方式
|
||||
|
||||
Linux中Shell脚本的执行通常有4种方式,分别为工作目录执行,绝对路径执行,sh执行,Shell环境执行。
|
||||
|
||||
### 1. 工作目录执行
|
||||
|
||||
工作目录执行,指的是执行脚本时,先进入到脚本所在的目录(此时,称为工作目录),然后使用 ./脚本方式执行
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# ./test.sh
|
||||
Hello Shell
|
||||
```
|
||||
|
||||
### 2. 绝对路径执行
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# /home/tan/scripts/test.sh
|
||||
Hello Shell
|
||||
```
|
||||
|
||||
### 3. bash 执行
|
||||
|
||||
```bash
|
||||
#在子shell中执行,不需要脚本的可执行权限
|
||||
[root@wxin ~]# sh test.sh
|
||||
Hello Shell
|
||||
[root@wxin ~]# bash test.sh
|
||||
Hello Shell
|
||||
```
|
||||
|
||||
### 4. 环境执行
|
||||
|
||||
Shell环境执行,指的是在当前的Shell环境中执行,可以使用 . 接脚本 或 source 接脚本。
|
||||
|
||||
不需要脚本的可执行权限,在当前shell中执行
|
||||
|
||||
```bash
|
||||
[root@wxin ~]# . test.sh
|
||||
Hello Shell
|
||||
[root@wxin ~]# source test.sh
|
||||
Hello Shell
|
||||
```
|
||||
|
Loading…
x
Reference in New Issue
Block a user