简介
Shell是脚本语言,解释型,无需提前编译。
Shel同时也是一个程序,它的一端连接着Linux内核,另一端连接着用户和其他应用程序,也就是说Shell是用户或应用程序与内核沟通的桥梁,

扩展名为.sh
#!/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都可以使用下面命令查看
user@PxeCtrlSys:~$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash现在Linux基本都是用的bash Shell,查看Linux的默认shell
user@PxeCtrlSys:~$ echo $SHELL
/bin/bash运行方法
- 作为可执行程序
代码保存test.sh文本中
# 进入脚本所在目录,让脚本拥有可执行权限
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 脚本的文件名
/bin/sh test.sh
# 或者
/bin/bash test.shShell变量
在Bash Shell中,每一个变量的值都是字符串,无论给变量赋值时有没使用引号,值都会以字符串的形式存储,即使将整数或小数赋值给变量,他们也会被视为字符串。
变量赋值
定义变量,三种定义方式,显式赋值
name="test"
name='test'
name=test注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。同时,变量名的命名须遵循如下规则:
- 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
- 中间不能有空格,可以使用下划线(_)。
- 不能使用标点符号。
- 不能使用bash里的关键字(可用help命令查看保留关键字)。
如果变量值包含空白字符,就必须使用引号包围起来,例如name="bom smith"
用语句给变量赋值
for file in `ls /etc`
或
for file in $(ls /etc)将/etc目录下的文件名循环出来。
使用变量
name="test"
echo $name
# 或者加花括号,含义一样,加花括号是为了帮助解释器识别变量的边界
echo ${name}例如和变量连接的字符
#!/bin/bash
for item in Python Java Shell Bat; do
echo "This is ${item}Script"
done运行结果如下
This is PythonScript
This is JavaScript
This is ShellScript
This is BatScript如果没有花括号,那么变量就是$itemScript,其值为空,结果就会错误。推荐加花括号{}。
修改变量的值
已定义过的变量可以重新赋值
#!/bin/bash
name='test'
echo ${name} # test
name="new"
echo $name # new变量赋值不能加$符号,只有在使用键盘时才能加$。
命令执行结果赋值给变量
支持将命令的执行结果赋值给变量使用,常见的两种方式:
value=`command`
# 或
value=$(command)
# 默认输出以空白符填充
echo $value
# 按照原格式输出,例如有换行符的
echo "$value"第一种是使用反引号包围起来,第二种是使用$()包围起来(推荐使用第二种方式)。command是执行命令。
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执行结果赋值给变量,输出不会自动添加换行符。
如果被替换的命令的输出内容包括多行(也即有换行符),或者含有多个连续的空白符,那么在输出变量时应该将变量用双引号包围,否则系统会使用默认的空白符来填充,这会导致换行无效,以及连续的空白符被压缩成一个。请看下面的代码:
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 命令可以将变量定义为只读变量,只读变量的值不能被改变。
下面的例子尝试更改只读变量,结果报错:
#!/bin/bash
name='test'
readonly name
name='new'
echo $name运行结果
test
/tmp/280278210/main.sh: line 4: name: readonly variable
# 当改为只读后,再次赋值就会报错删除变量
使用 unset 命令可以删除变量
#!/bin/bash
name='test'
unset name
echo $name # 执行没有输出变量删除后不能再次使用。unset命令不能删除只读变量。
变量类型,作用域
运行shell时,会同时存在三种变量:
- 全局变量 指变量在当前的整个 Shell 会话中都有效。每个 Shell 会话都有自己的作用域,彼此之间互不影响。在 Shell 中定义的变量,默认就是全局变量。
- 局部变量 在 Shell 函数中定义的变量默认也是全局变量,它和在函数外部定义变量拥有一样的效果;要想变量的作用域仅限于函数内部,那么可以在定义时加上
local命令,此时该变量就成了局部变量。 - 环境变量 只在当前 Shell 会话中有效,如果使用export命令将它导出,那么它就在所有的子 Shell 中也有效了。
Shell字符串
字符串最常用的数据类型,可以用单/双引号,也可以不用引号。
单引号
str='this is a string'单引号字符串的限制:
- 单引号里面的任何字符都会原样输出,单引号字符串里面的变量是无效的,也就是变量那一串就只是字符串;
- 单引号字符中不能出现单独一个的单引号(对单引号使用转义符后也不行),但能成对出现,相当于字符串拼接
s='abc\'0e',这个就会报错,而s='abc\'0'e'会输出abc\0e
双引号
- 双引号中可以有变量,输出时会先解析里面的变量和命令
- 双引号也可以出现转义字符
#!/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 中你不需要使用任何运算符,将两个字符串并排放在一起就能实现拼接。
#!/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名字获取字符串长度
使用${#变量名}获取长度
#!/bin/bash
name='lr'
echo ${#name}
# 运行结果
2截取字符串
Shell 截取字符串通常有两种方式:从指定位置开始截取和从指定字符(子字符串)开始截取。
从指定位置开始截取
种方式需要两个参数:除了指定起始位置,还需要截取长度,才能最终确定要截取的字符串。
从字符串左边开始计数,格式为${string: start :length}
#!/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(这符合常人思维)。计数方向不同,起始数字也不同。
- 不管从哪边开始计数,截取方向都是从左到右。
echo ${str:0-5:2} # 从右向左数第5个,长度为2的字符串
echo ${str:0-5} # 省略长度,直接从指定位置截取到最后
# 运行结果
Sh
Shell从指定字符(子字符串)开始截取
这种截取方式无法指定字符串长度,只能从指定字符(子字符串)截取到字符串末尾。Shell 可以截取指定字符(子字符串)右边的所有字符,也可以截取左边的所有字符。
使用 # 号截取右边字符,格式为${string#*chars}
其中,string 表示要截取的字符,chars 是指定的字符(或者子字符串),*是通配符的一种,表示任意长度的字符串。*chars连起来使用的意思是:忽略左边的所有字符,直到遇见 chars(chars 不会被截取)。
#!/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*}
#!/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 左边的所有字符。 |
查找字符串
#!/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。
定义数组
用括号表示数组,数组元素用空格符号分隔开。
数组名=(值1 值2 值3 ... 值n)或者
array_name=(
value0
value1
...
valuen
)还可以单独定义数组的各个分量
array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen可以不使用连续的下标,而且下标的范围没有限制
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读取数组
一般格式
${数组名[下标]}例如valuen=${array_name[n]},输出数组所有元素用echo ${array_name[@]}
#!/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 表示数组名。两种形式是等价的。
#!/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 删除整个数组,所有元素都会消失。
#!/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[*]})
#!/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注释
以#开始的行就是注释,会被解释器忽略
通过每一行添加一个#来设置多行注释
如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢?
每一行加个#符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。
多行注释
:<<EOF
注释内容...
注释内容...
注释内容...
EOFEOF也可以使用其他符号
:<<'
注释内容...
注释内容...
注释内容...
'
:<<!
注释内容...
注释内容...
注释内容...
!Shell脚本传递参数
想脚本传递参数,脚本内获取参数的格式为:$n。n代表一个数字,1为执行脚本的第一个参数,2为执行脚本的第二个参数,以此类推...
给脚本文件传递参数
向脚本传递3个参数,其中$0表示执行的文件名
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# 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"(仍然将每个参数都看作一份数据,彼此之间是独立的)
#!/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
3Shell函数
shell函数格式
[ function ] funname [()]
{
action;
[return int;]
}- 可以带
function func()定义,也可以直接func()定义,不带任何参数。 - 参数返回,可以显示加:
return返回,如果不加,将以最后一条命令运行结果,作为返回值。return后跟数值n(0-255)
无返回值函数
#!/bin/bash
func(){
echo "这是一个函数中的内容"
}
echo "开始调用函数"
func
echo "调用函数完成"
# 运行结果
user@PxeCtrlSys:~$ ./test.sh
开始调用函数
这是一个函数中的内容
调用函数完成有返回值函数
定义一个带return的函数
#!/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() 函数是类似的。
不过,也有一些命令返回其他值,表示不同类型的错误。
#!/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表示第二次参数
#!/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是一款表达式计算工具,使用它完成表达式的求值操作
例如,两个数量价,注意是反引号,而不是单引号
#!/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:进行整数计算
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)) | 对多个表达式同时进行计算。 |
在 (( )) 中使用变量无需加上$前缀,(( )) 会自动解析变量名,这使得代码更加简洁,也符合程序员的书写习惯。
算术运算
# 直接输出运算结果
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逻辑运算
# 结果为真,输出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(())进行自增++和自减--运算
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多个表达式计算
# 先计算第一个表达式,再计算第二个表达式
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
18Shell let:对整数进行数学运算
和双小括号 (( )) 一样,let 命令也只能进行整数运算,不能对小数(浮点数)或者字符串进行运算。
语法格式 let 表达式、let "表达式"、let '表达式',都等价于 ((表达式))。
当表达式中含有 Shell 特殊字符(例如 |)时,需要用双引号" "或者单引号' '将表达式包围起来。
和 (( )) 类似,let 命令也支持一次性计算多个表达式,并且以最后一个表达式的值作为整个 let 命令的执行结果。但是,对于多个表达式之间的分隔符,let 和 (( )) 是有区别的:
let命令以空格来分隔多个表达式;(( ))以逗号,来分隔多个表达式。
user@PxeCtrlSys:~$ i=2
user@PxeCtrlSys:~$ let i+=3
user@PxeCtrlSys:~$ echo $i
5let i+=3等价于((i+=3)),但后者效率更高。
let后面可以跟多个表达式,用空格分隔
user@PxeCtrlSys:~$ a=3
user@PxeCtrlSys:~$ let b=3**2 c=a+b
user@PxeCtrlSys:~$ echo $a $b
3 9
user@PxeCtrlSys:~$ echo $c
12Shell $[]:对整数进行数学运算
和 (())、let 命令类似,$[] 也只能进行整数运算。语法为 $[表达式]
$[] 会对表达式进行计算,并取得计算结果。如果表达式中包含了变量,那么你可以加$,也可以不加。
# 直接输出结果
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]
18Shell declare -i:将变量声明为整数
默认情况下,Shell每一个变量的值都是一个字符串,即使给变量赋值一个数字,它也是字符串。
使用 declare 命令的-i选项可以将一个变量声明为整数类型,这样在进行数学计算时就不会作为字符串处理了。
#!/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。 |
#!/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。 |
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 |
#!/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。 |
#!/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 权限。下面的代码,将检测该文件的各种属性:
#!/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 来确定一个命令是否是内建命令:
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 进程的一个函数。
| 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 环境中使用了哪些别名。
user@PxeCtrlSys:~$ alias
alias ls='ls --color=auto'终于知道我的腾讯云debian上ls命令没有颜色区分了
# 没有为ls创建别名
root@StarMeow-Svr:~# alias
root@StarMeow-Svr:~# alias ls='ls --color=auto'
root@StarMeow-Svr:~# ls
# 这儿的文件和文件夹就有颜色区分了使用 alias 当然也可以自定义别名,比如说一般的关机命令是shutdown-h now,写起来比较长,这时可以重新定义一个关机命令,以后就方便多了。使用 alias 定义的别名命令也是支持 Tab 键补全的,如下所示:
alias myShutdown='shutdown -h now'注意,这样定义别名只能在当前 Shell 环境中有效,换句话说,重新登录后这个别名就消失了。
永久生效alias别名
为了确保永远生效,可以将该别名手动写入到用户主目录中的.bashrc文件。
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文件中定义的别名,只能进入该文件手动删除。
# 例如已经通过alias查到如下别名ls
user@PxeCtrlSys:~$ alias
alias ls='ls --color=auto'
# 使用unalias ls就可以删除当前环境的别名
user@PxeCtrlSys:~$ unalias lsShell echo:输出字符串
用于字符串输出echo string
显示普通字符串
echo "It is a string1"这里的双引号可以完全省略
echo It is a string2显示转义字符
#!/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 默认会将它作为普通字符对待。
echo "hello \nworld"
# 运行结果
hello \nworld
echo -e "hello \nworld"
# 运行结果
hello
world同样双引号都是可以省略的
显示变量
read命令从标准输入中读取一行,并把输入行的每个字段的值指定给shell变量
#!/bin/bash
read name
echo "You entered $name"
# 运行结果
user@PxeCtrlSys:~$ ./test.sh
66
You entered 66显示换行:-e参数和\n
#!/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参数
#!/bin/bash
echo -e "this is first line \c"
echo "next"
# 运行结果
user@PxeCtrlSys:~$ ./test.sh
this is first line next-e开启转义,\c表示不换行
echo -n "this is first line"
echo -n "next"
# 运行结果
this is first linenext输出结果重定向到文件
#!/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原样输出,不转义,不取变量
直接使用单引号即可
#!/bin/bash
name='lr'
echo '$name"\'
# 运行结果
user@PxeCtrlSys:~$ ./test.sh
$name"\显示命令执行结果
使用反引号,而不是单引号,可以执行Linux的命令
#!/bin/bash
echo `date`
# 运行结果
user@PxeCtrlSys:~$ ./test.sh
Tue Mar 5 10:41:55 CST 2019Shell exit:退出Shell命令
exit 是一个 Shell 内置命令,用来退出当前Shell:
- 如果在终端中直接运行
exit命令,会退出当前登录的 Shell,并关闭终端; - 如果在Shell中出现
exit命令,会停止执行后边的所有代码,立即退出 Shell 脚本。
exit 命令可以接受的参数是一个状态值 n,代表退出时的状态。如果不指定,默认状态值是 0。
#!/bin/bash
echo "exit命令前输出"
exit 9
echo "exit命令后输出"
# 运行结果
user@PxeCtrlSys:~$ ./test.sh
exit命令前输出 # 也就是说exit后面的语句已经不会执行了
# 紧接着用 $? 来获取test.sh的退出状态
user@PxeCtrlSys:~$ echo $?
9Shell ulimit:显示并设置进程资源速度
系统的可用资源是有限的,如果不限制用户和进程对系统资源的使用,则很容易陷入资源耗尽的地步,而使用 ulimit 命令可以控制进程对可用资源的访问(ulimit 是一个 Shell 内置命令)。
默认情况下 Linux 系统的各个资源都做了软硬限制,其中硬限制的作用是控制软限制(换言之,软限制不能高于硬限制)。使用ulimit -a可以查看当前系统的软限制,使用命令ulimit -a –H可查看系统的硬限制。
ulimit -a查看软限制
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 则按照如下命令设置(可参照此方法调整其他参数)。
# -n参数是设置最大文件打开数
# 下面命令会同时设置硬限制和软限制
user@PxeCtrlSys:~$ ulimit -n 65536
# 使用-S参数单独设置软限制
user@PxeCtrlSys:~$ ulimit -S -n 65536
# 使用-H参数单独设置硬限制
user@PxeCtrlSys:~$ ulimit -H -n 65536limits.conf 配置文件
使用 ulimit 直接调整参数,只会在当前运行时生效,一旦系统重启,所有调整过的参数就会变回系统默认值。所以建议将所有的改动放在 ulimit 的系统配置文件中。
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 fileShell printf命令
模仿C语言的printf()程序,printf的脚本比echo移植性好。
printf使用引用文本或空格分隔的参数,外面可以在printf中使用格式化字符串,还可以制定字符串的宽度,左右对齐方式等。默认printf不会像echo自动添加换行符,可以手动添加\n
命令语法:printf format-string [arguments...]
format-string为格式控制字符串arguments参数列表
user@PxeCtrlSys:~$ echo "123"
123
user@PxeCtrlSys:~$ printf "123"
123user@PxeCtrlSys:~$ printf "123\n"
123格式化输出
#!/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 浮点 : 对应位置参数必须是数字型,否则报错!
#!/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
和 0printf转义序列
| 序列 | 说明 |
|---|---|
| \a | 警告字符,通常为ASCII的BEL字符 |
| \b | 后退 |
| \c | 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略 |
| \f | 换页(formfeed) |
| \n | 换行 |
| \r | 回车(Carriage return) |
| \t | 水平制表符 |
| \v | 垂直制表符 |
| \\ | 一个字面上的反斜杠字符 |
| \ddd | 表示1到3位数八进制值的字符。仅在格式字符串中有效 |
| \0ddd | 表示1到3位的八进制值字符 |
#!/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 | 小于等于则为真 |
#!/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
不相等代码中的[]执行基本的算术运算,如
#!/bin/bash
num1=100
num2=200
# 注意等号两边不能有空格,$[num1+num2]等同于`expr $num1 + $num2`
result=$[num1+num2]
echo "两个数相加的结果为:$result"
# 运行结果
user@PxeCtrlSys:~$ ./test.sh
两个数相加的结果为:300字符串测试
| 参数 | 说明 |
|---|---|
| = | 等于则为真 |
| != | 不相等则为真 |
| -z 字符串 | 字符串的长度为零则为真 |
| -n 字符串 | 字符串的长度不为零则为真 |
#!/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 文件名 | 如果文件存在且为块特殊文件则为真 |
#!/bin/bash
if test -w /home/user/file.ts
then
echo "文件存在且可写"
else
echo "文件不存在"
fi
# 运行结果
user@PxeCtrlSys:~$ ./test.sh
文件存在且可写与或非逻辑运算test
Linux还提供了与-a、或-o、非!三个逻辑操作符用于将测试条件连接起来,优先级为:! > -a > -o。
#!/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语句语法格式
if condition
then
command1
command2
...
commandN
fi或者
if condition; then
command1
command2
...
commandN
fi请注意 condition 后边的分号;,当 if 和 then 位于同一行的时候,这个分号是必须的,否则会有语法错误。
写成一行(适用于终端命令提示符):
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
# 在这个系统上运行
user@PxeCtrlSys:~$ ps -ef | grep -c "ssh"
15if else 语法格式
if condition
then
command1
command2
...
commandN
else
command
fiShell 支持任意数目的分支,当分支比较多时,if else-if else 语法格式
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi判断两个变量是否相等
#!/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相等多条件判断
#!/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结合使用
#!/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循环
一般格式为
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done写成一行:
for var in item1 item2 ... itemN; do command1; command2; … commandN; done;例如:
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循环使用命令行的位置参数
#!/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循环用于不断执行一系列命令,也用于从输入文件中读取数据;命令通常为测试条件,格式为:
while condition
do
command
done使用while循环,当不满足条件时,就跳出循环
#!/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循环可用于读取键盘输入
#!/bin/bash
echo "按下<Ctrl-D>退出"
echo -n "输入你所想的编程语言:"
while read Code
do
echo "$Code 是一门流行语言"
done
# 运行结果
user@PxeCtrlSys:~$ ./test.sh
按下<Ctrl-D>退出
输入你所想的编程语言:python
python 是一门流行语言
shell
shell 是一门流行语言无限循环
语法格式
while :
do
command
done或者
while true
do
command
done或者
for (( ; ; ))
do
command
doneuntil循环---条件不满足循环
until循环执行一系列命令,知道条件为true时停止。
until循环与while循环再处理方式上刚好相反。
一般while循环优于until循环,但在某些极少情况until更有用。
语法格式
until condition
do
command
donecondition一般为条件表达式,如果返回值为false,则继续执行循环体内的语句,否则跳出循环。表达式为true跳出循环
#!/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
9case多选择语句
可以用case语句匹配一个值与一个模式,如果匹配成功,则执行相匹配的命令。语法格式
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esaccase工作方式如上。取值后面必须为单词in,每一模式必须以右括号)结束。取值可以为变量或常熟。匹配发现取值复核某一模式后,其间所有命令开始执行直到;;。
取值将检测匹配每一个模式。一旦模式匹配,则执行完该匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号*捕获该值,再执行后面的命令。
#!/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命令允许跳出所有循环(终止执行后面的所有循环)。
#!/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语句是,不会跳出循环,而是之后的语句不会继续执行
#!/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之间的数字:^Cesac
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中已经存在的内容将被新内容替代。如果要将新内容添加到文件末尾,需要使用>>操作符。
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这个文件中了。
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也就是说使用>如果再次重定向到同样的文件,该文件内容则会被覆盖。
>> 重定向追加
如果不希望覆盖,则使用>>追加方式。
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,这样,本来需要从键盘获取输入的命令就会转移到从文件中读取内容。
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结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。
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,可以这样写:
command 2 > filestderr追加到file
如果希望 stderr 追加到 file 文件末尾,可以这样写:
command 2 >> file2 表示标准错误文件(stderr)。
stdout和stderr合并重定向到file
如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:
command > file 2>&1
或者
command >> file 2>&1stdin和stdout都重定向
如果希望对 stdin 和 stdout 都重定向,可以这样写(command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。):
command < file1 > file2Here Document
Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。
command << delimiter
document
delimiter它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。
delimiter:定界符、分隔符、分界符
- 结尾的
delimiter一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。 - 开始的
delimiter前后的空格会被忽略掉。
user@PxeCtrlSys:~$ wc -l << EOF
shell
python
EOF # 输入完回车就在下方显示结果
2将分隔符之间的文本显示出来
user@PxeCtrlSys:~$ cat << EOF
> shell
> python
> EOF # 回车后就会输入下方结果
shell
python/dev/null 文件
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
command > /dev/null/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
如果希望屏蔽 stdout 和 stderr,可以这样写:
command > /dev/null 2>&1注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
Shell文件包含
Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。
Shell 文件包含的语法格式如下:
. filename # 注意点号(.)和文件名中间有一空格
或
source filename创建两个 shell 脚本文件。
test1.sh 代码如下
user@PxeCtrlSys:~$ vim test1.sh
user@PxeCtrlSys:~$ cat test1.sh
a=2
b=3test2.sh 代码如下
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 添加执行权限,运行结果
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/
转载自
吾星喵:https://blog.starmeow.cn/detail/7ef605fc8dba5425d6965fbd4c8fbe1f/