参考链接: https://blog.csdn.net/yuioplv/article/details/81006250
Shell 脚本介绍
#!/bin/bash
"#!"
是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行。
shell
脚本并不能作为正式的编程语言,因为它是在Linux
的shell
中运行的,所以称为shell
脚本。
shell
是一种脚本语言;- 可以使用逻辑判断、循环等语法;
- 可以自定义函数;
shell
是系统命令的集合;shell
脚本可以实现自动化运维,能大大增加我们的运维效率;
shell 变量
变量类型
运行 shell 时,会同时存在三种变量:
- 局部变量
局部变量在脚本或命令中定义,仅在当前 shell 实例中有效,其他 shell 启动的程序不能访问局部变量。 - 环境变量
所有的程序,包括 shell 启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候 shell 脚本也可以定义环境变量。 - shell 变量
shell 变量是由 shell 程序设置的特殊变量。shell 变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了 shell 的正常运行
定义变量
Shell 支持以下三种定义变量的方式:
variable=value
variable='value'
variable="value"
variable 是变量名,value 是赋给变量的值。如果 value 不包含任何空白符(例如空格、Tab缩进等),那么可以不使用引号;如果 value 包含了空白符,那么就必须使用引号包围起来。
使用单引号和使用双引号的区别:
$ vim test.sh
#!/bin/bash
value="Let's see this'll difference"
variable1='variable1:${value}'
variable2="variable2:${value}"
echo $variable1
echo $variable2
~
~
运行结果:
$ bash test.sh
variable1:${value}
variable2:Let's see this'll difference
以单引号' '
包围变量的值时,单引号里面是什么就输出什么,即使内容中有变量和命令(命令需要反引起来)也会把它们原样输出。这种方式比较适合定义显示纯字符串的情况,即不希望解析变量、命令等的场景。
以双引号" "
包围变量的值时,输出时会先解析里面的变量和命令,而不是把双引号中的变量名和命令原样输出。这种方式比较适合字符串中附带有变量和命令并且想将其解析后再输出的变量定义。
如果变量的内容是数字,那么可以不加引号;如果真的需要原样输出就加单引号;其他没有特别要求的字符串等最好都加上双引号,定义变量时加双引号是最常见的使用场景。
Shell 变量的命名规范:
- 变量名由数字、字母、下划线组成;
- 必须以字母或者下划线开头;
- 不能使用 Shell 里的关键字(通过 help命令可以查看保留关键字)。
当前定义的所有环境变量可以通过set
命令获取。
$ set
...
HISTFILE=/root/.bash_history
HISTFILESIZE=2000
HISTSIZE=1000
HOME=/root
HOSTNAME=kubuntu
HOSTTYPE=x86_64
IFS=$' \t\n'
LANG=zh_CN.UTF-8
LANGUAGE=zh_CN:zh
LESSCLOSE='/usr/bin/lesspipe %s %s'
LESSOPEN='| /usr/bin/lesspipe %s'
LINES=57
LOGNAME=root
...
变量名外面的花括号{ }是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:
$ skill="Java"
$ echo "I am good at ${skill}Script"
I am good at JavaScript
由于在 Shell 脚本中,$
作为变量的前缀符,所以当需要在文本输出中显示$
时,应使用转义。
#$15 被当成了代入到字符串中的"变量"
$ echo "The cost of the item is $15"
The cost of the item is
#使用 \ 转义后正常打印 $ 字符
$ echo "The cost of the item is \$15"
The cost of the item is $15
使用变量
使用一个定义过的变量,只要在变量名前面加美元符号$即可
- 变量赋值:
var=value
(注意=
号两边不能有空格) - 变量使用:
$var
$ vim test.sh
#!/bin/bash
value="Let's see if this'll work"
~
~
$ echo $value
Let's see if this'll work
$ echo ${value}
Let's see if this'll work
shell替换
命令替换
命令替换是指 Shell 可以先执行命令,将输出结果暂时保存,在适当的地方输出。(将命令的结果赋值给变量)
用反引号 ``
(位于 Esc 键的下方)包围起来:
$ vim test.sh
#!/bin/bash
DATE=`date`
USERS=`who | wc -l`
UP=`date ; uptime`
echo "Date is $DATE"
echo "Logged in user are $USERS"
echo "Uptime is $UP" '>test.sh
~
~
运行结果:
$ bash test.sh
Date is 2020年 11月 22日 星期二 15:51:41 CST
Logged in user are 1
Uptime is 2020年 11月 22日 星期二 15:51:41 CST
15:51:41 up 56 min, 1 user, load average: 0.00, 0.00, 0.00
变量替换
变量替换可以根据变量的状态(是否为空、是否定义等)来改变它的值
可以使用的变量替换形式:
形式 | 说明 |
---|---|
${var} | 变量本来的值 |
${var:-word} | 如果变量 var 为空或已被删除(unset),那么返回 word,但不改变 var 的值 |
${var:=word} | 如果变量 var 为空或已被删除(unset),那么返回 word,并将 var 的值设置为 word。 |
${var:?message} | 如果变量 var 为空或已被删除(unset),那么将消息( message )送到标准错误输出,可以用来检测变量 var 是否可以被正常赋值。若此替换出现在 Shell 脚本中,那么脚本将停止运行。 |
${var:+word} | 如果变量 var 被定义,那么返回 word,但不改变 var 的值。 |
例:
$ vim test.sh
#!/bin/bash
echo ${var:-"Variable is not set"}
echo "1 - Value of var is ${var}"
echo ${var:="Variable is not set"}
echo "2 - Value of var is ${var}"
unset var
echo ${var:+"This is default value"}
echo "3 - Value of var is $var"
var="Prefix"
echo ${var:+"This is default value"}
echo "4 - Value of var is $var"
echo ${var:?"Print this message"}
echo "5 - Value of var is ${var}"
运行结果:
$ bash test.sh
Variable is not set
1 - Value of var is
Variable is not set
2 - Value of var is Variable is not set
3 - Value of var is
This is default value
4 - Value of var is Prefix
Prefix
5 - Value of var is Prefix
shell函数
shell函数定义
Shell 函数定义的语法格式如下:
function name() {
statements
[return value]
}
对各个部分的说明:
- function是 Shell 中的关键字,专门用来定义函数;
- name是函数名;
- statements是函数要执行的代码,也就是一组语句;
- return value表示函数的返回值,其中 return 是 Shell 关键字,专门用在函数中返回一个值;这一部分可以写也可以不写。
由{ }包围的部分称为函数体,调用一个函数,实际上就是执行函数体中的代码。
函数定义的简化写法
如果你嫌麻烦,函数定义时也可以不写 function 关键字:
name() {
statements
[return value]
}
如果写了 function 关键字,也可以省略函数名后面的小括号:
function name {
statements
[return value]
}
Shell特殊变量
变量 | 含义 |
---|---|
$0 | 当前脚本的文件名。 |
$n(n≥1) | 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是 $1,第二个参数是 $2。 |
$# | 传递给脚本或函数的参数个数。 |
$* | 传递给脚本或函数的所有参数。 |
$@ | 传递给脚本或函数的所有参数。当被双引号" "包含时,$@ 与 $* 稍有不同。 |
$? | 上个命令的退出状态,或函数的返回值。 |
$$ | 当前 Shell 进程 ID。对于 Shell 脚本,就是这些脚本所在的进程 ID。 |
$*
和 $@
的区别
$*
和 $@
都表示传递给函数或脚本的所有参数,不被双引号(" ")
包含时,都以"$1" "$2" … "$n"
的形式输出所有参数。
但是当它们被双引号(" ")
包含时,"$*"
会将所有的参数作为一个整体,以"$1 $2 … $n"
的形式输出所有参数;"$@"
会将各个参数分开,以"$1" "$2" … "$n"
的形式输出所有参数。
退出状态
$?
可以获取上一个命令的退出状态。所谓退出状态,就是上一个命令执行后的返回结果。
退出状态是一个数字,一般情况下,大部分命令执行成功会返回0
,失败返回1
。
不过,也有一些命令返回其他值,表示不同类型的错误。
shell函数调用
给脚本文件传递参数
$ vim test.sh
#!/bin/bash
echo "Process ID: $$"
echo "File Name: $0"
echo "First Parameter : $1"
echo "Second Parameter : $2"
echo "All parameters 1: $@"
echo "All parameters 2: $*"
echo "Total: $#"
~
~
运行结果为:
$ bash test.sh
$ bash test.sh
Process ID: 2355
File Name: test.sh
First Parameter :
Second Parameter :
All parameters 1:
All parameters 2:
Total: 0
给函数传递参数
编辑test.sh
文件
#!/bin/bash
#定义函数
function func(){
echo "Language: $1"
echo "URL: $2"
echo "First Parameter : $1"
echo "Second Parameter : $2"
echo "All parameters 1: $@"
echo "All parameters 2: $*"
echo "Total: $#"
}
#调用函数
func Java http://c.biancheng.net/java/
运行 test.sh
,并附带参数:
Language: Java
URL: http://c.biancheng.net/java/
First Parameter : Java
Second Parameter : http://c.biancheng.net/java/
All parameters 1: Java http://c.biancheng.net/java/
All parameters 2: Java http://c.biancheng.net/java/
Total: 2
Shell字符串
字符串是 shell
编程中最常用最有用的数据类型,字符串可以用单引号,也可以用双引号,也可以不用引号。
单引号
str='this is a string'
单引号字符串的限制:
- 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
- 单引号字串中不能出现单引号(对单引号使用转义符后也不行)
双引号
your_name='qj'
str="Hello, I know your are \"$your_name\"! \n"
- 双引号里可以有变量
- 双引号里可以出现转义字符
字符串操作
拼接字符串
your_name="qj"
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1
hello, qj ! hello, qj !
获取字符串长度
string="abcd"
echo ${#string} #输出4
提取子字符串
string="alibaba is a great company"
echo ${string:1:4} #输出 liba
查找子字符串
string="alibaba is a great company"
echo `expr index "$string" y` #输出26
Shell 数组
数组分类
- 普通数组:只能使用整数作为数组索引(元素的下标)
- 关联数组:可以使用字符串为数组索引(元素的下标)
普通数组定义
- 一次赋予一个值
数组名[索引下标]=值
例:
array_name[0]=value1
array_name[1]=value2
array_name[2]=value3
- 一次赋予多个值
数组名(值1 值2 值3...)
例:
array_name=(value1 value2 value3)
或者
array_name=(
value1
value2
value3
)
或者是命令
array1=(`` `cat /etc/passwd` ``)
array2=(`` `ls /root` ``)
读取数组
读取数组元素值的一般格式是:
${数组名[元素下标]}
例如:
echo ${array_name[0]} 获取数组里第一个元素
echo ${array_name[*]} 获取数组里所有元素
echo ${#array_name[*]} 获取数组里所有元素个数
echo ${!array_name[@]} 获取数组元素的索引下标
echo ${array_name[@]:1:2} 访问指定元素;1代表从下标为1的元素开始获取;2代表取后面几个元素
获取数组的长度
获取数组长度的方法与获取字符串长度的方法相同,例如:
#取得数组元素的个数
length=${#array_name[@]}
#或者
length=${#array_name[*]}
#取得数组单个元素的长度
lengthn=${#array_name[n]}
Shell echo 命令
echo
是 Shell
的一个内部指令,用于在屏幕上打印出指定的字符串。
命令格式:
echo test
可以使用 echo
实现复杂的输出格式控制。
显示转义字符
echo "\"It is a test\""
输出结果:
"It is a test"
- 双引号可以省略
显示变量
name="ok"
echo "$name It is a test"
输出结果:
OK It is a test
- 双引号可以省略
如果变量与其它字符相连的话,需要使用大括号({ }
):
mouth=8
echo "${mouth}-1-1927"
输出结果:
8-1-1927
显示换行
echo "OK!\n"
echo "It is a test"
输出:
OK!
It is a test
显示不换行
echo"OK!\c"
echo "It is a test"
输出:
OK!It si a test
显示结果重定向至文件
echo "It is a test" > myfile
原样输出字符串
若需要原样输出字符串(不进行转义),请使用单引号。例如:
echo '$name\"'
输出:
echo $name\"
显示命令执行结果
echo `date`
输出结果:#显示当前日期
1949年 10月 1日 星期六 14:00:59 CST
Shell printf 命令
:格式化输出语句
printf
命令模仿 C
程序库(library)
里的 printf()
程序。
printf
由 POSIX
标准所定义,因此使用 printf
的脚本比使用 echo
移植性好。
printf
使用引用文本或空格分隔的参数,外面可以在 printf
中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。
printf
命令的语法:
printf format-string [arguments...]
默认 printf
不像 echo
那样会自动换行,必须显式添加换行符(\n)
。
printf "Hello, Shell\n"
Hello, Shell
参数说明:
- format-string: 为格式控制字符串。
- arguments: 为参数列表。
例:
# format-string 为双引号
$ printf "%d %s\n" 1 "abc"
1 abc
# 单引号与双引号效果一样
$ printf '%d %s\n' 1 "abc"
1 abc
# 没有引号也可以输出
$ printf %s abcdef
abcdef
# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用
$ printf %s abc def
abcdef
$ printf "%s\n" abc def
abc
def
$ printf "%s %s %s\n" a b c d e f g h i j
a b c
d e f
g h i
j
# 如果没有 arguments,那么 %s 用 NULL 代替,%d 用0代替
$ printf "%s and %d \n"
and 0
# 如果以 %d 的格式来显示字符串,那么会有警告,提示无效的数字,此时默认置为0
$ printf "The first program always prints'%s,%d\n'" Hello Shell
-bash: printf: Shell: invalid number
The first program always prints 'Hello,0'
$
$ vim test.sh
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
printf "%-10s %-8s %-4.2f\n" 张三 男 66.1234
printf "%-10s %-8s %-4.2f\n" 李四 男 48.6543
printf "%-10s %-8s %-4.2f\n" 二丫 女 47.9876
执行脚本,输出结果如下:
$ bash test.sh
姓名 性别 体重kg
张三 男 66.12
李四 男 48.65
二丫 女 47.99
%s %c %d %f
都是格式替代符
%-10s
指一个宽度为10
个字符(-
表示左对齐,没有则表示右对齐),任何字符都会被显示在10
个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
%-4.2f
指格式化为小数,其中.2
指保留2
位小数。
Shell test 命令
test < 表达式 >
Shell
中的 test
命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。
Shell 基本运算符
四则运算符号
算数运算:默认情况下,shell
就只支持简单的整数运算
运算内容:加( + )
、减(--)
、乘( * )
、除( / )
、求余数( % )
表达式 | 举例 |
---|---|
$(( )) | echo $(( 1 + 1 )) |
$[ ] | echo$[ 10 - 5 ] |
expr | expr 10 / 5 |
let | n=1;let n+1 等价于 let n=n+1 |
类C风格的数值比较
在(( ))中,=表示赋值 ==表示判断
判断参数 | 含义 |
---|---|
== 相等 | ((1==2)) |
!= 不相等 | ((1!=2)) |
>= 大于等于 | ((1>=2)) |
<= 小于等于 | ((1<=2)) |
字符串比较
双引号引起来,看作一个整体;=和==在[字符串]比较中都表示判断
判断参数 | 含义 |
---|---|
= 相等 | ((1=2)) |
== 相等 | ((1==2)) |
!= 不相等 | ((1!=2)) |
!== 相等 | ((1!==2)) |
>= 大于等于 | ((1>=2)) |
<= 小于等于 | ((1<=2)) |
整数判断
判断整数
判断参数 | 含义 |
---|---|
-eq | 相等 |
-ne | 不等 |
-gt | 大于 |
-lt | 小于 |
-ge | 大于等于 |
-le | 小于等于 |
多重条件判断
运算符 | 说明 |
---|---|
-a 和 && | 逻辑与,两个条件都为真 表达式才为真。[1 -eq 1 -a 1 -ne 0] [1 -eq 1 ]&& [1 -ne 0] |
-o 和 || | 逻辑或,只要有一个条件为真 表达式就为真。 [1 -eq 1 -o 1 -ne 1] |
! | 非运算,表达式为 true 则返回 false,否则返回 true。 |
; | 用于分割命令或表达式。 |
&&
前面的表达式为真,才会执行后面的代码
||
前面的表达式为假,才会执行后面的代码
字符串判断
运算符 | 说明 |
---|---|
-z | 判断是否为空字符串,字符串长度为0则成立。 |
-n | 判断是否为非空字符串,字符串长度为0则成立。 |
string1 = string2 | 判断字符串是否相等。 |
string1 != string2 | 判断字符串是否相不等。 |
文件判断
判断文件类型
判断参数 | 含义 |
---|---|
-e file | 检测文件是否存在(任何类型),如果是,则返回 true。 |
-f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 |
-d file | 检测文件是否是目录,如果是,则返回 true。 |
-L file | 检测文件是否存在并且是一个符号链接。 |
-b file | 检测文件是否是块设备文件,如果是,则返回 true。 |
-S file | 判断某文件是否是一个套接子文件。 |
-c file | 检测文件是否是字符设备文件,如果是,则返回 true。 |
-p file | 检测文件是否是有名管道,如果是,则返回 true。 |
-s file | 检测文件是否为空(文件大小是否大于0),不为空返回 true。 |
判断文件权限
判断参数 | 含义 |
---|---|
-r file | 检测文件当前用户对其是否可读 |
-w file | 检测文件当前用户对其是否可写 |
-x file | 检测文件当前用户是否可执行 |
-u file | 检测文件是否设置了 SUID 位,高级权限冒险位。 |
-g file | 检测文件是否设置了 SGID 位,高级权限强制位。 |
-k file | 检测文件是否设置了t位,高级权限粘滞位。 |
Shell if else 语句
if
语句通过关系运算符判断表达式的真假来决定要执行的分支。Shell
有三种 if ... else
语句:
if ... fi
语句;if ... else ... fi
语句;if ... elif ... else ... fi
语句。
if … else 语句
if ... else
语句的语法:
单分支
if 判断条件;then
条件为真的分支代码
fi
如果 expression
返回 true
,then
后边的语句将会被执行;如果返回 false
,不会执行任何语句。
以 fi
来结尾闭合 if
。
注意:expression
(表达式)和方括号([ ]
)之间必须有空格,否则会有语法错误。
例:
#!/bin/sh
a=10
b=20
if [ $a == $b ]
then
echo "a is equal to b"
fi
if [ $a != $b ]
then
echo "a is not equal to b"
fi
运行结果:
a is not equal to b
if … else … fi 语句
if ... else ... fi
语句的语法:
双分支
if 判断条件; then
条件为真的分支代码
else
条件为假的分支代码
fi
如果 expression
返回 true
,那么 then
后边的语句将会被执行;否则,执行 else
后边的语句。
例:
#!/bin/sh
a=10
b=20
if [ $a == $b ]
then
echo "a is equal to b"
else
echo "a is not equal to b"
fi
执行结果:
a is not equal to b
if … elif … fi 语句
if ... elif ... fi
语句可以对多个条件进行判断,语法为:
多分支
if 判断条件 1 ; then
条件为真的分支代码
elif 判断条件 2 ; then
条件为真的分支代码
elif 判断条件 3 ; then
条件为真的分支代码
else
以上条件都为假的分支代码
fi
逐条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if 语句
expression
的值为 true
,就执行 expression
后面的语句;如果都为 false
,那么不执行任何语句。
例:
#!/bin/sh
a=10
b=20
if [ $a == $b ]
then
echo "a is equal to b"
elif [ $a -gt $b ]
then
echo "a is greater than b"
elif [ $a -lt $b ]
then
echo "a is less than b"
else
echo "None of the condition met"
fi
运行结果:
a is less than b
Shell case esac 语句
case ... esac
与其他语言中的 switch ... case
语句类似,是一种多分枝选择结构。
case
语句匹配一个值或一个模式,如果匹配成功,执行相匹配的命令。
case
语句格式如下:
case 值 in
模式1)
command1
command2
command3
;;
模式2)
command1
command2
command3
;;
*)
command1
command2
command3
;;
esac
case
工作方式:取值后面必须为关键字 in
,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;
。;;
与其他语言中的 break 类似,意思是跳到整个 case
语句的最后。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 *
捕获该值,再执行后面的命令。
下面的脚本提示输入1
到4
,与每一种模式进行匹配:
echo 'Input a number between 1 to 4'
echo 'Your number is:\c'
read aNum
case $aNum in
1) echo 'You select 1'
;;
2) echo 'You select 2'
;;
3) echo 'You select 3'
;;
4) echo 'You select 4'
;;
*) echo 'You do not select a number between 1 to 4'
;;
esac
输入不同内容,会输出不同的结果。
例:
Input a number between 1 to 4
Your number is:3
You select 3
例2:
#!/bin/bash
option="${1}"
case ${option} in
-f) FILE="${2}"
echo "File name is $FILE"
;;
-d) DIR="${2}"
echo "Dir name is $DIR"
;;
*)
echo "`basename ${0}`:usage: [-f file] | [-d directory]"
exit 1 # Command to come out of the program with status 1
;;
esac
运行结果:
$ bash test.sh
test.sh: usage: [ -f file ] | [ -d directory ]
$ bash test.sh -f index.htm
File name is index.htm
$ bash test.sh -d unix
Dir name is unix
$
Shell for 循环
与其他编程语言类似,Shell
支持 for
循环。
for
循环一般格式为:
for 变量 in 列表
do
command1
command2
...
commandN
done
列表是一组值(数字、字符串等)组成的序列,每个值通过空格分隔。每循环一次,会将列表中的下一个值赋给变量。
in
列表是可选的,如果不用它,for
循环使用命令行的位置参数。
例,顺序输出当前列表中的数字:
for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done
运行结果:
The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5
顺序输出字符串中的字符:
for str in 'This is a string'
do
echo $str
done
运行结果:
This is a string
显示主目录下以 .bash 开头的文件:
#!/bin/bash
for FILE in $HOME/.bash*
do
echo $FILE
done
运行结果:
/root/.bash_history
/root/.bash_logout
/root/.bash_profile
/root/.bashrc
Shell while 循环
while
循环用于不断执行一系列命令,也用于从输入文件中读取数据;命令通常为测试条件。其格式为:
while command
do
Statement(s) to be executed if command is true
done
命令执行完毕,控制返回循环顶部,从头开始直至测试条件为假。
while
循环
测试:如果 COUNTER
小于5
,那么返回 true
。COUNTER
从0
开始,每次循环处理时,COUNTER
加1
。运行上述脚本,返回数字1到5,然后终止。
COUNTER=0
while [ $COUNTER -lt 5 ]
do
COUNTER='expr $COUNTER+1'
echo $COUNTER
done
运行脚本,输出:
1
2
3
4
5
while
循环可用于读取键盘信息。
例:
输入信息被设置为变量 FILM
,结束循环。
echo 'type <CTRL-D> to terminate'
echo -n 'enter your most liked film:'
while read FILM
do
echo "Yeah! great film the $FILM"
done
运行脚本,输出结果:
type <CTRL-D> to terminate
enter your most liked film: Sound of Music
Yeah! great film the Sound of Music
Shell until 循环
until
循环执行一系列命令直至条件为 true
时停止。until
循环与 while
循环在处理方式上刚好相反。一般 while
循环优于 until
循环,但在某些时候,也只是极少数情况下,until
循环更加有用。
until
循环格式为:
until command
do
Statement(s) to be executed until command is true
done
command
一般为条件表达式,如果返回值为 false
,则继续执行循环体内的语句,否则跳出循环。
例:使用 until
命令输出0
~
9
的数字:
#!/bin/bash
a=0
until [ ! $a -lt 10 ]
do
echo $a
a=`expr $a + 1`
done
运行结果:
0
1
2
3
4
5
6
7
8
9
shell跳出循环
Shell break 和 continue 命令
在循环过程中,当需要在未达到循环结束条件时强制跳出循环,Shell
可以使用 break
和 continue
来跳出循环。
break 命令
break
命令允许跳出所有循环(终止执行后面的所有循环)。
例:
Shell
脚本进入死循环直至用户输入数字大于5
。强制跳出循环,要使用 break
命令返回到 shell
提示符下。
#!/bin/bash
while :
do
echo -n "Input a number between 1 to 5: "
read aNum
case $aNum in
1|2|3|4|5) echo "Your number is $aNum!"
;;
*) echo "You do not select a number between 1 to 5, game is over!"
break
;;
esac
done
在嵌套循环中,break
命令后面可以跟一个整数,表示跳出第几层循环。 break
n
表示跳出第 n
层循环。
嵌套循环,例:如果 var1
等于2
,并且 var2
等于0
,跳出循环。
#!/bin/bash
for var1 in 1 2 3
do
for var2 in 0 5
do
if [ $var1 -eq 2 -a $var2 -eq 0 ]
then
break 2
else
echo "$var1 $var2"
fi
done
done
break
2
表示直接跳出外层循环。运行结果:
1 0
1 5
continue 命令
continue
命令与 break
命令类似,continue
不会跳出所有循环,仅跳出当前循环。
例:
#!/bin/bash
while :
do
echo -n "Input a number between 1 to 5: "
read aNum
case $aNum in
1|2|3|4|5) echo "Your number is $aNum!"
;;
*) echo "You do not select a number between 1 to 5!"
continue
echo "Game is over!"
;;
esac
done
当输入大于5的数字时,该循环不会结束,语句 echo "Game is over!"
不会被执行。
continue
后面可以跟一个数字,表示跳出第几层循环。 continue
n
表示跳出第 n
层循环。
例2:
#!/bin/bash
NUMS="1 2 3 4 5 6 7"
for NUM in $NUMS
do
Q=`expr $NUM % 2`
if [ $Q -eq 0 ]
then
echo "Number is an even number!!"
continue
fi
echo "Found odd number"
done
运行结果:
Found odd number
Number is an even number!!
Found odd number
Number is an even number!!
Found odd number
Number is an even number!!
Found odd number
Shell 输入输出重定向
Linux 中,标准输入设备指的是键盘,标准输出设备指的是显示器。
Linux 中一切皆文件,包括标准输入设备(键盘)和标准输出设备(显示器)在内的所有计算机硬件都是文件。
输入与输出的文件描述符
文件描述符 | 文件名 | 类型 | 硬件 |
---|---|---|---|
0 | stdin | 标准输入文件 | 键盘 |
1 | stdout | 标准输出文件 | 显示器 |
2 | stderr | 标准错误输出文件 | 显示器 |
输出重定向
命令的输出不仅可以是显示器,也可以输出为文件,称为输出重定向。
Bash 支持的输出重定向符号
类 型 | 符 号 | 作 用 | |
---|---|---|---|
标准输出重定向 | command >file | 以覆盖的方式,把 command 的正确输出结果输出到 file 文件中。 | |
command >>file | 以追加的方式,把 command 的正确输出结果输出到 file 文件中。 | ||
标准错误输出重定向 | command 2>file | 以覆盖的方式,把 command 的错误信息输出到 file 文件中。 | |
command 2>>file | 以追加的方式,把 command 的错误信息输出到 file 文件中。 | ||
正确输出和错误信息同时保存 | command >file 2>&1 | 以覆盖的方式,把正确输出和错误信息同时保存到同一个文件(file)中。 | |
command >>file 2>&1/td> | 以追加的方式,把正确输出和错误信息同时保存到同一个文件(file)中。 | ||
command >file1 2>file2 | 以覆盖的方式,把正确的输出结果输出到 file1 文件中,把错误信息输出到 file2 文件中。 | ||
command >>file1 2>>file2 | 以追加的方式,把正确的输出结果输出到 file1 文件中,把错误信息输出到 file2 文件中。 | ||
command >file 2>file | 【不推荐】这两种写法会导致 file 被打开两次,引起资源竞争,所以 stdout 和 stderr 会互相覆盖。 | ||
command >>file 2>>file |
在输出重定向中,>代表的是覆盖,>>代表的是追加。
输出重定向命令语法:
$ command > file
输出到显示器的内容会被重定向到文件。
例:命令输出在不会显示器上显示。
$ ls -a >testfile
查看 testfile
文件内容:
$ cat testfile
.bash_history
.bashrc
.cache
.local
.profile
.ssh
.vim
$
输出重定向会覆盖文件内容
例:
$ echo line 1 > testfile
$ cat users
line 1
$
可以使用 >>
追加到文件末尾,不覆盖文件内容。
例:
$ echo line 2 >> testfile
$ cat testfile
line 1
line 2
$
输入重定向
输入重定向就是改变输入的方向,不再使用键盘作为命令输入的来源,而是使用文件作为命令的输入。
Bash 支持的输出重定向符号
符号 | 说明 |
---|---|
command <file | 将 file 文件中的内容作为 command 的输入。 |
command <<END | 从标准输入(键盘)中读取数据,直到遇见分界符 END 才停止(分界符可以是任意的字符串,用户自己定义)。 |
command file2 | 将 file1 作为 command 的输入,并将 command 的处理结果输出到 file2。 |
输入重定向完整写法为fd<file, fd 表示文件描述符,如不指定,则默认为 0,表示标准输入文件。
【示例】统计文档行数。
Linux wc 命令可以用来对文本进行统计,包括单词个数、行数、字节数,用法如下:
wc [选项] [文件名]
-c
选项统计字节数,-w
选项统计单词数,-l
选项统计行数。
$ echo '12345
> abcde '>tests
$ cat tests
12345
abcde
$
$ wc -l tests
2 tests
$
或输入重定向到 tests
文件:
$ wc -l < tests
2
$
wc -l tests
会输出文件名;wc -l < tests
不会输出文件名,仅从标准输入读取内容。
重定向命令列表
命令 | 说明 |
---|---|
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 之间的内容作为输入。 |
/dev/null 文件
将输出重定向到 /dev/null
,只执行命令,不在屏幕上显示输出结果:
$ command > /dev/null
/dev/null
是一个特殊的文件,写入的内容都会被丢弃。将命令的输出重定向/dev/null
,内容会"禁止输出"。
屏蔽 stdout 和 stderr:
$ command > /dev/null 2>&1file
Shell 文件包含
Shell 可以包含外部脚本,将外部脚本的内容合并到当前脚本。
可以使用:
. filename
或
source filename
一般常用点号(.
),点号(.
)和文件名中间有空格。
创建两个脚本,subscript.sh
作为被调用脚本 。
例:
url="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
主文件 main.sh
,内容:
#!/bin/bash
. ./subscript.sh
echo $url
执行脚本:
$ bash main.sh
http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4
$
sed文本编辑器
sed常见语法格式有二,一命令行模式,二脚本模式。
语法格式
sed [options] '处理动作' 文件名
常用选项
选项 | 说明 |
---|---|
- e | 进行多项(多次)编辑 |
- n | 取消默认输出 |
- r | 使用扩展正则表达式 |
- i | 原地编辑(修改源文件) |
- f | 指定sed脚本的文件名 |
常见处理动作
选项 | 说明 |
---|---|
'p' | 打印 |
'i' | 在指定指定行之前插入内容 |
'a' | 在指定指定行之后插入内容 |
'c' | 替换指定行所有内容 |
'd' | 删除指定行 |
对文件进行增、删、改、查
操作
语法:sed 选项 ’定位+命令‘ 需要处理的文件
打印文件内容
[root@server ~] # sed '' a.txt 对文件什么都不做
[root@server ~] # sed - n 'p' a.txt 打印每一行,取消默认输出
[root@server ~] # sed - n '1p' a.txt 打印第一行
[root@server ~] # sed - n '2p' a.txt 打印第二行
[root@server ~] # sed - n '1,5p' a.txt 打印1到5行
[root@server ~] # sed - n '$p' a.txt 打印最后一行
增加文件内容
[root@server ~] # sed '$a99999' a.txt 文件最后一行下增加内容
[root@server ~] # sed 'a99999' a.txt 文件每行下增加内容
[root@server ~] # sed '5a99999' a.txt 文件第5行下增加内容
[root@server ~] # sed '$i99999' a.txt 文件最后一行上一行增加内容
[root@server ~] # sed 'i99999' a.txt 文件每行上一行增加内容
[root@server ~] # sed '6i99999' a.txt 文件第6行上一行增加内容
[root@server ~] # sed '/^uucp/i99999' a.txt 以ccup开头的上一行增加内容
修改文件内容
[root@server ~] # sed '5chello' a.txt 替换第5行内容
[root@server ~] # sed 'chello' a.txt 替换文件所有内容
[root@server ~] # sed '1,5chello' a.txt 替换文件1到5行内容,为hello
[root@server ~] # sed '/^uucp/c99999' a.txt 替换以uucp开头的行
删除文件内容
[root@server ~] # sed '1d' a.txt 删除文件第1行
[root@server ~] # sed 1,5d' a.txt 删除文件1到5行
[root@server ~] # sed '$d' a.txt 删除文件最后一行
对文件进行搜索替换操作
语法: sed 选项 's/搜索内容/替换内容/动作' 需要处理的文件
s
表示search
搜索;/
表示分隔符,可以自定义;p
打印和g
全局替换
[root@server ~] # sed -n 's/root/ROOT/p' a.txt
[root@server ~] # sed -n '1,5s/^/#/p' a.txt 注释掉文件1到5行内容
sed结合正则使用
sed 选项 'sed命令 或者 正则表达式 或者 地址定位' 文件名
1.定址用于决定对哪些进行编辑。地址形式可以是数字、正则表达式、或者二者结合。
2.如没有指定地址,sed将处理输入文件的所有行。
正则 | 说明 | 备注 |
---|---|---|
/key/ | 查询包含关键字的行 | sed -n '/root/p' 1.txt |
/key1/,/key2/ | 匹配包含两个关键字之间的行 | sed -n '/^adm/,/^mysql/p' 1.txt |
/key/,x | 从匹配关键字的行开始到文件第x行之间的行(包含关键字所在行) | sed -n '/^ftp/,7p' |
x,/key/ | 从第x行开始到与关键字的匹配行之间的行 | |
x,y! | 不包含x到y行 | |
/key/! | 不包含关键字的行 | sed -n '/bash$/!p' 1.txt |
脚本编写
#!/bin/sed -f
1,5d
s /root/hello/g
3i666
5i777
a
p
使用
sed -f 脚本 file
./sed.sh file
awk
- 一种编程语言,主要用于在linux/unix下对文本和数据进项处理,是linux/unix下的一个工具。要处理的数据可以来自标准输入,一个或多个文件,或其它命令输出。
- awk处理文本和数据的方式是:逐行扫描文件,默认从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行你想要的操作。
- awk用于处理文件和数据
- 可以用来统计数据
- 支持条件判断,支持for和while循环
awk命令行模式使用
语法结构:
awk 选项 '命令部分' 文件名
常用选项
- -F 定义字段分割符号,默认分隔符为空格
- -V 定义变量并赋值
命令说明
- 正则表达式,地址定位
'/root/{awk语句}' 等同sed中:'/root/p'
'NR==1,NR==5 {awk语句}' 等同sed中:'/1,5/p'
'/^root/,/^ftp/{awk 语句你}' 等同sed中:'/^root/,/^ftp/p'
- {awk语句1;awk语句2;…}
'{print $0;print $1}' 等同于sed:'p'
'NR==5{print $0}' 等同于sed:'5p'
- BEGIN…END…
'BEGIN {awk语句} ; {处理中} ;END {awk语句}'
'BEGIN {awk语句} ; {处理中}'
'{处理中} ;END {awk语句}'
脚本使用
#!/bin/awk -f
BEGIN {FS=':'}
NR==1,NR==3{print $1''\t''$NF}
…
执行
awk 选项 -f awk的脚本文件 要处理的文件
awk -f awk.sh filename
或
./awk.sh filename
awk相关变量
变量 | 变量说明 | 备注 |
---|---|---|
$0 | 当前处理行的所有记录 | |
$1,$2,$3,…$n | 文件中每行以间隔符号分割的不同字段 | awk -F: '{print $1,$3}' |
NF | 当前记录的字段数(列数) | awk -F: '{print NF}' |
$NF | 最后一列 | $(NF-1)表示倒数第二列 |
FNR/NR | 行号 | |
FS | 定义间隔符 | ‘BEGIN{FS=":"};{print $1,$3}’ |
OFS | 定义输出字段分割符,默认空格 | ‘BEGIN{OFS="\t"};{print $1,$3}’ |
RS | 输入记录分割符,默认换行 | ‘BEGIN{RS="\t"};{print $0}’ |
ORS | 输出记录分割符,默认换行 | ‘BEGIN{ORS="\n\n"};{print $1,$3}’ |
FILENAME | 当前输入的文件名 |
grep命令:
命令语法:
grep [OPTIONS] PATTERN [FILE...]
grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]
- i | :忽略大小写对文件内容进行匹配。 |
---|---|
- v | :反向匹配,根据用户输入的信息取反。 |
- o | : 输入匹配的是什么,输出的就是什么,而不是显示匹配字串的一整行。 |
- E | :grep的扩展,或者使用egrep命令。 |
- - color | : 匹配到的字符显示颜色。 |
基本正则表达式:grep
grep -E 或者 egrep 的基本命令都是差不多一致的。
fgrep: 不支持使用正则表达式,如果输入*
,直接匹配文本字面意思,就只匹配文本内带*
的行。
基本正则表达式元字符:
字符匹配:
. | : 匹配任意单个 字符,包括标点符号,特殊字符。 |
---|---|
[ ] | : 匹配指定范围内的任意单个字符。 |
[^] | : 反向匹配任意单个字符。 |
针对[ ]
又有几个扩展的元字符:
[0-9] | :表示匹配所有数字 |
---|---|
[a-z] | :表示匹配所有小写字母 |
[A-Z] | :表示匹配所有大写字母 |
次数匹配:
用于实现指定前面字符
所能够出现的次数。
* | :匹配任意长度,它前面的字符长度 可以出现任意次(可以出现0次)。 |
---|---|
? | : 匹配前面字符可出现0次或1次,前面的字符可有可无 。 |
{m} | :匹配m次,它前面的字符要出现 m次 。 |
{m,n} | :匹配指定字符至少出现m次,至多n次,仍然匹配前面的字符。 |
{0,n} | :匹配前面的字符至少出现0次,至多出现n次。 |
.* | :匹配前面的任意长度任意字符 。 |
位置锚定:
^ | : 行首锚定,写在匹配字符的左侧。 |
---|---|
$ | :行尾锚定,写在匹配字符的右侧。 |
^$ | : 空白行。 |
< | : 词首,出现于单词左侧 \b。 |
> | : 词尾,出现于单词右侧 \b。 |
分组:
分组是因为被匹配的内容在文件中出现多次,而不需要多次输入同样的信息去匹配。
() | :在括号内,可以出现任何正确的正则表达式匹配。 |
---|---|
() \1 | :引用第一个左括号以及与之对应的右括号所包括的所有内容。 |
\2 | :引用第二个左括号以及与之对应的右括号所包括的所有内容,可以有\n次。 |
字符集合:
数字:[:dihit:]
小写字母:[:lower:]
大写字母:[:upper:]
标点符号:[:punct:]
空白字符:[:space:]
所有字母:[:alpha:]
所有数字:[:alnum:]
*)在引用字符合集的时候需要如[[:space:]]这样使用。
扩展正则表达式: egrep
*)元字符匹配与grep基本正则表达式匹配一致,包括位置锚定,字符合集。
次数匹配:
* | :星号在扩展正则表达式和基本正则表达式匹配一至,并且在基本正则表达式中不用加\ 进行转义。 |
---|---|
? | :匹配前面的字符可以出现任意次,包括0次。 |
+ | :匹配前面的字符至少出现1次。 |
{m} | : 匹配前面的字符至少出现m次。 |
{m,n} | :匹配前面的字符至少出现m次,至多n次。 |
分组:
()\1: :引用第一个左括号以及与之对应的右括号所包括的所有内容。
或者:
| : 用于表示匹配a或A,例如:(cat|CAT).