您现在的位置是: 网站首页 >Shell Shell
Shell脚本快速入门学习
admin2019年3月12日 17:31 【Linux 】 1665人已围观
个人学习整理 ## 简介 Shell是脚本语言,解释型,无需提前编译。 Shel同时也是一个程序,它的一端连接着Linux内核,另一端连接着用户和其他应用程序,也就是说Shell是用户或应用程序与内核沟通的桥梁, ![BLOG_20190312_173618_67](/media/blog/images/2019/03/BLOG_20190312_173618_67.png "博客图集BLOG_20190312_173618_67.png") 扩展名为`.sh` ```bash #!/bin/bash echo "Hello World !" ``` `#!` 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。 `/bin/bash`指明了解释器的具体路径。 `echo` 命令用于向窗口输出文本。 ## 常用的Shell > Shell 既是一种脚本编程语言,也是一个连接内核和用户的软件。 常见的 Shell 有 sh、bash、csh、tcsh、ash 等。 bash shell 是 Linux 的默认 shell,bash 由 GNU 组织开发,保持了对 sh shell 的兼容性,是各种 Linux 发行版默认配置的 shell。 > bash 兼容 sh 意味着,针对 sh 编写的 Shell 代码可以不加修改地在 bash 中运行。 尽管如此,bash 和 sh 还是有一些不同之处: - 一方面,bash 扩展了一些命令和参数; - 另一方面,bash 并不完全和 sh 兼容,它们有些行为并不一致,但在大多数企业运维的情况下区别不大,特殊场景可以使用 bash 代替 sh。 ## 查看Shell Shell是一个程序,一般放在`/bin`或`/user/bin`目录下,Linux系统可用的Shell都可以使用下面命令查看 ```bash user@PxeCtrlSys:~$ cat /etc/shells # /etc/shells: valid login shells /bin/sh /bin/dash /bin/bash /bin/rbash ``` 现在Linux基本都是用的bash Shell,查看Linux的默认shell ```bash user@PxeCtrlSys:~$ echo $SHELL /bin/bash ``` ## 运行方法 - 作为可执行程序 代码保存`test.sh`文本中 ```bash # 进入脚本所在目录,让脚本拥有可执行权限 chmod +x ./test.sh # 执行方式 ./test.sh ``` 一定要写成`./test.sh`,而不是`test.sh`,运行其它二进制的程序也一样,直接写 `test.sh`,linux 系统会去 PATH 里寻找有没有叫 `test.sh` 的,而只有 `/bin`, `/sbin`, `/usr/bin`,`/usr/sbin` 等在 PATH 里,你的当前目录通常不在 PATH 里,所以写成 `test.sh` 是会找不到命令的,要用 `./test.sh` 告诉系统说,就在**当前目录**找。 - 作为解释器参数 直接运行解释器,其参数就是 shell 脚本的文件名 ```bash /bin/sh test.sh # 或者 /bin/bash test.sh ``` # Shell变量 在Bash Shell中,每一个变量的值都是字符串,无论给变量赋值时有没使用引号,值都会以字符串的形式存储,即使将整数或小数赋值给变量,他们也会被视为字符串。 ## 变量赋值 定义变量,三种定义方式,显式赋值 ```bash name="test" name='test' name=test ``` 注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。同时,变量名的命名须遵循如下规则: - 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。 - 中间不能有空格,可以使用下划线(_)。 - 不能使用标点符号。 - 不能使用bash里的关键字(可用help命令查看保留关键字)。 如果变量值包含空白字符,就必须使用引号包围起来,例如`name="bom smith"` 用语句给变量赋值 ```bash for file in `ls /etc` 或 for file in $(ls /etc) ``` 将`/etc`目录下的文件名循环出来。 ## 使用变量 ```bash name="test" echo $name # 或者加花括号,含义一样,加花括号是为了帮助解释器识别变量的边界 echo ${name} ``` 例如和变量连接的字符 ```bash #!/bin/bash for item in Python Java Shell Bat; do echo "This is ${item}Script" done ``` 运行结果如下 ```python This is PythonScript This is JavaScript This is ShellScript This is BatScript ``` 如果没有花括号,那么变量就是`$itemScript`,其值为空,结果就会错误。推荐加花括号`{}`。 ## 修改变量的值 已定义过的变量可以重新赋值 ```bash #!/bin/bash name='test' echo ${name} # test name="new" echo $name # new ``` 变量赋值不能加`$`符号,只有在使用键盘时才能加`$`。 ## 命令执行结果赋值给变量 支持将命令的执行结果赋值给变量使用,常见的两种方式: ```bash value=`command` # 或 value=$(command) # 默认输出以空白符填充 echo $value # 按照原格式输出,例如有换行符的 echo "$value" ``` 第一种是使用反引号包围起来,第二种是使用`$()`包围起来(推荐使用第二种方式)。`command`是执行命令。 ```bash user@PxeCtrlSys:~$ res=`who` user@PxeCtrlSys:~$ echo $res user pts/0 2019-03-11 09:04 (192.168.96.16) user pts/1 2019-03-11 09:04 (192.168.96.16) user@PxeCtrlSys:~$ res=$(ls /) user@PxeCtrlSys:~$ echo $res bin boot dev etc home initrd.img lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var vmlinuz ``` 执行结果赋值给变量,输出不会自动添加换行符。 如果被替换的命令的输出内容包括多行(也即有换行符),或者含有多个连续的空白符,那么在输出变量时应该将变量用双引号包围,否则系统会使用默认的空白符来填充,这会导致换行无效,以及连续的空白符被压缩成一个。请看下面的代码: ```bash user@PxeCtrlSys:~$ res=`who` # 不用引号包围变量,输出不会自动换行 user@PxeCtrlSys:~$ echo $res user pts/0 2019-03-11 09:04 (192.168.96.16) user pts/1 2019-03-11 09:04 (192.168.96.16) # 使用引号包围变量,输出按照原来的格式 user@PxeCtrlSys:~$ echo "$res" user pts/0 2019-03-11 09:04 (192.168.96.16) user pts/1 2019-03-11 09:04 (192.168.96.16) ``` 为了防止出现格式混乱的情况,建议在输出变量时加上双引号。 ## 只读变量 使用 `readonly` 命令可以将变量定义为只读变量,只读变量的值不能被改变。 下面的例子尝试更改只读变量,结果报错: ```bash #!/bin/bash name='test' readonly name name='new' echo $name ``` 运行结果 ```bash test /tmp/280278210/main.sh: line 4: name: readonly variable # 当改为只读后,再次赋值就会报错 ``` ## 删除变量 使用 `unset` 命令可以删除变量 ```bash #!/bin/bash name='test' unset name echo $name # 执行没有输出 ``` 变量删除后不能再次使用。`unset`命令不能删除只读变量。 ## 变量类型,作用域 运行shell时,会同时存在三种变量: 1. **全局变量** 指变量在当前的整个 Shell 会话中都有效。每个 Shell 会话都有自己的作用域,彼此之间互不影响。在 Shell 中定义的变量,默认就是全局变量。 1. **局部变量** 在 Shell 函数中定义的变量默认也是全局变量,它和在函数外部定义变量拥有一样的效果;要想变量的作用域仅限于函数内部,那么可以在定义时加上`local`命令,此时该变量就成了局部变量。 1. **环境变量** 只在当前 Shell 会话中有效,如果使用export命令将它导出,那么它就在所有的子 Shell 中也有效了。 # Shell字符串 字符串最常用的数据类型,可以用单/双引号,也可以不用引号。 ## 单引号 ```bash str='this is a string' ``` 单引号字符串的限制: - 单引号里面的任何字符都会**原样输出**,单引号字符串里面的变量是无效的,也就是变量那一串就只是字符串; - 单引号字符中不能出现单独一个的单引号(对单引号使用转义符后也不行),但能成对出现,相当于字符串拼接`s='abc\'0e'`,这个就会报错,而`s='abc\'0'e'`会输出`abc\0e` ## 双引号 - 双引号中可以有变量,输出时会先解析里面的变量和命令 - 双引号也可以出现转义字符 ```bash #!/bin/bash name='lr' echo $name s1="my name is $name" echo $s1 s2="my name is "$name"" echo $s2 s3="my name is \"$name\"" echo $s3 # 运行结果 lr my name is lr my name is lr my name is "lr" ``` ## 单/双引号使用场景 建议: - 如果变量的值是数字,那么不加引号`a=1`; - 如果要将字符串原样输出,就用反引号`str='单引号中原样输出${test}'`; - 没有特别要求最好都是用双引号(最常见)。 ## 拼接字符串 字符串的拼接(也称字符串连接或者字符串合并),在 Shell 中你不需要使用任何运算符,将两个字符串并排放在一起就能实现拼接。 ```bash #!/bin/bash name='lr' s1="hello, $name" # 双引号中可以直接使用变量 echo $s1 s2="hello, "$name"" # 相当于两对双引号拼接 echo $s2 s3='hello, $name' # 单引号中变量都当作普通字符串 echo $s3 s4='hello, "$name"' echo $s4 s5='hello, '$name'' # 相当于两对单引号和变量进行拼接 echo $s5 nick='xy' s6=$nick$name # 中间不能有空格 s7="$nick $name" # 如果是双引号包围,就允许中间有空格 s8=$nick"..."$name # 中间可以出现其他字符串 s9="$nick...$name" # 中间也可以这样写 s10="昵称${nick}...${name}名字" echo $s6 echo $s7 echo $s8 echo $s9 echo $s10 # 运行结果 user@PxeCtrlSys:~$ ./test.sh hello, lr hello, lr hello, $name hello, "$name" hello, lr xylr xy lr xy...lr xy...lr 昵称xy...lr名字 ``` ## 获取字符串长度 使用`${#变量名}`获取长度 ```python #!/bin/bash name='lr' echo ${#name} # 运行结果 2 ``` ## 截取字符串 Shell 截取字符串通常有两种方式:从指定位置开始截取和从指定字符(子字符串)开始截取。 ### 从指定位置开始截取 种方式需要两个参数:除了指定起始位置,还需要截取长度,才能最终确定要截取的字符串。 **从字符串左边开始计数**,格式为`${string: start :length}` ```python #!/bin/bash str="I love Python, but I need study Shell" echo ${str:2:7} # 下标以0开始,从第2个开始截取,共截取7个字符 echo ${str:19} # 省略长度,直接从指定位置截取到最后 # 运行结果 love Py I need study Shell ``` **从右边开始计数**,格式为`${string: 0-start :length}` 多了`0-`,这是固定的写法,专门用来表示从字符串右边开始计数。 - 从左边开始计数时,起始数字是 0(这符合程序员思维);从右边开始计数时,起始数字是 1(这符合常人思维)。计数方向不同,起始数字也不同。 - 不管从哪边开始计数,截取方向都是从左到右。 ```bash echo ${str:0-5:2} # 从右向左数第5个,长度为2的字符串 echo ${str:0-5} # 省略长度,直接从指定位置截取到最后 # 运行结果 Sh Shell ``` ### 从指定字符(子字符串)开始截取 这种截取方式无法指定字符串长度,只能从指定字符(子字符串)截取到字符串末尾。Shell 可以截取指定字符(子字符串)右边的所有字符,也可以截取左边的所有字符。 **使用 # 号截取右边字符**,格式为`${string#*chars}` 其中,`string` 表示要截取的字符,`chars` 是指定的字符(或者子字符串),`*`是通配符的一种,表示任意长度的字符串。`*chars`连起来使用的意思是:忽略左边的所有字符,直到遇见 `chars`(`chars` 不会被截取)。 ```bash #!/bin/bash str="I love Python, but I need study Shell" echo ${str#*,} # 以 , 分割,取右边 echo ${str#*Python,} # 以Python , 分割取右边 echo ${str#I love} # 如果不需要忽略左边的字符,那么也可以不写* echo ${str#*o} # 以o分割,因为字符串有好两个o,那么遇到第一个就结束了 echo ${str##*o} # 使用两个#,就可以匹配到最后一个指定字符(子字符串)右方内容 # 运行结果 user@PxeCtrlSys:~$ ./test.sh but I need study Shell but I need study Shell Python, but I need study Shell ve Python, but I need study Shell n, but I need study Shell ``` 如果希望直到最后一个指定字符(子字符串)再匹配结束,那么可以使用##,具体格式为:`${string##*chars}` **使用 % 截取左边字符**,格式为`${string%chars*}` ```bash #!/bin/bash str="I love Python, but I need study Shell" echo ${str%,*} # 以,分割,取左边 echo ${str%Python,*} # 以Python,分割取左边 echo ${str%Shell} # 如果不需要忽略右边的字符,那么也可以不写* echo ${str%o*} # 以o分割,因为字符串有好两个o,从右往左,那么遇到第一个就结束了 echo ${str%%o*} # 使用两个%%,就可以匹配到从右往左的最后一个指定字符(子字符串)左方内容 # 运行结果 user@PxeCtrlSys:~$ ./test.sh I love Python I love I love Python, but I need study I love Pyth I l ``` ### 截取字符串汇总 | 格式 | 说明 | | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | `${string: start :length} ` | 从 `string` 字符串的左边索引为 `start` 的字符开始,向右截取 `length` 个字符。 | | `${string: start} ` | 从 `string` 字符串的左边索引为 `start` 的字符开始截取,直到最后。 | | `${string: 0-start :length} ` | 从 `string` 字符串的右边第 `start` 个字符开始,向右截取 `length` 个字符。 | | `${string: 0-start} ` | 从 `string` 字符串的右边第 `start` 个字符开始截取,直到最后。 | | `${string#*chars}` | 从 string 字符串第一次出现 `*chars` 的位置开始,截取 `*chars` 右边的所有字符。 | | `${string##*chars}` | 从 string 字符串最后一次出现 `*chars` 的位置开始,截取 `*chars` 右边的所有字符。 | | `${string%*chars}` | 从 string 字符串第一次出现 `*chars` 的位置开始,截取 `*chars` 左边的所有字符。 | | `${string%%*chars} ` | 从 `string` 字符串最后一次出现 `*chars` 的位置开始,截取 `*chars` 左边的所有字符。 | ## 查找字符串 ```python #!/bin/bash str="I love Python, but I need study Shell" echo `expr index "$str" SP` # 查找字符S或者P最先出现的位置,下标以1开始,第8个字符为P,就输入了8 # 运行结果 8 ``` 以上脚本中 ` 是反引号,而不是单引号 ' # Shell数组 bash支持一维数组(但不支持多维数组),并且没有限制数组大小。数组元素的下标从0开始,获取数组元素使用小标,下标可以试整数或算术表达式,其值应大于等于0。 ## 定义数组 用括号表示数组,数组元素用`空格`符号分隔开。 ```bash 数组名=(值1 值2 值3 ... 值n) ``` 或者 ```bash array_name=( value0 value1 ... valuen ) ``` 还可以单独定义数组的各个分量 ```bash array_name[0]=value0 array_name[1]=value1 array_name[n]=valuen ``` 可以不使用连续的下标,而且下标的范围没有限制 ```bash array=([2]=12 [5]=99) echo ${array[0]} # 为空值 echo ${array[2]} # 输出值为12 array[0]=5 # 赋值一个 echo ${array[0]} # 输出值为5 # 获取数组的所有元素 echo ${array[*]} 5 12 99 # 获取数组长度 echo ${#array[*]} 3 ``` ## 读取数组 一般格式 ```bash ${数组名[下标]} ``` 例如`valuen=${array_name[n]}`,输出数组所有元素用`echo ${array_name[@]}` ```bash #!/bin/bash arr[0]=1 arr[2]=2 echo ${arr[@]} # 使用 @ 符号可以获取数组中的所有元素: 1 2 echo ${arr[2]} # 2 echo ${arr[1]} # 没有的值获取为空 ``` 使用`@`或`*`可以获取数组中的所有元素。 ## 获取数组的长度 所谓数组长度,就是数组元素的个数。 利用`@`或`*`,可以将数组扩展成列表,然后使用`#`来获取数组元素的个数,格式为 **`${#array_name[@]}`** 或 **`${#array_name[*]}`** ,其中`array_name` 表示数组名。两种形式是等价的。 ```bash #!/bin/bash arr=(1 2 3 "abc") echo ${#arr[@]} # 获取数组的长度:4 echo ${#arr[*]} # *也可以:4 echo ${#arr[3]} # 获取数组中单独元素的长度:3 ``` 获取数组中元素为字符串的长度`${#arr[2]}`(假设下标为2的元素为字符串),因为获取字符串长度的方法是 `${#string_name}` ## 删除数组的元素或数组 使用 **`unset arr[index]`** 删除数组元素,使用 **`unset arr`** 删除整个数组,所有元素都会消失。 ```bash #!/bin/bash arr=(1 2 3 "abc") echo ${#arr[@]} # 获取数组的长度:4 echo 原来数组的长度为:${#arr[*]} # *也可以:4 echo ${#arr[3]} # 获取数组中单独元素的长度:3 unset arr[1] echo "删除第二个元素后长度为:${#arr[*]}" # 运行结果 user@PxeCtrlSys:~$ ./test.sh 4 原来数组的长度为:4 3 删除第二个元素后长度为:3 ``` ## 数组拼接、合并 将两个数组连接成一个数组。 拼接数组的思路是:先利用@或*,将数组扩展成列表,然后再合并到一起。格式为 **`array_new=(${array1[@]} ${array2[@]})`** 或 **`array_new=(${array1[*]} ${array2[*]})`** ```bash #!/bin/bash arr1=(1 2 3 "abc") arr2=(66) echo "数组1:${arr1[*]}" echo "数组2:${arr2[@]}" new_arr=(${arr1[*]} ${arr2[@]}) echo "合并后的数组为:${new_arr[*]}" # 运行结果 user@PxeCtrlSys:~$ ./test.sh 数组1:1 2 3 abc 数组2:66 合并后的数组为:1 2 3 abc 66 ``` # Shell注释 以`#`开始的行就是注释,会被解释器忽略 通过每一行添加一个`#`来设置多行注释 如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢? 每一行加个`#`符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。 ## 多行注释 ```bash :<<EOF 注释内容... 注释内容... 注释内容... EOF ``` `EOF`也可以使用其他符号 ```bash :<<' 注释内容... 注释内容... 注释内容... ' :<<! 注释内容... 注释内容... 注释内容... ! ``` # Shell脚本传递参数 想脚本传递参数,脚本内获取参数的格式为:`$n`。`n`代表一个数字,1为执行脚本的第一个参数,2为执行脚本的第二个参数,以此类推... ## 给脚本文件传递参数 向脚本传递3个参数,其中**`$0`表示执行的文件名** ```bash user@PxeCtrlSys:~$ vim test.sh user@PxeCtrlSys:~$ chmod +x test.sh # 添加执行权限 user@PxeCtrlSys:~$ ./test.sh 执行的文件名:./test.sh 传递的第一个参数: 传递的第二个参数: 传递的第三个参数: 所有参数: user@PxeCtrlSys:~$ ./test.sh 1 2 执行的文件名:./test.sh 传递的第一个参数:1 传递的第二个参数:2 传递的第三个参数: 所有参数:1 2 user@PxeCtrlSys:~$ ./test.sh 1 2 3 执行的文件名:./test.sh 传递的第一个参数:1 传递的第二个参数:2 传递的第三个参数:3 所有参数:1 2 3 ``` ```bash # test.sh文件内容 #!/bin/bash echo "执行的文件名:$0" echo "传递的第一个参数:$1" echo "传递的第二个参数:$2" echo "传递的第三个参数:$3" echo "所有参数:$*" ``` 如果参数个数太多,达到或者超过了 10 个,那么就得用`${n}`的形式来接收了,例如 `${10}`、`${23}`。`{ }`的作用是为了帮助解释器识别参数的边界,这跟使用变量时加`{ }`是一样的效果。 ## 特殊字符处理参数(特殊变量) | 参数处理 | 说明 | | ------- | ------------------------------------------------------------- | | `$#` | 获取传递到脚本的参数个数 | | `$*` | 以一个单字符串显示所有向脚本传递的参数,如`$*`就和`"$1 $2 $3 ... $n"`输出形式一样 | | `$$` | 脚本运行的当前ID号 | | `$!` | 后台运行的最后一个进程的ID号 | | `$@` | 与`$*`相同,但是使用时加括号,斌仔引号中返回每个参数,`"$1" "$2" … "$n"`形式 | | `$-` | 显示Shell使用的当前选项,与set命令功能相同 | | `$?` | 显示最后命令的退出状态。0表示没有错误,其他任何值表示有错误 | ## $* 和 $@ 的区别 - 相同点:都是引用所有参数 - 不同点:只有在双引号中提现出来。假设在脚本运行时写了三个参数1、2、3,则`*`等价于`"1 2 3"`(传递了一个参数:会将所有的参数从整体上看做一份数据,而不是把每个参数都看做一份数据。),而`@`等价于`"1" "2" "3"`(仍然将每个参数都看作一份数据,彼此之间是独立的) ```bash #!/bin/bash echo "执行的文件名:$0" echo "传递的第一个参数:$1" echo "传递的第二个参数:$2" echo "传递的第三个参数:$3" echo "\$*演示" for i in "$*";do echo $i done echo "\$@演示" for j in $@;do echo $j done # 运行结果 user@PxeCtrlSys:~$ ./test.sh 1 2 3 执行的文件名:./test.sh 传递的第一个参数:1 传递的第二个参数:2 传递的第三个参数:3 $*演示 # 只循环了一次,因为$*当作一个参数 1 2 3 $@演示 # 循环多次,$@当作多个参数 1 2 3 ``` # Shell函数 shell函数格式 ```bash [ function ] funname [()] { action; [return int;] } ``` - 可以带`function func()`定义,也可以直接`func()`定义,不带任何参数。 - 参数返回,可以显示加:`return`返回,如果不加,将以最后一条命令运行结果,作为返回值。`return`后跟数值n(0-255) ## 无返回值函数 ```bash #!/bin/bash func(){ echo "这是一个函数中的内容" } echo "开始调用函数" func echo "调用函数完成" # 运行结果 user@PxeCtrlSys:~$ ./test.sh 开始调用函数 这是一个函数中的内容 调用函数完成 ``` ## 有返回值函数 定义一个带`return`的函数 ```bash #!/bin/bash func(){ echo "请输入两个数,执行加法" echo -n "请输入第一个数:" read num1 echo -n "请输入第二个数:" read num2 # return $[ $num1 + $num2] # 下方表达式也正确 return $(( $num1 + $num2 )) } echo "开始调用函数" func echo "函数返回值为 $?" echo "调用函数完成" # 运行结果 user@PxeCtrlSys:~$ ./test.sh 开始调用函数 请输入两个数,执行加法 请输入第一个数:2 请输入第二个数:3 函数返回值为 5 调用函数完成 ``` 函数返回值在调用该函数后通过 `$?` 来获得。 > 所有函数在使用前必须定义。这就是说必须将函数放在脚本开始部分,直到shell解释器首次发现它时才可以使用。调用函数仅使用其函数名即可。 ## $? 使用方法:返回值或退出状态 `$?`用来获取函数返回值或者上一个命令的退出状态 每一条 Shell 命令,不管是 Bash 内置命令(例如 `test`、`echo`),还是外部的 Linux 命令(例如 `cd`、`ls`),还是自定义的 Shell 函数,当它退出(运行结束)时,都会返回一个比较小的整数值给调用(使用)它的程序,这就是命令的退出状态(exit status)。 `if` 语句的判断条件,从本质上讲,判断的就是命令的退出状态。 ### $? 获取上一次命令退出状态 退出状态,就是上一个命令执行后的返回结果。退出状态是一个数字,一般情况下,大部分命令执行成功会返回 0,失败返回 1,这和C语言的 main() 函数是类似的。 不过,也有一些命令返回其他值,表示不同类型的错误。 ```bash #!/bin/bash echo -n 请输入a: read a echo -n 请输入b: read b (( $a == $b )); echo "退出状态:$?" # 运行结果 user@PxeCtrlSys:~$ ./test.sh 请输入a:1 请输入b:2 退出状态:1 user@PxeCtrlSys:~$ echo $? 0 user@PxeCtrlSys:~$ ./test.sh 请输入a:6 请输入b:6 退出状态:0 user@PxeCtrlSys:~$ echo $? 0 ``` ### $? 获取函数的返回值 ## 给函数传递参数 Shell调用函数也可以向其传递参数。在函数体内部,通过 `$n` 的形式来传递参数。例如`$1`表示第一个参数。`$2`表示第二次参数 ```bash #!/bin/bash func(){ echo "第一个参数:$1" echo "第二个参数:$2" echo "第二个参数:${2}" echo "第五个参数:$5" echo "第10个参数:$10" # 这个相当于第一个参数$1连接一个0 echo "第10个参数:${10}" echo "第11个参数:$11" # 相当于第一个参数$1连接一个1 echo "第11个参数:${11}" echo "参数总数有 $# 个" echo "作为一个地府传输出所有参数:$*" } echo "开始调用函数" func 0 2 3 4 5 23 36 65 99 123 echo "函数返回值为 $?" echo "调用函数完成" # 运行结果 user@PxeCtrlSys:~$ ./test.sh 开始调用函数 第一个参数:0 第二个参数:2 第二个参数:2 第五个参数:5 第10个参数:00 第10个参数:123 第11个参数:01 第11个参数: 参数总数有 10 个 作为一个地府传输出所有参数:0 2 3 4 5 23 36 65 99 123 函数返回值为 0 调用函数完成 ``` > `$10`不能获取到第10个参数,正确用法是`${10}`。**当`n>=10`时,需要使用`${n}`来获取参数**。 处理参数的特殊字符 | 参数处理 | 说明 | | ------- | ------------------------------------------------------ | | $# | 传递到脚本的参数个数 | | $* | 以一个单字符串显示所有向脚本传递的参数 | | $$ | 脚本运行的当前进程ID号 | | $! | 后台运行的最后一个进程的ID号 | | $@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 | | $- | 显示Shell使用的当前选项,与set命令功能相同。 | | $? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 | # Shell基本运算符 支持的运算符 - 算术运算符 - 关系运算符 - 布尔运算符 - 字符串运算符 - 文件测试运算符 原生bash不支持简单的数学运算,但可以使用其他命令实现,例如`awk`、`expr`(最常用) `expr`是一款表达式计算工具,使用它完成表达式的求值操作 例如,两个数量价,注意是反引号,而不是单引号 ```bash #!/bin/bash val=`expr 2+2` echo "求值结果:$val" val=`expr 2 + 2` echo "求值结果:$val" # 运行结果 user@PxeCtrlSys:~$ ./test.sh 求值结果:2+2 求值结果:4 ``` - 表达式和运算符之间要用空格,例如`2+2`是不正确的,需要写成`2 + 2`,这与大多数编程语言不同 - 完整的表达式要被两个反引号\` \`包含. ## 算术运算符 ### 两个数直接加不会进行算术运算 Shell 和其它编程语言不同,Shell 不能直接进行算数运算,必须使用数学计算命令。 默认情况下,Shell 不会直接进行算术运算,而是把`+`两边的数据(数值或者变量)当做字符串,把`+`当做字符串连接符,最终的结果是把两个字符串拼接在一起形成一个新的字符串。这是因为,在Shell如果不特别知名,每一个变量都是字符串,无论赋值的时候有没使用引号,值都会以字符串形式存储,默认情况下不区分变量类型。 ### Shell expr:进行整数计算 ```bash a 不等于 b #!/bin/bash a=2 b=3 echo `expr $a + $b` echo `expr $a - $b` echo `expr $a \* $b` # 做乘法需要添加斜杠转义 echo `expr $b / $a` echo `expr $b % $a` c=$b echo "赋值后c的值:$c" if [ $a == $b ] then echo "a 等于 b" else echo "a 不等于 b" fi if [ $b == $c ] then echo "b 等于 c" fi if [ $a != $b ] then echo "a 不等于 b" fi if [ $c != $b ] then echo "c 不等于 b" fi # 运行结果 user@PxeCtrlSys:~$ ./test.sh 5 -1 6 1 1 赋值后c的值:3 a 不等于 b b 等于 c a 不等于 b ``` | 运算符 | 说明 (例如a=2、b=3) | 举例 | | ----- | ----------------------------------------- | -------------------- | | + | 加法 | \`expr $a + $b\` 结果为 30。 | | - | 减法 | \`expr $a - $b\` 结果为 -10。 | | * | 乘法 | \`expr $a \\* $b\` 结果为 200。 | | / | 除法 | \`expr $b / $a\` 结果为 2。 | | % | 取余 | \`expr $b % $a\` 结果为 0。 | | = | 赋值 | a=$b 将把变量 b 的值赋给 a。 | | == | 相等。用于比较两个数字,相同则返回 true。 | \[ $a == $b \] 返回 false。 | | != | 不相等。用于比较两个数字,不相同则返回 true。 | \[ $a != $b \] 返回 true。 | - 条件表达式要放在方括号之间,并且要有空格,例如: `[$a==$b]` 是错误的,必须写成 **`[ $a == $b ]`**。 - 乘号(`*`)前边必须加反斜杠(`\*`)才能实现乘法运算; - `if...then...fi` 是条件语句 - 在 MAC 中 shell 的 `expr` 语法是:`$((表达式))`,此处表达式中的 "*" 不需要转义符号 "\" ## 数学计算命令 Shell 中常用的六种数学计算方式 | 运算操作符/运算命令 | 说明 | | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `(())` | 用于整数运算,效率很高,**推荐使用**。 | | `let` | 用于整数运算,和 `(())` 类似。 | | [`$[]` | 用于整数运算,不如 `(())` 灵活。 | | `expr` | 可用于整数运算,也可以处理字符串。比较麻烦,需要注意各种细节,不推荐使用。 | | `bc` | Linux下的一个计算器程序,可以处理整数和小数。Shell 本身只支持整数运算,想计算小数就得使用 `bc` 这个外部的计算器。 | | `declare -i` | 将变量定义为整数,然后再进行数学运算时就不会被当做字符串了。功能有限,仅支持最基本的数学运算(加减乘除和取余),不支持逻辑运算、自增自减等,所以在实际开发中很少使用。 | ### Shell (()):对整数进行数学运算 **`(( ))` 只能进行整数运算**,不能对小数(浮点数)或者字符串进行运算。语法格式 **`((表达式))`** ,就是将数学运算表达式放在`((`和`))`之间。 表达式可以只有一个,也可以有多个,多个表达式之间以逗号`,`分隔。对于多个表达式的情况,以最后一个表达式的值作为整个 `(( ))` 命令的执行结果。 可以使用`$`获取 `(( ))` 命令的结果,这和使用`$`获得变量值是类似的。 | 运算操作符/运算命令 | 说明 | | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `((a=10+66) ((b=a-15)) ((c=a+b))` | 这种写法可以在计算完成后给变量赋值。以 `((b=a-15))` 为例,即将 `a-15` 的运算结果赋值给变量 `c`。 注意,使用变量时不用加`$`前缀,**`(( ))` 会自动解析变量名**。 | | `a=$((10+66) b=$((a-15)) c=$((a+b))` | 可以在 `(( ))` 前面加上`$`符号获取 `(( ))` 命令的执行结果,也即获取整个表达式的值。以 `c=$((a+b))` 为例,即将 `a+b` 这个表达式的运算结果赋值给变量 `c`。 注意,类似 `c=((a+b))` 这样的写法是错误的,不加`$`就不能取得表达式的结果。 | | `((a>7 && b==c))` | `(( ))` 也可以进行逻辑运算,在 `if` 语句中常会使用逻辑运算。 | | `echo $((a+10))` | 需要立即输出表达式的运算结果时,可以在 `(( ))` 前面加`$`符号。 | | `((a=3+5, b=a+10))` | 对多个表达式同时进行计算。 | 在 `(( ))` 中使用变量无需加上`$`前缀,`(( ))` 会自动解析变量名,这使得代码更加简洁,也符合程序员的书写习惯。 #### 算术运算 ```bash # 直接输出运算结果 user@PxeCtrlSys:~$ echo $((1+1)) 2 user@PxeCtrlSys:~$ echo $((2*3)) 6 # 计算完成后,给变量赋值 user@PxeCtrlSys:~$ i=5 user@PxeCtrlSys:~$ ((i=i*2)) user@PxeCtrlSys:~$ echo $i 10 user@PxeCtrlSys:~$ ((i*=2)) # 简写,等效于 ((i=i*2)) user@PxeCtrlSys:~$ echo $i 20 # 复杂运算,结果赋值给变量a,变量在括号内 user@PxeCtrlSys:~$ ((a=2-5*2/4+2**2)) user@PxeCtrlSys:~$ echo $a 4 # 运算结果赋值给变量b,变量b在括号外,需要使用$ user@PxeCtrlSys:~$ b=$((2-5*2/4+2**2)) user@PxeCtrlSys:~$ echo $b 4 # 直接输出表达式的值,$符号不能去掉 user@PxeCtrlSys:~$ echo $((2-5*2/4+2**2)) 4 # 利用公式计算1-100的和 user@PxeCtrlSys:~$ echo $((100*(100+1)/2)) 5050 ``` #### 逻辑运算 ```bash # 结果为真,输出1,1表示真 user@PxeCtrlSys:~$ echo $((3<5)) 1 user@PxeCtrlSys:~$ echo $((3>5)) 0 user@PxeCtrlSys:~$ echo $((3==2+1)) 1 # 多条件成立 user@PxeCtrlSys:~$ if ((8>6&&5==2+3)) > then > echo yes > fi yes ``` #### (())进行自增++和自减--运算 ```bash user@PxeCtrlSys:~$ a=10 # ++在后面,先输出a的值,在自增 user@PxeCtrlSys:~$ echo $((a++)) 10 user@PxeCtrlSys:~$ echo $a 11 # --在后面,先输出a的值,再自减 user@PxeCtrlSys:~$ echo $((a--)) 11 user@PxeCtrlSys:~$ echo $a 10 # --在前面,先自减,再输出a的值 user@PxeCtrlSys:~$ echo $((--a)) 9 user@PxeCtrlSys:~$ echo $a 9 # ++在前面,先自增,再输出a的值 user@PxeCtrlSys:~$ echo $((++a)) 10 user@PxeCtrlSys:~$ echo $a 10 ``` #### 多个表达式计算 ```bash # 先计算第一个表达式,再计算第二个表达式 user@PxeCtrlSys:~$ ((a=3*2, b=a+6)) user@PxeCtrlSys:~$ echo $a $b 6 12 # 以最后一个表达式的结果作为整个(())命令的执行结果 user@PxeCtrlSys:~$ c=$((1+2, a+b)) user@PxeCtrlSys:~$ echo $c 18 ``` ### Shell let:对整数进行数学运算 和双小括号 `(( ))` 一样,`let` 命令也只能进行整数运算,不能对小数(浮点数)或者字符串进行运算。 语法格式 **`let 表达式`**、**`let "表达式"`**、**`let '表达式'`**,都等价于 `((表达式))`。 当表达式中含有 Shell 特殊字符(例如 `|`)时,需要用双引号`" "`或者单引号`' '`将表达式包围起来。 和 `(( ))` 类似,`let` 命令也支持一次性计算多个表达式,并且以最后一个表达式的值作为整个 `let` 命令的执行结果。但是,对于多个表达式之间的分隔符,`let` 和 `(( ))` 是有区别的: - `let` 命令以空格来分隔多个表达式; - `(( ))` 以逗号`,`来分隔多个表达式。 ```bash user@PxeCtrlSys:~$ i=2 user@PxeCtrlSys:~$ let i+=3 user@PxeCtrlSys:~$ echo $i 5 ``` `let i+=3`等价于`((i+=3))`,但后者效率更高。 `let`后面可以跟多个表达式,用空格分隔 ```bash user@PxeCtrlSys:~$ a=3 user@PxeCtrlSys:~$ let b=3**2 c=a+b user@PxeCtrlSys:~$ echo $a $b 3 9 user@PxeCtrlSys:~$ echo $c 12 ``` ### Shell $[]:对整数进行数学运算 和 `(())`、`let` 命令类似,`$[]` 也只能进行整数运算。语法为 **`$[表达式]`** `$[]` 会对`表达式`进行计算,并取得计算结果。如果`表达式`中包含了变量,那么你可以加`$`,也可以不加。 ```bash # 直接输出结果 user@PxeCtrlSys:~$ echo $[2*3] 6 user@PxeCtrlSys:~$ echo $[(2+3)/3] 1 user@PxeCtrlSys:~$ echo $[(2+3)%3] 2 user@PxeCtrlSys:~$ a=6 # 将结果赋值给变量 user@PxeCtrlSys:~$ b=$[a*2] user@PxeCtrlSys:~$ echo $b 12 user@PxeCtrlSys:~$ echo $[a+b] 18 # 变量前加$对结果没有影响 user@PxeCtrlSys:~$ echo $[$a+$b] 18 ``` ### Shell declare -i:将变量声明为整数 默认情况下,Shell每一个变量的值都是一个字符串,即使给变量赋值一个数字,它也是字符串。 使用 `declare` 命令的`-i`选项可以将一个变量声明为整数类型,这样在进行数学计算时就不会作为字符串处理了。 ```bash #!/bin/bash echo 定义之前,直接求两个数的和 m=2 n=3 echo $m+$n echo 求和后赋值给一个变量 ret=$m+$n echo $ret echo -e "\n声明变量为整数" declare -i m n ret m=2 n=3 echo 直接输出声明后的求和 echo $m+$n ret=$m+$n echo 求和后赋值变量 echo $ret # 运行结果 user@PxeCtrlSys:~$ ./test.sh 定义之前,直接求两个数的和 2+3 求和后赋值给一个变量 2+3 声明变量为整数 直接输出声明后的求和 2+3 求和后赋值变量 5 ``` 除了将 `m`、`n` 定义为整数,还必须将 `ret` 定义为整数,如果不这样做,在执行`ret=$m+$n`时,Shell 依然会将 `m`、`n` 视为字符串。 此外,也不能写类似`echo $m+$n`这样的语句,这种情况下 `m`、`n` 也会被视为字符串。 总之,除了将参与运算的变量定义为整数,还得将承载结果的变量定义为整数,而且只能用整数类型的变量来承载运算结果,不能直接使用 echo 输出。 和 `(())`、`let`、`$[]` 不同,`declare -i`的功能非常有限,仅支持最基本的数学运算(加减乘除和取余),不支持逻辑运算(比较运算、与运算、或运算、非运算),所以在实际开发中很少使用。 ## 关系运算符 关系运算符**只支持数字**,不支持字符串,除非字符串的值是数字 | 运算符 | 说明(例如a=2、b=3) | 举例 | | ----- | --------------------------------------------------------- | ---- | | -eq | **等于**检测两个数是否相等,相等返回 true。 | \[ $a -eq $b \] 返回 false。 | | -ne | **不等于**检测两个数是否不相等,不相等返回 true。 | \[ $a -ne $b \] 返回 true。 | | -gt | **大于**检测左边的数是否大于右边的,如果是,则返回 true。 | \[ $a -gt $b \] 返回 false。 | | -lt | **小于**检测左边的数是否小于右边的,如果是,则返回 true。 | \[ $a -lt $b \] 返回 true。 | | -ge | **大等于**检测左边的数是否大于等于右边的,如果是,则返回 true。 | \[ $a -ge $b \] 返回 false。 | | -le | **小等于**检测左边的数是否小于等于右边的,如果是,则返回 true。 | \[ $a -le $b \] 返回 true。 | ```bash #!/bin/bash a=2 b=3 if [ $a -eq $b ] then echo "a 大于 b" else echo "a 小于 b" fi # 运行结果 user@PxeCtrlSys:~$ ./test.sh a 小于 b ``` ## 布尔运算符 | 运算符 | 说明(例如a=2、b=3) | 举例 | | ----- | ----------------------------------------------- | ------------- | | ! | 非运算,表达式为 true 则返回 false,否则返回 true。 | \[ ! false \] 返回 true。 | | -o | 或运算,有一个表达式为 true 则返回 true。 | \[ $a -lt 20 -o $b -gt 100 \] 返回 true。 | | -a | 与运算,两个表达式都为 true 才返回 true。 | \[ $a -lt 20 -a $b -gt 100 \] 返回 false。 | ```bash 2 小于 5 或 3 大于 100 : 返回 true #!/bin/bash a=2 b=3 if [ $a != $b ] then echo "$a != $b : a 不等于 b" else echo "$a != $b: a 等于 b" fi if [ $a -lt 100 -a $b -gt 15 ] then echo "$a 小于 100 且 $b 大于 15 : 返回 true" else echo "$a 小于 100 且 $b 大于 15 : 返回 false" fi if [ $a -lt 100 -o $b -gt 100 ] then echo "$a 小于 100 或 $b 大于 100 : 返回 true" else echo "$a 小于 100 或 $b 大于 100 : 返回 false" fi if [ $a -lt 5 -o $b -gt 100 ] then echo "$a 小于 5 或 $b 大于 100 : 返回 true" else echo "$a 小于 5 或 $b 大于 100 : 返回 false" fi # 运行结果 user@PxeCtrlSys:~$ ./test.sh 2 != 3 : a 不等于 b 2 小于 100 且 3 大于 15 : 返回 false 2 小于 100 或 3 大于 100 : 返回 true 2 小于 5 或 3 大于 100 : 返回 true ``` ## 逻辑运算符 | 运算符 | 说明 | 举例 | | ----- | --------- | ------------------------------------------- | | `&&` | 逻辑的 AND | `[[ $a -lt 100 && $b -gt 100 ]]` 返回 false | | `||` | 逻辑的OR | `[[ $a -lt 100 || $b -gt 100 ]]` 返回 true | ```bash #!/bin/bash a=2 b=3 if [[ $a -lt 5 && $b -gt 2 ]] then echo "返回true" else echo "返回false" fi if [[ $a -ge 2 || $b -le 3 ]] then echo "返回true" else echo "返回false" fi # 运行结果 user@PxeCtrlSys:~$ ./test.sh 返回true 返回true ``` ## 字符串运算符 | 运算符 | 说明 | 举例 | | ----- | ------------------------------------- | ----------------------- | | = | 检测两个字符串是否相等,相等返回 true。 | \[ $a = $b \] 返回 false。 | | != | 检测两个字符串是否相等,不相等返回 true。 | \[ $a != $b \] 返回 true。 | | -z | 检测字符串长度是否为0,为0返回 true。 | \[ -z $a \] 返回 false。 | | -n | 检测字符串长度是否为0,不为0返回 true。 | \[ -n "$a" \] 返回 true。 | | $ | 检测字符串是否为空,不为空返回 true。 | \[ $a \] 返回 true。 | ```bash #!/bin/bash a="abc" b="efg" if [ $a = $b ] then echo "$a = $b : a 等于 b" else echo "$a = $b: a 不等于 b" fi if [ $a != $b ] then echo "$a != $b : a 不等于 b" else echo "$a != $b: a 等于 b" fi if [ -z $a ] then echo "-z $a : 字符串长度为 0" else echo "-z $a : 字符串长度不为 0" fi if [ -n "$a" ] then echo "-n $a : 字符串长度不为 0" else echo "-n $a : 字符串长度为 0" fi if [ $a ] then echo "$a : 字符串不为空" else echo "$a : 字符串为空" fi # 运行结果 user@PxeCtrlSys:~$ ./test.sh abc = efg: a 不等于 b abc != efg : a 不等于 b -z abc : 字符串长度不为 0 -n abc : 字符串长度不为 0 abc : 字符串不为空 ``` ## 文件测试运算符 文件测试运算符用于检测Unix文件的各种属性 | 操作符 | 说明 | 举例 | | --- | --- | --- | | -b file | 检测文件是否是块设备文件,如果是,则返回 true。 | \[ -b $file \] 返回 false。 | | -c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | \[ -c $file \] 返回 false。 | | -d file | 检测文件是否是目录,如果是,则返回 true。 | \[ -d $file \] 返回 false。 | | -f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | \[ -f $file \] 返回 true。 | | -g file | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | \[ -g $file \] 返回 false。 | | -k file | 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 | \[ -k $file \] 返回 false。 | | -p file | 检测文件是否是有名管道,如果是,则返回 true。 | \[ -p $file \] 返回 false。 | | -u file | 检测文件是否设置了 SUID 位,如果是,则返回 true。 | \[ -u $file \] 返回 false。 | | -r file | 检测文件是否可读,如果是,则返回 true。 | \[ -r $file \] 返回 true。 | | -w file | 检测文件是否可写,如果是,则返回 true。 | \[ -w $file \] 返回 true。 | | -x file | 检测文件是否可执行,如果是,则返回 true。 | \[ -x $file \] 返回 true。 | | -s file | 检测文件是否为空(文件大小是否大于0),不为空返回 true。 | \[ -s $file \] 返回 true。 | | -e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | \[ -e $file \] 返回 true。 | 变量 file 表示文件"/home/user/test.sh",它的大小为100字节,具有 rwx 权限。下面的代码,将检测该文件的各种属性: ```python #!/bin/bash file="/home/user/test.sh" if [ -r $file ] then echo "文件可读" else echo "文件不可读" fi if [ -w $file ] then echo "文件可写" else echo "文件不可写" fi if [ -x $file ] then echo "文件可执行" else echo "文件不可执行" fi if [ -f $file ] then echo "文件为普通文件" else echo "文件为特殊文件" fi if [ -d $file ] then echo "文件是个目录" else echo "文件不是个目录" fi if [ -s $file ] then echo "文件不为空" else echo "文件为空" fi if [ -e $file ] then echo "文件存在" else echo "文件不存在" fi # 运行结果 user@PxeCtrlSys:~$ ./test.sh 文件可读 文件可写 文件可执行 文件为普通文件 文件不是个目录 文件不为空 文件存在 ``` # Shell内建(内置)命令 由 Bash 自身提供的命令,而不是文件系统中的某个可执行文件。 用于进入或者切换目录的 cd 命令,虽然我们一直在使用它,但如果不加以注意很难意识到它与普通命令的性质是不一样的:该命令并不是某个外部文件,只要在 Shell 中你就一定可以运行这个命令。 可以使用 `type` 来确定一个命令是否是内建命令: ```bash user@PxeCtrlSys:~$ type cd cd is a shell builtin # 内建命令 user@PxeCtrlSys:~$ type ls ls is aliased to 'ls --color=auto' user@PxeCtrlSys:~$ type ssh ssh is /usr/bin/ssh # 外部命令 ``` 通常来说,内建命令会比外部命令执行得更快,执行外部命令时不但会触发磁盘 I/O,还需要 fork 出一个单独的进程来执行,执行完成后再退出。而执行内建命令相当于调用当前 Shell 进程的一个函数。 | <br> | <br> | <br> | <br> | <br> | <br> | <br> | | ------- | ------- | ------ | ------- | -------- | -------- | -------- | | bash | : | . | \[ | alias | bg | bind | | break | builtin | cd | command | compgen | complete | continue | | declare | dirs | disown | echo | enable | eval | exec | | exit | export | fc | fg | getopts | hash | help | | history | jobs | kill | let | local | logout | popd | | printf | pushd | pwd | read | readonly | return | set | | shift | shopt | source | suspend | test | times | trap | | type | typeset | ulimit | umask | unalias | unset | wait | # Shell alias:给命令创建别名 `alias` 用来给命令创建一个别名。 ## 查看alias所有别名 若直接输入该命令且不带任何参数,则列出当前 Shell 环境中使用了哪些别名。 ```bash user@PxeCtrlSys:~$ alias alias ls='ls --color=auto' ``` 终于知道我的腾讯云debian上`ls`命令没有颜色区分了 ```bash # 没有为ls创建别名 root@StarMeow-Svr:~# alias root@StarMeow-Svr:~# alias ls='ls --color=auto' root@StarMeow-Svr:~# ls # 这儿的文件和文件夹就有颜色区分了 ``` 使用 `alias` 当然也可以自定义别名,比如说一般的关机命令是`shutdown-h now`,写起来比较长,这时可以重新定义一个关机命令,以后就方便多了。使用 alias 定义的别名命令也是支持 Tab 键补全的,如下所示: ```bash alias myShutdown='shutdown -h now' ``` 注意,这样定义别名只能在当前 Shell 环境中有效,换句话说,重新登录后这个别名就消失了。 ## 永久生效alias别名 为了确保永远生效,可以将该别名手动写入到用户主目录中的`.bashrc`文件。 ```bash root@StarMeow-Svr:~# vim /root/.bashrc # 将下方代码取消被注释 export LS_OPTIONS='--color=auto' eval "`dircolors`" alias ls='ls $LS_OPTIONS' alias ll='ls $LS_OPTIONS -l' alias l='ls $LS_OPTIONS -lA' # 修改完后使其生效 root@StarMeow-Svr:~# source ~/.bashrc root@StarMeow-Svr:~# ls # 这儿的文件和文件夹就有颜色区分了 root@StarMeow-Svr:~# alias alias l='ls $LS_OPTIONS -lA' alias ll='ls $LS_OPTIONS -l' alias ls='ls $LS_OPTIONS' ``` ## 删除alias别名 使用 `unalias` 内建命令可以删除当前 Shell 环境中的别名。`unalias` 有两种使用方法: - 第一种用法是在命令后跟上某个命令的别名,用于删除指定的别名。 - 第二种用法是在命令后接`-a`参数,删除当前 Shell 环境中所有的别名。 同样,这两种方法都是在当前 Shell 环境中生效的。要想永久删除在`.bashrc`文件中定义的别名,只能进入该文件手动删除。 ```bash # 例如已经通过alias查到如下别名ls user@PxeCtrlSys:~$ alias alias ls='ls --color=auto' # 使用unalias ls就可以删除当前环境的别名 user@PxeCtrlSys:~$ unalias ls ``` # Shell echo:输出字符串 用于字符串输出`echo string` ## 显示普通字符串 ```bash echo "It is a string1" ``` 这里的双引号可以完全省略 ```bash echo It is a string2 ``` ## 显示转义字符 ```bash #!/bin/bash echo "\"It is a string1\"" echo \"It is a string2\" # 运行结果 user@PxeCtrlSys:~$ ./test.sh "It is a string1" "It is a string2" ``` 默认情况下,`echo` 不会解析以反斜杠`\`开头的转义字符。比如,`\n`表示换行,`echo` 默认会将它作为普通字符对待。 ```bash echo "hello \nworld" # 运行结果 hello \nworld echo -e "hello \nworld" # 运行结果 hello world ``` 同样双引号都是可以省略的 ## 显示变量 `read`命令从标准输入中读取一行,并把输入行的每个字段的值指定给shell变量 ```bash #!/bin/bash read name echo "You entered $name" # 运行结果 user@PxeCtrlSys:~$ ./test.sh 66 You entered 66 ``` ## 显示换行:-e参数和\n ```bash #!/bin/bash echo -e "this is first line \n" echo "next" # 运行结果 user@PxeCtrlSys:~$ ./test.sh this is first line next ``` 输出中`-e`表示开启转义,`\n`表示换行 ## 显示不换行:-e参数和\c或-n参数 ```bash #!/bin/bash echo -e "this is first line \c" echo "next" # 运行结果 user@PxeCtrlSys:~$ ./test.sh this is first line next ``` `-e`开启转义,`\c`表示不换行 ```bash echo -n "this is first line" echo -n "next" # 运行结果 this is first linenext ``` ## 输出结果重定向到文件 ```bash #!/bin/bash echo -e "this is first line" > file.ts # 运行结果 user@PxeCtrlSys:~$ ./test.sh user@PxeCtrlSys:~$ ls file.ts software test.sh user@PxeCtrlSys:~$ cat file.ts this is first line ``` ## 原样输出,不转义,不取变量 直接使用单引号即可 ```bash #!/bin/bash name='lr' echo '$name"\' # 运行结果 user@PxeCtrlSys:~$ ./test.sh $name"\ ``` ## 显示命令执行结果 使用反引号,而不是单引号,可以执行Linux的命令 ```bash #!/bin/bash echo `date` # 运行结果 user@PxeCtrlSys:~$ ./test.sh Tue Mar 5 10:41:55 CST 2019 ``` # Shell exit:退出Shell命令 exit 是一个 Shell 内置命令,用来退出当前Shell: - 如果在终端中直接运行 `exit` 命令,会退出当前登录的 Shell,并关闭终端; - 如果在Shell中出现 `exit` 命令,会停止执行后边的所有代码,立即退出 Shell 脚本。 `exit` 命令可以接受的参数是一个状态值 `n`,代表退出时的状态。如果不指定,默认状态值是 0。 ```bash #!/bin/bash echo "exit命令前输出" exit 9 echo "exit命令后输出" # 运行结果 user@PxeCtrlSys:~$ ./test.sh exit命令前输出 # 也就是说exit后面的语句已经不会执行了 # 紧接着用 $? 来获取test.sh的退出状态 user@PxeCtrlSys:~$ echo $? 9 ``` # Shell ulimit:显示并设置进程资源速度 系统的可用资源是有限的,如果不限制用户和进程对系统资源的使用,则很容易陷入资源耗尽的地步,而使用 ulimit 命令可以控制进程对可用资源的访问(ulimit 是一个 Shell 内置命令)。 默认情况下 Linux 系统的各个资源都做了软硬限制,其中硬限制的作用是控制软限制(换言之,软限制不能高于硬限制)。使用`ulimit -a`可以查看当前系统的软限制,使用命令`ulimit -a –H`可查看系统的硬限制。 ## ulimit -a查看软限制 ```bash user@PxeCtrlSys:~$ ulimit -a core file size (blocks, -c) 0 # core文件大小,单位是block,默认为0 data seg size (kbytes, -d) unlimited # 数据段大小,单位是kbyte,默认不做限制 scheduling priority (-e) 0 # 调度优先级,默认为0 file size (blocks, -f) unlimited # 创建文件的大小,单位是block,默认不做限制 pending signals (-i) 15596 # 挂起的信号数量,默认是8192 max locked memory (kbytes, -l) 64 # 最大锁定内存的值,单位是kbyte,默认是32 max memory size (kbytes, -m) unlimited # 最大可用的常驻内存值,单位是kbyte,默认不做限制 open files (-n) 65536 # 最大打开的文件数,默认是1024 pipe size (512 bytes, -p) 8 # 管道最大缓冲区的值 POSIX message queues (bytes, -q) 819200 # 消息队列的最大值,单位是byte real-time priority (-r) 0 # 程序的实时性优先级,默认为0 stack size (kbytes, -s) 8192 # 栈大小,单位是kbyte cpu time (seconds, -t) unlimited # 最大cpu占用时间,默认不做限制 max user processes (-u) 15596 # 用户最大进程数,默认是8192 virtual memory (kbytes, -v) unlimited # 最大虚拟内存,单位是kbyte,默认不做限制 file locks (-x) unlimited # 文件锁,默认不做限制 ``` 每一行中都包含了相应的改变该项设置的参数,以最大可以打开的文件数为例(`open files` 默认是 1024),想要增大至 4096 则按照如下命令设置(可参照此方法调整其他参数)。 ```bash # -n参数是设置最大文件打开数 # 下面命令会同时设置硬限制和软限制 user@PxeCtrlSys:~$ ulimit -n 65536 # 使用-S参数单独设置软限制 user@PxeCtrlSys:~$ ulimit -S -n 65536 # 使用-H参数单独设置硬限制 user@PxeCtrlSys:~$ ulimit -H -n 65536 ``` ## limits.conf 配置文件 使用 `ulimit` 直接调整参数,只会在当前运行时生效,一旦系统重启,所有调整过的参数就会变回系统默认值。所以建议将所有的改动放在 `ulimit` 的系统配置文件中。 ```bash user@PxeCtrlSys:~$ cat /etc/security/limits.conf # /etc/security/limits.conf #该文件是ulimit的配置文件,任何对系统的ulimit的修改都应写入该文件 #Each line describes a limit for a user in the form: #配置应该写成西面格式,即每个配置占用1行,每行4列 #<domain> <type> <item> <value> # #Where: #<domain>取值如下:一个用户名、一个组名,组名前面用@符号、通配符*、通配符% #<domain> can be: # - a user name # - a group name, with @group syntax # - the wildcard *, for default entry # - the wildcard %, can be also used with %group syntax, # for maxlogin limit # - NOTE: group and wildcard limits are not applied to root. # To apply a limit to the root user, <domain> must be # the literal username root. # #<type>有两个可用值:soft用于设置软限制、hard用于设置硬限制 #<type> can have the two values: # - "soft" for enforcing the soft limits # - "hard" for enforcing hard limits # #<item> can be one of the following: # - core - limits the core file size (KB) # - data - max data size (KB) # - fsize - maximum filesize (KB) # - memlock - max locked-in-memory address space (KB) # - nofile - max number of open files # - rss - max resident set size (KB) # - stack - max stack size (KB) # - cpu - max CPU time (MIN) # - nproc - max number of processes # - as - address space limit (KB) # - maxlogins - max number of logins for this user # - maxsyslogins - max number of logins on the system # - priority - the priority to run user process with # - locks - max number of file locks the user can hold # - sigpending - max number of pending signals # - msgqueue - max memory used by POSIX message queues (bytes) # - nice - max nice priority allowed to raise to values: [-20, 19] # - rtprio - max realtime priority # - chroot - change root to directory (Debian-specific) # #<domain> <type> <item> <value> # #以下是使用样例 #* soft core 0 #root hard core 100000 #* hard rss 10000 #@student hard nproc 20 #@faculty soft nproc 20 #@faculty hard nproc 50 #ftp hard nproc 0 #ftp - chroot /ftp #@student - maxlogins 4 # End of file ``` # Shell printf命令 模仿C语言的`printf()`程序,printf的脚本比echo移植性好。 `printf`使用引用文本或空格分隔的参数,外面可以在`printf`中使用格式化字符串,还可以制定字符串的宽度,左右对齐方式等。默认`printf`不会像`echo`自动添加换行符,可以手动添加`\n` 命令语法:`printf format-string [arguments...]` - `format-string`为格式控制字符串 - `arguments`参数列表 ```bash user@PxeCtrlSys:~$ echo "123" 123 user@PxeCtrlSys:~$ printf "123" 123user@PxeCtrlSys:~$ printf "123\n" 123 ``` 格式化输出 ```bash #!/bin/bash printf "%-10s %-8s %-4s\n" 姓名 性别 体重 printf "%-10s %-8s %-4.2f\n" 郭静 男 40 # 运行结果 user@PxeCtrlSys:~$ ./test.sh 姓名 性别 体重 郭静 男 40.00 ``` `%s`、`%c`、`%d`、`%f`都是格式化替代符 `%-10s`指一个宽度为10个字符(`-`表示左对齐,没有该符号就是右对齐),任何字符都会被显示在10个字符宽的字符内。如果不足则以空格填充,超出也会将内容全部显示出来。 `%-4.2f`指格式化为小数,`.2`指保留两位小数。 **%d %s %c %f** 格式替代符详解: - **d: Decimal 十进制整数** :对应位置参数必须是十进制整数,否则报错! - **s: String 字符串** :对应位置参数必须是字符串或者字符型,否则报错! - **c: Char 字符** :对应位置参数必须是字符串或者字符型,否则报错! - **f: Float 浮点** : 对应位置参数必须是数字型,否则报错! ```bash #!/bin/bash #!/bin/bash # 格式化字符串为双引号 printf "%d %s\n" 1 "abc" # 格式化字符串为单引号,效果和双引号一致 printf '%d %s\n' 1 'abc' # 没有引号也可以输出,但要求参数只能是一个的情况 printf %s abc echo # 使用这个是为了输出换行 # 格式中只指定了一个参数,但多出的参数仍然会被按照该格式输出,格式化字符串被重用 printf %s abc "def" echo printf "\s\n" qwe "zxc" # 参数按照格式化指定个数进行分组输出,而不够的将输出空值 printf "%s %s %s\n" a b c d e f g # 如果没有参数,那么 %s 用空值代替,%d用0代替 printf "%s 和 %d\n" # 运行结果 user@PxeCtrlSys:~$ ./test.sh 1 abc 1 abc abc abcdef \s a b c d e f g 和 0 ``` ## printf转义序列 | 序列 | 说明 | | ------ | -------------------------------------------------------------- | | \\a | 警告字符,通常为ASCII的BEL字符 | | \\b | 后退 | | \\c | 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略 | | \\f | 换页(formfeed) | | \\n | 换行 | | \\r | 回车(Carriage return) | | \\t | 水平制表符 | | \\v | 垂直制表符 | | \\\ | 一个字面上的反斜杠字符 | | \\ddd | 表示1到3位数八进制值的字符。仅在格式字符串中有效 | | \\0ddd | 表示1到3位的八进制值字符 | ```bash #!/bin/bash printf "<%s>" "a\nb" echo printf "<%b>" "a\nb" echo printf "abc\adef" echo 这儿要换行的 # 运行结果 user@PxeCtrlSys:~$ ./test.sh <a\nb> <a b> abcdef这儿要换行的 ``` # Shell test 命令 Shell中的test用于检查某个条件是否成立,可以进行数值、字符和文件三个方面的测试。 ## 数值测试 | 参数 | 说明 | | --- | ------------ | | -eq | 等于则为真 | | -ne | 不等于则为真 | | -gt | 大于则为真 | | -ge | 大于等于则为真 | | -lt | 小于则为真 | | -le | 小于等于则为真 | ```bash #!/bin/bash num1=100 num2=200 if test $[num1] -eq $[num2] then echo '两个数相等' else echo '两个数不相等' fi # 运行结果 user@PxeCtrlSys:~$ ./test.sh 两个数不相等 #!/bin/bash num1=100 num2=200 # 也可以直接比较两个数的结果 if test $num1 -eq $num2 then echo "相等" else echo "不相等" fi # 运行结果 user@PxeCtrlSys:~$ ./test.sh 不相等 ``` **代码中的`[]`执行基本的算术运算**,如 ```bash #!/bin/bash num1=100 num2=200 # 注意等号两边不能有空格,$[num1+num2]等同于`expr $num1 + $num2` result=$[num1+num2] echo "两个数相加的结果为:$result" # 运行结果 user@PxeCtrlSys:~$ ./test.sh 两个数相加的结果为:300 ``` ## 字符串测试 | 参数 | 说明 | | -------- | --------------------- | | = | 等于则为真 | | != | 不相等则为真 | | -z 字符串 | 字符串的长度为零则为真 | | -n 字符串 | 字符串的长度不为零则为真 | ```bash #!/bin/bash str1='shell' str2='she11' if test $str1 = $str2 then echo "两个字符串相等" else echo "两个字符串不相等" fi if test -n $str1 then echo "字符串的长度不为0" else echo "字符串的长度为0" fi # 运行结果 user@PxeCtrlSys:~$ ./test.sh 两个字符串不相等 字符串的长度不为0 ``` ## 文件测试 | 参数 | 说明 | | -------- | -------------------------------- | | -e 文件名 | 如果文件存在则为真 | | -r 文件名 | 如果文件存在且可读则为真 | | -w 文件名 | 如果文件存在且可写则为真 | | -x 文件名 | 如果文件存在且可执行则为真 | | -s 文件名 | 如果文件存在且至少有一个字符则为真 | | -d 文件名 | 如果文件存在且为目录则为真 | | -f 文件名 | 如果文件存在且为普通文件则为真 | | -c 文件名 | 如果文件存在且为字符型特殊文件则为真 | | -b 文件名 | 如果文件存在且为块特殊文件则为真 | ```bash #!/bin/bash if test -w /home/user/file.ts then echo "文件存在且可写" else echo "文件不存在" fi # 运行结果 user@PxeCtrlSys:~$ ./test.sh 文件存在且可写 ``` ## 与或非逻辑运算test Linux还提供了与`-a`、或`-o`、非`!`三个逻辑操作符用于将测试条件连接起来,优先级为:`!` > `-a` > `-o`。 ```bash #!/bin/bash # file.ts文件存在且可写,以及test.sh存在且可执行时为真 if test -w /home/user/file.ts -a -x /home/user/test.sh then echo "文件都存在,且一个可写,一个可执行" else echo "文件不存在" fi # 运行结果 user@PxeCtrlSys:~$ ./test.sh 文件都存在,且一个可写,一个可执行 ``` # Shell流程控制 在sh/bash里如果else分支没有语句执行,就不要写这个else ## if else `if`语句语法格式 ```bash if condition then command1 command2 ... commandN fi ``` 或者 ```bash if condition; then command1 command2 ... commandN fi ``` 请注意 `condition` 后边的分号`;`,当 `if` 和 `then` 位于同一行的时候,这个分号是必须的,否则会有语法错误。 写成一行(适用于终端命令提示符): ```bash if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi # 在这个系统上运行 user@PxeCtrlSys:~$ ps -ef | grep -c "ssh" 15 ``` `if else` 语法格式 ```bash if condition then command1 command2 ... commandN else command fi ``` Shell 支持任意数目的分支,当分支比较多时,`if else-if else` 语法格式 ```bash if condition1 then command1 elif condition2 then command2 else commandN fi ``` 判断两个变量是否相等 ```bash #!/bin/bash a=10 b=10 if [ $a -eq $b ] then echo "a和b相等" elif [ $a -gt $b ] then echo "a大于b" else echo "a小于b" # -lt fi # 运行结果 user@PxeCtrlSys:~$ ./test.sh a和b相等 ``` 多条件判断 ```bash #!/bin/bash read age if (( $age <= 2 )); then echo "婴儿" elif (( $age >= 3 && $age <= 8 )); then echo "幼儿" elif (( $age >= 9 && $age <= 17 )); then echo "少年" elif (( $age >= 18 && $age <=25 )); then echo "成年" elif (( $age >= 26 && $age <= 40 )); then echo "青年" elif (( $age >= 41 && $age <= 60 )); then echo "中年" else echo "老年" fi ``` ### 与test结合使用 ```bash #!/bin/bash a=$[3*6] b=$[12+3] if test $[a] -eq $[b] then echo "a和b相等" else echo "a和b不相等" fi # 运行结果 user@PxeCtrlSys:~$ ./test.sh a和b不相等 ``` ## for循环 一般格式为 ```bash for var in item1 item2 ... itemN do command1 command2 ... commandN done ``` 写成一行: ```bash for var in item1 item2 ... itemN; do command1; command2; … commandN; done; ``` 例如: ```bash user@PxeCtrlSys:~$ for i in 1 2 3 4; do echo $i; echo $[i*i]; done; 1 1 2 4 3 9 4 16 ``` 当变量在列表里,`for`循环即执行一次所有命令,使用变量名获取列表中的当前取值。命令可为任何有效的shell命令和语句。`in`列表可以包含替换、字符串和文件名。 `in`列表是可选的,如果不使用它,`for`循环使用命令行的位置参数 ```bash #!/bin/bash # 顺序输出当前列表中的数字 for i in 1 2 3 4 5 do echo "循环中输出的值为:$i" done # 顺序输出字符串中的字符 times=0 for str in "顺序输出字符串中的数字" do echo 输出 $[time+1] 次 echo $str done # 运行结果 user@PxeCtrlSys:~$ ./test.sh 循环中输出的值为:1 循环中输出的值为:2 循环中输出的值为:3 循环中输出的值为:4 循环中输出的值为:5 输出 1 次 顺序输出字符串中的数字 ``` 如果是循环字符串,那么它并不是按照字符串中每一个字符输出,而是当作一个整体,循环整个串一次 ## while语句---条件满足循环 `while`循环用于不断执行一系列命令,也用于从输入文件中读取数据;命令通常为测试条件,格式为: ```bash while condition do command done ``` 使用w`hile`循环,当不满足条件时,就跳出循环 ```bash #!/bin/bash num=1 while(( $num<=5 )) do echo $num let "num++" # 直接使用:let num++ 也是可以的 done # 运行结果 user@PxeCtrlSys:~$ ./test.sh 1 2 3 4 5 ``` 上方用了Bash的`let`命令,它用于执行一个或多个表达式,变量计算中不需要加上`$`来表示变量。 `while`循环可用于读取键盘输入 ```bash #!/bin/bash echo "按下<Ctrl-D>退出" echo -n "输入你所想的编程语言:" while read Code do echo "$Code 是一门流行语言" done # 运行结果 user@PxeCtrlSys:~$ ./test.sh 按下<Ctrl-D>退出 输入你所想的编程语言:python python 是一门流行语言 shell shell 是一门流行语言 ``` ## 无限循环 语法格式 ```bash while : do command done ``` 或者 ```bash while true do command done ``` 或者 ```bash for (( ; ; )) do command done ``` ## until循环---条件不满足循环 `until`循环执行一系列命令,知道条件为`true`时停止。 `until`循环与`while`循环再处理方式上刚好相反。 一般`while`循环优于`until`循环,但在某些极少情况`until`更有用。 语法格式 ```bash until condition do command done ``` condition一般为条件表达式,如果返回值为false,则继续执行循环体内的语句,否则跳出循环。**表达式为true跳出循环** ```bash #!/bin/bash a=0 # a小等于10取反,就表示a大等于10,比如3<10为false继续循环 # 由于是先+1再判断,当输出了a=9,+1后,就变成10,满足了10=10为true,就退出循环 until [ ! $a -lt 10 ] do echo $a a=`expr $a + 1` done # 运行结果 user@PxeCtrlSys:~$ ./test.sh 0 1 2 3 4 5 6 7 8 9 ``` ## case多选择语句 可以用`case`语句匹配一个值与一个模式,如果匹配成功,则执行相匹配的命令。语法格式 ```bash case 值 in 模式1) command1 command2 ... commandN ;; 模式2) command1 command2 ... commandN ;; esac ``` `case`工作方式如上。取值后面必须为单词`in`,每一模式必须以右括号`)`结束。取值可以为变量或常熟。匹配发现取值复核某一模式后,其间所有命令开始执行直到`;;`。 取值将检测匹配每一个模式。一旦模式匹配,则执行完该匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号`*`捕获该值,再执行后面的命令。 ```bash #!/bin/bash echo "输入1到5之间的数字:" echo 你输入的数字为: read num case $num in 1) echo '你输入了1' ;; 2) echo 你输入了2 ;; 3) echo "你输入了3";; 4) echo "你输入了4" ;; 5) echo 你输入了5 ;; *) echo "你输入的数字不在该区间" ;; esac # 运行结果 user@PxeCtrlSys:~$ ./test.sh 输入1到5之间的数字: 你输入的数字为: 1 你输入了1 user@PxeCtrlSys:~$ ./test.sh 输入1到5之间的数字: 你输入的数字为: 2 你输入了2 user@PxeCtrlSys:~$ ./test.sh 输入1到5之间的数字: 你输入的数字为: 3 你输入了3 user@PxeCtrlSys:~$ ./test.sh 输入1到5之间的数字: 你输入的数字为: 4 你输入了4 user@PxeCtrlSys:~$ ./test.sh 输入1到5之间的数字: 你输入的数字为: 5 你输入了5 user@PxeCtrlSys:~$ ./test.sh 输入1到5之间的数字: 你输入的数字为: 6 你输入的数字不在该区间 ``` ## 跳出循环 未达到循环结束条件时强制跳出循环 ### break `break`命令允许跳出所有循环(终止执行后面的所有循环)。 ```bash #!/bin/bash while : # 无限循环 do echo -n "输入1到5之间的数字:" read num case $num in 1|2|3|4|5) echo "你输入的数字为:$num" ;; *) echo '你数的数字不在该区间,跳出循环' break;; esac done # 运行结果 user@PxeCtrlSys:~$ ./test.sh 输入1到5之间的数字:3 你输入的数字为:3 输入1到5之间的数字:6 你数的数字不在该区间,跳出循环 ``` ### continue 当执行到`continue`语句是,不会跳出循环,而是之后的语句不会继续执行 ```bash #!/bin/bash while : # 无限循环 do echo -n "输入1到5之间的数字:" read num case $num in 1|2|3|4|5) echo "你输入的数字为:$num" ;; *) echo '你数的数字不在该区间' continue echo "continue之后的不会再执行" ;; esac done # 运行结果 user@PxeCtrlSys:~$ ./test.sh 输入1到5之间的数字:3 你输入的数字为:3 输入1到5之间的数字:6 你数的数字不在该区间 输入1到5之间的数字:2 你输入的数字为:2 输入1到5之间的数字:^C ``` ### esac `case`的语法需要一个`esac`(就是`case`反过来)作为结束标记,每个`case`分支用右圆括号,用两个分号表示`break`。 # Shell输入/输出重定向 大多数Unix系统命令是从终端接受输入并将产生的输出发送回终端。一个命令通常从一个叫标准输入的地方读取,默认情况下,这恰好是终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,也恰好时终端。 | 命令 | 说明 | | --------------- | ---------------------------------------------- | | command > file | 将输出重定向到 file。 | | command < file | 将输入重定向到 file。 | | command >> file | 将输出以追加的方式重定向到 file。 | | n > file | 将文件描述符为 n 的文件重定向到 file。 | | n >> file | 将文件描述符为 n 的文件以追加的方式重定向到 file。 | | n >& m | 将输出文件 m 和 n 合并。 | | n <& m | 将输入文件 m 和 n 合并。 | | << tag | 将开始标记 tag 和结束标记 tag 之间的内容作为输入。 | > 需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。 ## 输出重定向 重定向一般通过在命令间插入特定的符号来实现,类似于`command > file`,这个命令的意思就是将命令`command`的输出内容存入`file`,且`file`中已经存在的内容将被新内容替代。如果要将新内容添加到文件末尾,需要使用`>>`操作符。 ```bash user@PxeCtrlSys:~$ who > users user@PxeCtrlSys:~$ cat users user pts/0 2019-03-11 09:04 (192.168.96.16) user pts/1 2019-03-11 09:04 (192.168.96.16) ``` ### > 重定向覆盖 使用`>`并不会在终端显示内容,而是将`who`的输出保存到`users`这个文件中了。 ```bash user@PxeCtrlSys:~$ ls / > users user@PxeCtrlSys:~$ cat users bin boot dev etc home initrd.img lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var vmlinuz ``` 也就是说使用`>`如果再次重定向到同样的文件,该文件内容则会被覆盖。 ### >> 重定向追加 如果不希望覆盖,则使用`>>`追加方式。 ```bash user@PxeCtrlSys:~$ who >> users user@PxeCtrlSys:~$ cat users bin boot dev etc home initrd.img lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var vmlinuz user pts/0 2019-03-11 09:04 (192.168.96.16) user pts/1 2019-03-11 09:04 (192.168.96.16) ``` ## 输入重定向 和输出重定向类似,也可以从文件获取输入,语法为`command < file`,这样,本来需要从键盘获取输入的命令就会转移到从文件中读取内容。 ```bash user@PxeCtrlSys:~$ cat users user pts/0 2019-03-11 09:04 (192.168.96.16) user pts/1 2019-03-11 09:04 (192.168.96.16) # 获取文件的行数 user@PxeCtrlSys:~$ wc -l users 2 users # 输入重定向到文件,获取行数 user@PxeCtrlSys:~$ wc -l < users 2 ``` 结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。 ```bash command < infile > outfile ``` 同时替换输入和输出,执行command,从文件infile读取内容,然后将输出写入到outfile中。 ## 重定向深入讲解 一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件: - 标准输入文件(stdin):`stdin`的文件描述符为0,Unix程序默认从`stdin`读取数据。 - 标准输出文件(stdout):`stdout` 的文件描述符为1,Unix程序默认向`stdout`输出数据。 - 标准错误文件(stderr):`stderr`的文件描述符为2,Unix程序会向`stderr`流中写入错误信息。 默认情况下,`command > file` 将 `stdout` 重定向到 `file`,`command < file` 将`stdin` 重定向到 `file`。 ### stderr重定向到file 如果希望 `stderr` 重定向到 `file`,可以这样写: ```bash command 2 > file ``` ### stderr追加到file 如果希望 `stderr` 追加到 `file` 文件末尾,可以这样写: ```bash command 2 >> file ``` `2` 表示标准错误文件(stderr)。 ### stdout和stderr合并重定向到file 如果希望将 `stdout` 和 `stderr` 合并后重定向到 `file`,可以这样写: ```bash command > file 2>&1 或者 command >> file 2>&1 ``` ### stdin和stdout都重定向 如果希望对 `stdin` 和 `stdout` 都重定向,可以这样写(`command` 命令将 `stdin` 重定向到 `file1`,将 `stdout` 重定向到 `file2`。): ```bash command < file1 > file2 ``` ## Here Document Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。 ```bash command << delimiter document delimiter ``` 它的作用是将两个 `delimiter` 之间的内容(document) 作为输入传递给 `command`。 delimiter:定界符、分隔符、分界符 - 结尾的`delimiter` 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。 - 开始的`delimiter`前后的空格会被忽略掉。 ```bash user@PxeCtrlSys:~$ wc -l << EOF shell python EOF # 输入完回车就在下方显示结果 2 ``` 将分隔符之间的文本显示出来 ```bash user@PxeCtrlSys:~$ cat << EOF > shell > python > EOF # 回车后就会输入下方结果 shell python ``` ## /dev/null 文件 如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /`dev/null`: ```bash command > /dev/null ``` `/dev/null` 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 `/dev/null` 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。 如果希望屏蔽 `stdout` 和 `stderr`,可以这样写: ```bash command > /dev/null 2>&1 ``` > 注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。 # Shell文件包含 Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。 Shell 文件包含的语法格式如下: ```bash . filename # 注意点号(.)和文件名中间有一空格 或 source filename ``` 创建两个 shell 脚本文件。 test1.sh 代码如下 ```bash user@PxeCtrlSys:~$ vim test1.sh user@PxeCtrlSys:~$ cat test1.sh a=2 b=3 ``` test2.sh 代码如下 ```bash user@PxeCtrlSys:~$ vim test2.sh user@PxeCtrlSys:~$ cat test2.sh #!/bin/bash # 使用 . 号来引用test1.sh文件 . ./test1.sh # 或者使用下方的代码 # source ./test1.sh echo "从test1.sh引入的a的值为:$a,b的值为:$b" ``` 为 test2.sh 添加执行权限,运行结果 ```bash user@PxeCtrlSys:~$ chmod +x test2.sh user@PxeCtrlSys:~$ ./test2.sh 从test1.sh引入的a的值为:2,b的值为:3 ``` > 被包含的文件 test1.sh 不需要可执行权限。 参考学习两篇文章 http://www.runoob.com/linux/linux-shell.html http://c.biancheng.net/shell/
很赞哦! (0)
相关文章
文章交流
- emoji