diff --git a/README.md b/README.md index 22d3469..6159228 100644 --- a/README.md +++ b/README.md @@ -7,45 +7,44 @@ Shell Tutorial ## 目录 - [脚本运行](#脚本运行) -- [基础](#基础) - - [特殊字符](#特殊字符) - - [注释](#注释) - - [命令分割符](#命令分割符) - - [结束符](#结束符) - - [句号/圆点](#句号圆点) - - [引号](#引号) - - [命令替换](#命令替换) - - [操作符](#操作符) - - [赋值](#赋值) - - [计算操作符](#计算操作符) - - [位操作符](#位操作符) - - [逗号操作符](#逗号操作符) - - [逻辑操作符](#逻辑操作符) - - [变量](#变量) - - [变量值](#变量值) - - [定义变量](#定义变量) - - [只读变量](#只读变量) - - [使用变量](#使用变量) - - [删除变量unset](#删除变量unset) - - [变量类型](#变量类型) - - [内部变量](#内部变量) - - [位置参数](#位置参数) - - [参数替换](#参数替换) - - [declare/typeset](#declaretypeset) - - [变量间接引用](#变量间接引用) - - [$RANDOM](#$RANDOM) - - [双括号结构](#双括号结构) - - [转义字符](#转义字符) - - [测试](#测试) - - [测试结构](#测试结构) - - [文件测试操作符](#文件测试操作符) - - [比较操作符](#比较操作符) - - [操作字符串](#操作字符串) - - [循环和分支](#循环和分支) - - [Loops](#Loops) - - [嵌套循环](#嵌套循环) - - [循环控制](#循环控制) - - [case/select](#case/select) +- [特殊字符](#特殊字符) + - [注释](#注释) + - [命令分割符](#命令分割符) + - [结束符](#结束符) + - [句号/圆点](#句号圆点) + - [引号](#引号) + - [命令替换](#命令替换) +- [操作符](#操作符) + - [赋值](#赋值) + - [计算操作符](#计算操作符) + - [位操作符](#位操作符) + - [逗号操作符](#逗号操作符) + - [逻辑操作符](#逻辑操作符) +- [变量](#变量) + - [变量值](#变量值) + - [定义变量](#定义变量) + - [只读变量](#只读变量) + - [使用变量](#使用变量) + - [删除变量unset](#删除变量unset) + - [变量类型](#变量类型) + - [内部变量](#内部变量) + - [位置参数](#位置参数) + - [参数替换](#参数替换) + - [declare/typeset](#declaretypeset) + - [变量间接引用](#变量间接引用) + - [$RANDOM](#$RANDOM) + - [双括号结构](#双括号结构) +- [转义字符](#转义字符) +- [测试](#测试) + - [测试结构](#测试结构) + - [文件测试操作符](#文件测试操作符) + - [比较操作符](#比较操作符) +- [操作字符串](#操作字符串) +- [循环和分支](#循环和分支) + - [Loops](#Loops) + - [嵌套循环](#嵌套循环) + - [循环控制](#循环控制) +- [case/select](#caseselect) @@ -101,11 +100,9 @@ bash demo1 脚本以"#!"行开头,行将会命令解释器(sh或是bash)。`#!/bin/rm` 当你运行这个脚本时,除了这个脚本消失了之外,你不会发现更多其他的东西。 -## 基础 +## 特殊字符 -### 特殊字符 - -#### 注释 +### 注释 以`#`开头的行就是注释,会被解释器忽略。注释行前面也可以有空白字符。 @@ -132,7 +129,7 @@ echo ${PATH#*:} # 前面的#是参数代换,不是注释. echo $(( 2#101011 )) # 基本转换,不是注释. ``` -#### 命令分割符 +### 命令分割符 分号`;`命令分割符,分割符允许在同一行里有两个或更多的命令。执行[demo2](./example/demo2),会将 demo2 拷贝输出 demo2.bak 。 @@ -147,7 +144,7 @@ else fi; echo "File test complete." ``` -#### 结束符 +### 结束符 双分号`;;`,case语句分支的结束符。[demo3](./example/demo3) @@ -163,7 +160,7 @@ esac # 允许字符串的范围出现在[]中, exit 0 ``` -#### 句号/圆点 +### 句号/圆点 作为一个文件名的组成部分`.`,当点`.`以一个文件名为前缀时,起作用使该文件变成了隐藏文件。这种隐藏文件ls一般是不会显示出来的。 @@ -171,7 +168,7 @@ exit 0 点(.)字符匹配。作为正则表达式的一部分,匹配字符时,单点(.)表示匹配任意一个字符。 -#### 引号 +### 引号 引号一个很重要的作用是保护命令行上的一个参数不被shell解释,而把此参数传递给要执行的程序来处理它。 @@ -216,7 +213,7 @@ str="Hello, I know your are \"$your_name\"! \n" 双引号里可以有变量 双引号里可以出现转义字符 -#### 命令替换 +### 命令替换 命令替换"`",将会重新分配一个命令甚至是多个命令的输出;它会将命令的输出如实地添加到另一个上下文中。[demo4](./example/demo4) @@ -238,9 +235,9 @@ rm `cat filename` # "filename" 包含了需要被删除的文件列表 # ( -- 同时覆盖了那些以"-"开头的文件所产生的特殊情况 ) ``` -### 操作符 +## 操作符 -#### 赋值 +### 赋值 变量赋值,初始化或改变一个变量的值,通用的变量赋值操作符,可以用于数值和字符串的赋值 @@ -262,14 +259,12 @@ then fi ``` -#### 计算操作符 - -| 操作符 | 描述 | 操作符 | 描述 | -| ---- | ---- | ---- | ---- | -| `+` | 加 | `/` | 除 | -| `-` | 减 | `**` | 求幂 | -| `*` | 乘 | `%` | 求模[demo6](./example/demo6) | +### 计算操作符 +| 操作符 | 描述 | 操作符 | 描述 | 操作符 | 描述 | +| ---- | ---- | ---- | ---- |---- | ---- | +| `+` | 加 | `/` | 除 | `**` | 求幂 | +| `-` | 减 | `*` | 乘 | `%` | 求模[demo6](./example/demo6) | ```shell # Bash在版本2.02引入了"**"求幂操作符. @@ -301,24 +296,19 @@ let "var *= 4" # 使变量var的值乘上4并把值赋给var. - [Bash不能处理浮点计算,它会把含有小数点的数当成字符串。](./example/demo10) -#### 位操作符 +### 位操作符 位操作符很少在脚本中使用。他们主要用于操作和测试从端口或sockets中读到的数据。“位运算”更多地用于编译型的语言,比如说C和C++,它们运行起来快地像飞。 -| 操作符 | 描述 | -| ---- | ---- | -| `<<` | 位左移(每移一位相当乘以2) | -| `<<=` | 位左移赋值 | -| `>>` | 位右移(每移一位相当除以2) | -| `>>=` | "位右移赋值"(和<<=相反) | -| `&` | 位与 | -| `&=` | 位于赋值 | -| `|` | 位或 | -| `|=` | 位或赋值 | -| `~` | 位反 | -| `!` | 位非 | -| `^` | 位或 | -| `^=` | 位或赋值 | +| 操作符 | 描述 | 操作符 | 描述 | +| ---- | ---- | ---- | ---- | +| `<<` | 位左移(每移一位相当乘以2) | `|` | 位或 | +| `<<=` | 位左移赋值 | `|=` | 位或赋值 | +| `>>` | 位右移(每移一位相当除以2) | `~` | 位反 | +| `>>=` | "位右移赋值"(和<<=相反) | `!` | 位非 | +| `&` | 位与 | `^` | 位或 | +| `&=` | 位于赋值 | `^=` | 位或赋值 | + ```shell # <<= @@ -326,7 +316,7 @@ let "var *= 4" # 使变量var的值乘上4并把值赋给var. let "var <<= 2" 结果使var的二进制值左移了二位(相当于乘以4) ``` -#### 逻辑操作符 +### 逻辑操作符 逻辑与`&&` @@ -360,7 +350,7 @@ bash$ echo $(( 1 && 2 )) $((3 && 0)) $((4 || 0)) $((0 || 0)) 1 0 1 0 ``` -#### 逗号操作符 +### 逗号操作符 逗号`,`操作符连接两个或更多的算术操作。所有的操作都被求值(可能会有副作用),但只返回最后一个操作的结构。[demo5](./example/demo5) @@ -372,11 +362,11 @@ let "t2 = ((a = 9, 15 / 3))" # 初始化"a"并求"t2"的值. echo "t2 = $t2 a = $a" # t2 = 5 a = 9 ``` -### 变量 +## 变量 变量,是脚本编程中的如何进行数据表现的办法。它们可以在算术计算中作为操作数,在一个字符串表达式中作为符号表达抽象的意义或是其他的其它意义。变量是表示计算机内存中保存一种数据需要占的一个位置或一组的位置的标识。 -#### 变量值 +### 变量值 如果variable1是一个变量的名字,那么$variable1就是引用这个变量的值――即这个变量它包含的数据。[变量赋值与替换例子](./example/demo12) @@ -398,7 +388,7 @@ echo "$uninitialized" # 5 # 这个无法证实(也可能是不可移植)的行为。 ``` -#### 定义变量 +### 定义变量 ```shell # 变量名不加美元符号 @@ -407,14 +397,14 @@ your_var="elaine" your_var="newname" ``` -**⚠️注意** +**注意⚠️** 1. 首个字符必须为字母(a-z,A-Z)。 1. 中间不能有空格,可以使用下划线(_),等号左右也不能有空格。 1. 不能使用标点符号。 1. 不能使用bash里的关键字(可用help命令查看保留关键字)。 -#### 只读变量 +### 只读变量 ```shell #!/bin/bash @@ -426,7 +416,7 @@ github="https://www.github.com" /bin/sh: NAME: This variable is read only. ``` -#### 使用变量 +### 使用变量 ```shell your_var="github" @@ -435,7 +425,7 @@ echo ${your_var} echo "your name is ${your_var}-l" ``` -#### 删除变量unset +### 删除变量unset 变量被删除后不能再次使用。unset 命令不能删除只读变量。 @@ -445,7 +435,7 @@ unset myUrl echo $myUrl ``` -#### 变量类型 +### 变量类型 不同与许多其他的编程语言,Bash不以"类型"来区分变量。本质上来说,Bash变量是字符串,但是根据环境的不同,Bash允许变量有整数计算和比较。其中的决定因素是变量的值是不是只含有数字。 @@ -453,9 +443,10 @@ echo $myUrl 2. **环境变量** 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。 3. **shell变量** shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行 -#### 内部变量 +### 内部变量 | 内部变量 | 说明 | +| ---- | ---- | | $BASH | Bash二进制程序文件的路径 | | $BASH_ENV | 该环境变量保存一个Bash启动文件路径,当启动一个脚本程序时会去读该环境变量指定的文件。 | | $BASH_SUBSHELL | 一个指示子shell(subshell)等级的变量。它是Bash版本3新加入的。 | @@ -493,7 +484,7 @@ echo $myUrl | $TMOUT | 如果$TMOUT环境变量被设为非零值时间值time,那么经过time这么长的时间后,shell提示符会超时.这将使此shell退出登录 | | $UID | 用户ID号,这是当前用户的用户标识号,它在/etc/passwd文件中记录。| -#### 位置参数 +### 位置参数 | 参数处理 | 说明 | | ---- | ---- | @@ -505,7 +496,7 @@ echo $myUrl | `$-` | 显示Shell使用的当前选项,与set命令功能相同。 | | `$?` | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 | -#### 参数替换 +### 参数替换 **${parameter}**:和$parameter是相同的,都是表示变量parameter的值,可以把变量和字符串连接。 @@ -528,7 +519,7 @@ echo ${username-`whoami`} **${parameter=default}, ${parameter:=default}**:如果变量parameter没有设置,把它设置成默认值。除了引起的当变量被声明且值是空值时有些不同外,两种形式几乎相等。 ```shell -echo "###### \${parameter+alt_value} ########" +echo "===== \${parameter+alt_value} =====" echo a=${param1+xyz}; echo "a = $a" # a = @@ -542,7 +533,7 @@ a=${param3+xyz} echo "a = $a" # a = xyz echo -echo "###### \${parameter:+alt_value} ########" +echo "====== \${parameter:+alt_value} ======" a=${param4:+xyz} echo "a = $a" # a = @@ -617,7 +608,7 @@ echo "${filename##*.}" # data # 文件的扩展名. ``` -#### declare/typeset +### declare/typeset declare或typeset内建命令(它们是完全相同的)可以用来限定变量的属性。这是在某些编程语言中使用的定义类型不严格的方式。 @@ -637,7 +628,7 @@ declare或typeset内建命令(它们是完全相同的)可以用来限定变量 | `-x export` | 函这样将声明一个变量作为脚本的环境变量而被导出。 | `declare -x var3` | | `-x var=$value` | declare命令允许在声明变量类型的时候同时给变量赋值。| `declare -x var3=373` | -#### 变量间接引用 +### 变量间接引用 假设一个变量的值是第二个变量的名字。这样要如何才能从第一个变量处重新获得第二个变量的值?例如,`a=letter_of_alphabet`和`letter_of_alphabet=z`,是否能由a引用得到z ? 这确实可以办到,这种技术被称为间接引用。 @@ -653,7 +644,7 @@ echo "Now a = $a" # 现在 a = z exit 0 ``` -#### $RANDOM +### $RANDOM $RANDOM是Bash的一个返回伪随机整数(范围为0 - 32767)的内部函数(而不是一个常量或变量),它不应该用于产生加密的密钥。[demo25](./example/demo25) @@ -679,7 +670,7 @@ echo "Throw of the dice = $throw" exit 0 ``` -#### 双括号结构 +### 双括号结构 用`((...))`结构来使用C风格操作符来处理变量。[demo26](./example/demo26) @@ -701,7 +692,7 @@ echo "a (after ++a) = $a" echo "a (after --a) = $a" ``` -### 转义字符 +## 转义字符 在单个字符前面的转义符`\`告诉shell不必特殊解释这个字符,只把它当成字面上的意思。但在一些命令和软件包里,比如说echo和sed,转义一个字符可能会引起一个相反的效果--因为它们可能触发那个字符的特殊意思。[demo13](./example/demo13) @@ -715,7 +706,7 @@ echo "a (after --a) = $a" `\0ddd` 将自负表示成1到3的八进制数值 -### 退出/退出状态 +## 退出/退出状态 `$?` 变量用于测试脚本中的命令执行结果非常的有用。[demo14](./example/demo14) @@ -749,9 +740,9 @@ exit 113 # 返回113状态码给shell。 | 255* | Exit status out of range | `exit -1` | exit takes only integer args in the range 0 - 255 | -### 测试 +## 测试 -#### 测试结构 +### 测试结构 一个if/then结构能包含嵌套的比较和测试。 @@ -773,11 +764,11 @@ elif是else if的缩写。作用是在一个if/then里嵌入一个内部的if/th `(( ))`结构扩展并计算一个算术表达式的值。如果表达式值为0,会返回1或假作为退出状态码。一个非零值的表达式返回一个0或真作为退出状态码。这个结构和先前test命令及`[]`结构的讨论刚好相反。 -#### 文件测试操作符 +### 文件测试操作符 如果下面的条件成立返回真。[demo15](./example/demo15) -| 测试操作符 | 描述 | +| 操作符 | 描述 | | ---- | ---- | | -e | 文件存在 | | -a | 文件存在,这个和-e的作用一样. 它是不赞成使用的,所以它的用处不大。 | @@ -790,11 +781,11 @@ elif是else if的缩写。作用是在一个if/then里嵌入一个内部的if/th | -h | 文件是一个符号链接 | | -L | 文件是一个符号链接 | | -S | 文件是一个socket | -| -t | 文件(描述符)与一个终端设备相关。这个测试选项可以用于检查脚本中是否标准输入 ([ -t 0 ])或标准输出([ -t 1 ])是一个终端。| +| -t | 文件(描述符)与一个终端设备相关。| | -r | 文件是否可读 (指运行这个测试命令的用户的读权限) | | -w | 文件是否可写 (指运行这个测试命令的用户的读权限) | | -x | 文件是否可执行 (指运行这个测试命令的用户的读权限) | -| -g | 文件或目录的设置-组-ID(sgid)标记被设置,如果一个目录的sgid标志被设置,在这个目录下创建的文件都属于拥有此目录的用户组,而不必是创建文件的用户所属的组。这个特性对在一个工作组里的同享目录很有用处。 | +| -g | 文件或目录的设置-组-ID(sgid)标记被设置。 | | -u | 文件的设置-用户-ID(suid)标志被设置 | | -k | 粘住位设置 | | -N | 文件最后一次读后被修改 | @@ -803,7 +794,13 @@ elif是else if的缩写。作用是在一个if/then里嵌入一个内部的if/th | f1 -ef f2 | 文件f1和f2 是相同文件的硬链接 | | ! | "非" -- 反转上面所有测试的结果(如果没有给出条件则返回真)。| -#### 比较操作符 + +**注意⚠️** + +1. `-t` 这个测试选项可以用于检查脚本中是否标准输入 ([ -t 0 ])或标准输出([ -t 1 ])是一个终端。 +1. `-g` 如果一个目录的sgid标志被设置,在这个目录下创建的文件都属于拥有此目录的用户组,而不必是创建文件的用户所属的组。这个特性对在一个工作组里的同享目录很有用处。 + +### 比较操作符 二元比较操作符比较两个变量或是数值。注意整数和字符串比较的分别。 @@ -843,11 +840,11 @@ elif是else if的缩写。作用是在一个if/then里嵌入一个内部的if/th 在一个混合测试中,把一个字符串变量引号引起来可能还不够。如果$string变量是空的话,表达式`[ -n "$string" -o "$a" = "$b" ]`在一些Bash版本中可能会引起错误。安全的办法是附加一个外部的字符串给可能有空字符串变量比较的所有变量,`[ "x$string" != x -o "x$a" = "x$b" ]` (x字符可以互相抵消) -### 操作字符串 +## 操作字符串 Bash已经支持了令人惊讶的字符串操作的数量。不一致的命令语法和冗余的功能,导致真的学起来有困难。 -#### 字符串长度 +### 字符串长度 ```shell String=abcABC123ABCabc @@ -870,7 +867,7 @@ echo `expr match "$String" 'abc[A-Z]*.2'` # 8 echo `expr "$String" : 'abc[A-Z]*.2'` # 8 ``` -#### 索引 +### 索引 `expr index $string $substring` 在字符串$string中$substring第一次出现的数字位置 @@ -883,7 +880,7 @@ echo `expr index "$String" 1c` # 3 # 'c' (in #3 position) matches before '1'. ``` -#### 字串提取 +### 字串提取 `${string:position}` 把$string中从第$postion个字符开始字符串提取出来。如果$string是"*"或"@",则表示从位置参数中提取第$postion后面的字符串。 `${string:position:length}` 把$string中$postion个字符后面的长度为$length的字符串提取出来。[demo18](./example/demo18) @@ -912,7 +909,7 @@ echo ${String: -4} # Cabc # 圆括号或附加的空白字符可以转义$position参数. ``` -#### 字串移动 +### 字串移动 `${string#substring}`从$string左边开始,剥去最短匹配$substring子串。 `${string##substring}`从$string左边开始,剥去最长匹配$substring子串。 @@ -942,7 +939,7 @@ echo ${String%%b*c} # a # 从$String后面尾部开始,剥去匹配'a'到'C'之间最长的字符串. ``` -#### 用awk处理字符串 +### 用awk处理字符串 Bash脚本可以调用awk的字符串操作功能来代替它自己内建的字符串操作符 @@ -967,9 +964,9 @@ exit 0 ``` -### 循环和分支 +## 循环和分支 -#### Loops +### Loops 重复一些命令的代码块,如果条件不满足就退出循环,下面是一个基本的循环结构。[demo27](./example/demo27) @@ -989,7 +986,7 @@ do done ``` -#### 嵌套循环 +### 嵌套循环 嵌套循环就是在一个循环中还有一个循环,内部循环在外部循环体中。[demo28](./example/demo28) @@ -1021,7 +1018,7 @@ exit 0 ``` -#### 循环控制 +### 循环控制 影响循环行为的命令 `break`, `continue`, break命令将会跳出循环,continue命令将会跳过本次循环下边的语句,直接进入下次循环。[demo29](./example/demo29) @@ -1047,24 +1044,24 @@ done ``` -### case/select +## case/select case/select依靠在代码块的顶部或底部的条件判断来决定程序的分支。 -#### case(in)/esac +### case(in)/esac case它允许通过判断来选择代码块中多条路径中的一条。它的作用和多个if/then/else语句相同,是它们的简化结构,特别适用于创建目录。[demo30](./example/demo30) ->>> -case "$variable" in -?"$condition1" ) -?command... -?;; -?"$condition2" ) -?command... -?;; -esac ->>> + +> case "$variable" in +> ?"$condition1" ) +> ?command... +> ?;; +> ?"$condition2" ) +> ?command... +> ?;; +> esac + - 对变量使用`""`并不是强制的,因为不会发生单词分离。 - 每句测试行,都以右小括号`)`结尾。 @@ -1115,7 +1112,7 @@ exit 0 ``` -#### select +### select select结构是建立菜单的另一种工具,这种结构是从ksh中引入的。