一、 Shell
1. Shell 简介
1. Shell 是一个用 C 语言编写的程序,它既是一种命令语言,又是一种程序设计语言,它是用户使用 Linux 的桥梁2. Shell 脚本(Shell Script),是一种为 Shell 编写的脚本程序3. 用户开发的 Shell 脚本可以驻留在命令搜索路径的目录之下(通常是 /bin、/usr/bin 等),像普通命令一样使用。如果打算反复使用编好的 Shell 脚本,可以开发出自己的新命令4. Linux 的 Shell 种类众多,常见的有: 1)Bourne Shell(/usr/bin/sh 或 /bin/sh) 2)Bourne Again Shell(/bin/bash) 3)Shell for Root(/sbin/sh) ....5. 本文关注的是 Bash,也就是 Bourne Again Shell 1)由于易用和免费,Bash 在日常工作中被广泛使用 2)Bash 也是大多数 Linux 系统默认的 Shell 3)一般情况下,人们并不区分 Bourne Shell 和 Bourne Again Shell,所以 #!/bin/sh 也可以写为 #!/bin/bash,#! 是一个约定的标记,其路径告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell6. Shell 脚本的编写 1)使用 vi/vim 命令来创建文件,新建一个文件 test.sh,扩展名为 sh( sh 代表 Shell,扩展名并不影响脚本执行,见名知意就好 ) 2)第一行一般为:#!/bin/bash 3)如: #!/bin/bash echo "Hello World !"7. Shell 脚本有两种执行方法: 1)作为可执行程序,如: # chmod a+x ./test.sh # ./test.sh 注意:这个要求在 Shell 脚本的开头必须指明执行该脚本的具体 Shell,即 #!/bin/bash 2)作为解释器参数,这种运行方式是直接运行解释器,其参数就是 Shell 脚本的文件名,如: # /bin/bash test.sh # /bin/php test.php 注意:这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没意义8. Shell 注释,在脚本文件中以 "#" 开头的行就是注释,会被解释器忽略。sh 里没有多行注释,只能每一行开头加一个 # 号 1)每一行开头加个 # 符号太费力了,可以把这一段要注释的代码,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果
2. echo、read、printf 命令
1. echo 命令,自带换行功能 1)显示普通字符串,如: echo "It is a test" # 双引号可以省略 2)显示转义字符,如: echo "\"It is a test\"" # 结果:"It is a test" 3)显示不换行,如: echo -e "OK!\c" # -e 开启转义,\c 不换行 echo "It it a test" # 结果:OK! It is a test 4)显示命令执行结果,如: echo `date` # 结果将显示当前日期 5)显示变量值,如: echo ${your_name} 6)显示结果重定向至文件,如: echo "It is a test" > myfile # > 表示覆盖重定向, >> 表示追加重定向2. read 命令 1)read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 Shell 变量,如: #!/bin/bash read -p "请输入:" str echo "${str} It is a test" # 结果 请输入:hello hello It is a test3. printf 命令 1)默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n 2)printf 命令模仿 C 程序库(library)里的 printf() 程序,因此使用 printf 的脚本比使用 echo 移植性好 3)printf 命令的语法:printf 格式控制 参数列表 ,如: printf "%-6s %-6s %-4s\n" 姓名 性别 体重kg printf "%-6s %-7s %-4.2f\n" 小明 男 66.1234 # 结果 姓名 性别 体重kg 小明 男 66.12 a. %s %c %d %f 都是格式替代符,格式只指定了一个参数,如果没有 arguments,那么 %s 用 NULL 代替,%d 用 0 代替 b. %-6s 指一个宽度为 6 个字符( - 表示左对齐,没有则表示右对齐),任何字符都会被显示在6个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来 c. %-4.2f 指格式化为小数,其中 .2 指保留 2 位小数
3. Shell 变量
1. 定义变量,变量名前面不加美元符号,如: your_name="tom" your_company="alibaba" 注意:变量名和等号之间不能有空格2. 使用一个定义过的变量,只要在变量名前面加美元符号即可,如: echo $your_name echo ${your_company}hello 注意:花括号是可选的,为了帮助解释器识别变量的边界。如果不给 your_company 变量加花括号解释器就会把 your_companyhello 当成一个变量。推荐给所有变量加上花括号,这是个好的编程习惯3. 可以将一个命令的执行结果赋值给变量。有两种形式: 1)`commands` ,"`" 是反单引号,如: dirPath=`pwd` 2)$(commands),如: dirPath=$(pwd)4. 设置一个变量为只读属性 1)有些重要的 Shell 变量,赋值后不应该修改,那么可设置它为 readonly ,如: oracle_home=/usr/local/oracle/bin readonly oracle_home5. Shell 字符串 1)字符串是 Shell 编程中最常用最有用的数据类型,字符串可以用单引号,也可以用双引号,也可以不用引号,如: str='this is a string' str="this is a string" str=this is a string 2)单引号字符串的限制 a. 单引号里的任何字符都会原样输出,即单引号字符串中的变量是无效的 b. 单引号字串中不能出现单引号,对单引号使用转义符后也不行 3)双引号的优点 a. 双引号里可以有变量 b. 双引号里可以出现转义字符 4)获取字符串长度:${#str},如: string="abcd" echo ${#string} #输出 4 5)提取子串,如: string="alibaba is a great company" echo ${string:1:4} #输出 liba 6)查询子串位置,以下脚本中 "`" 是反单引号,而不是单引号 "'" string="alibaba is a great company" echo `expr index "$string" is`6. Shell 数组 1)bash 支持一维数组(不支持多维数组),并且没有限定数组的大小,数组元素的下标从 0 开始编号 2)定义数组,用括号来表示数组,数组元素用 "空格" 符号分割,如: array_name=(value0 value1 value2 value3) # 还可以单独定义数组的各个分量: array_name[0]=value0 array_name[i]=valuei # 也可以用 declare 命令显式声明一个数组,一般形式是:declare -a 数组名 3)读取数组元素值,如: valuen=${array_name[n]} 4)使用 * 或 @ 符号可以获取数组中的所有元素,如: echo ${array_name[*]} echo ${array_name[@]} 5)获取数组长度的方法与获取字符串长度的方法相同,如: # 取得数组元素的个数 length=${#array_name[@]} # 或者 length=${#array_name[*]} # 取得数组单个元素的长度 lengthi=${#array_name[i]}
4. Shell 传递参数
1. 在执行 Shell 脚本时,向脚本传递参数,多个参数用空格分割,脚本内获取参数的格式为:$n,n 是一个数字。其中 $0 为执行的文件名,$1 为执行脚本的第一个参数,$2 为执行脚本的第二个参数,.... 如:test.sh #!/bin/bash echo "执行的文件名:$0"; echo "第一个参数为:$1"; echo "第二个参数为:$2"; # 测试结果 # chmod a+x ./test.sh # ./test.sh hello world 执行的文件名:test.sh 第一个参数为:hello 第二个参数为:world2. 另外,还有几个特殊字符用来处理参数 1)$# 传递到脚本的参数个数 2)$* 以一个字符串形式显示所有向脚本传递的参数。一般在使用时加引号,即 "$*" ,表示以 "$1 $2 ... $n" 的形式输出所有参数,输出结果是一个字符串 3)$@ 以多个字符串形式显示所有向脚本传递的参数。一般在使用时加引号,即 "$@" ,表示以 "$1" "$2" ... "$n" 的形式输出所有参数,输出结果是多个字符串 4)$$ 脚本运行的当前进程 ID 号 5)$! 后台运行的最后一个进程的 ID 号 6)$? 显示最后命令的退出状态。0 表示没有错误,其他任何值表明有错误
5. Shell 函数定义
1. Linux Shell 允许用户定义函数,然后可以在 Shell 脚本中随便调用2. Shell 中函数的定义格式如下: [ function ] funname [()]{ action; [return int;] } 1)可以带 function fun() 定义,也可以直接 fun() 定义,不带任何参数 2)return 函数返回值,如果不加 return,将以最后一条命令的运行结果作为返回值。return 后跟数值 3)函数返回值在调用该函数后通过 $? 来获得3. 在 Shell 中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,同 Shell 传递参数,如: #!/bin/bash funWithParam(){ echo "第一个参数为 ${1}" echo "第十个参数为 ${10}" echo "参数总数有 $# 个" echo "作为一个字符串输出所有参数 $*" } funWithParam 1 2 3 4 5 6 7 8 9 10 11 # 结果 第一个参数为 1 第十个参数为 10 参数总数有 11 个 作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 10 11
6. Shell 运算符
1. 注意: 1)表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2 2)完整的运算表达式要被反单引号 ` ` 包含,如:`expr $a + $b` 3)条件表达式要放在方括号之间,并且要有空格,例如: [$a==$b] 是错误的,必须写成 [ $a == $b ] 4)条件测试命令 test ,语法:test 表达式 ,效果等同于方括号 []。如:test $a == $b 等价于 [ $a == $b ]2. 算术运算符 假定变量 a 为 10,变量 b 为 20 1)+ 加法,如:`expr $a + $b` 2)- 减法,如:`expr $a - $b` 3)* 乘法,如:`expr $a \* $b`,在 MAC 中 Shell 的 expr 语法是:$((表达式)),此处表达式中的 "*" 不需要转义符号 "\" 4)/ 除法,如:`expr $b / $a` 5)% 取余,如:`expr $b % $a` 6)= 赋值,如:a=$b 7)== 相等,如:[ $a == $b ] 返回 false 8)!= 不相等,如:[ $a != $b ] 返回 true3. 关系运算符,关系运算符只支持数字,不支持字符串,除非字符串的值是数字 假定变量 a 为 10,变量 b 为 20 1)-eq 相等,如:[ $a -eq $b ] 返回 false 2)-ne 不相等,如:[ $a -ne $b ] 返回 true 3)-gt 大于,如:[ $a -gt $b ] 返回 false 4)-lt 小于,如:[ $a -lt $b ] 返回 true 5)-ge 大于等于,如:[ $a -ge $b ] 返回 false 6)-le 小于等于,如:[ $a -le $b ] 返回 true4. 布尔运算符 假定变量 a 为 10,变量 b 为 20 1)-a 与运算,如:[ $a -lt 20 -a $b -gt 100 ] 返回 false 2)-o 或运算,如:[ $a -lt 20 -o $b -gt 100 ] 返回 true 3) ! 非运算,如:[ ! false ] 返回 true5. 逻辑运算符 假定变量 a 为 10,变量 b 为 20 1)&& 逻辑的 AND ,如:[[ $a -lt 100 && $b -gt 100 ]] 返回 false 2)|| 逻辑的 OR ,如:[[ $a -lt 100 || $b -gt 100 ]] 返回 true6. 字符串运算符 假定变量 a 为 "abc",变量 b 为 "efg" 1) = 相等,如:[ $a = $b ] 返回 false 2)!= 不相等,如:[ $a != $b ] 返回 true 3)-z 检测字符串长度是否为 0,如:[ -z $a ] 返回 false 4)-n 检测字符串长度是否不为 0,如:[ -n $a ] 返回 true7. 文件测试运算符用于检测 Linux 文件的各种属性 假定 file="/usr/local/temp/test.sh",它是大小为 100 字节,具有 rwx 权限的文件 1)-d 检测文件是否是目录,如:[ -d $file ] 返回 false 2)-f 检测文件是否是普通文件,如:[ -f $file ] 返回 true 3)-p 检测文件是否是有名管道,如:[ -p $file ] 返回 false 4)-r 检测文件是否可读,如:[ -r $file ] 返回 true 5)-w 检测文件是否可写,如:[ -w $file ] 返回 true 6)-x 检测文件是否可执行,如:[ -x $file ] 返回 true 7)-s 检测文件大小是否大于 0,如:[ -s $file ] 返回 true 8)-e 检测文件或目录是否存在,如:[ -e $file ] 返回 true
7. Shell 流程控制
1. if 语法格式: if condition1 then command1 elif condition2 then command2 else commandN fi 2. for 语法格式: for var in items do commands done 3. while 语法格式: while condition do commands done 4. until 语法格式: until condition do commands done 5. 无限循环语法格式: 1)while : do command done 2)while true do command done 3)for (( ; ; )) 5. case 语法格式:每个 case 分支用右圆括号,用两个分号表示 break #!/bin/bash while : do echo -n "输入 1 到 5 之间的数字:" read num case $num in 1|2|3|4|5) echo "你输入的数字为 $num!" ;; *) echo "你输入的数字不是 1 到 5 之间的! 游戏结束" break ;; esac done 6. continue 命令与 break 命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环
8. Shell 输入/输出重定向
1. 一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件: 1)标准输入文件(stdin ):文件描述符为 0,Unix 程序默认从 stdin 读取数据 2)标准输出文件(stdout):文件描述符为 1,Unix 程序默认向 stdout 输出数据 3)标准错误文件(stderr):文件描述符为 2,Unix 程序会向 stderr 流中写入错误信息 默认情况下,command > file 将 stdout 重定向到 file;command < file 将 stdin 重定向到 file;如果希望 stderr 重定向到 file,可以这样写:command 2 > file2. 重定向命令 1)将输出以覆盖的方式重定向到 file command > file 2)将输出以追加的方式重定向到 file command >> file 3)将输入重定向到 file command < file 4)将文件描述符为 n 的文件重定向到 file n > file 5)将文件描述符为 n 的文件以追加的方式重定向到 file n >> file 6)将输出文件 m 和 n 合并 n >& m 7)将输入文件 m 和 n 合并 n <& m 8)将开始标记 tag 和结束标记 tag 之间的内容(document) 作为输入传递给 command command << tag document tag 注意:结尾的 tag 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进 9)command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。即执行 command,从文件 file1 读取内容,然后将输出写入到 file2 中 command < file1 > file2 10)将 stdout 和 stderr 合并后重定向到 file command > file 2>&1 11)/dev/null 文件是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果 a. 如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null : command > /dev/null b. 如果希望屏蔽 stdout 和 stderr : command > /dev/null 2>&1
9. Shell 文件包含
1. Shell 文件包含的两种语法格式: 1). filename 注意点号(.)和文件名中间有一空格 2)source filename2. 实例 1)创建两个 Shell 脚本文件 定义 test1.sh 如下: #!/bin/bash url="http://www.baidu.com" 定义 test2.sh 如下: #!/bin/bash . ./test1.sh # 使用 . 号来引用 test1.sh 文件或者使用 source ./test1.sh 包含文件代码 echo "百度地址:$url" 2)测试结果 # chmod +x test2.sh # ./ test2.sh 百度网址:http://www.baidu.com 3)注意:被包含的文件 test1.sh 不需要可执行权限
10. 参考资料
https://www.w3cschool.cn/linux/linux-shell.html https://www.w3cschool.cn/shellbook/