Shell
Shell 概述

1/bin/sh
2/bin/bash
3/usr/bin/sh
4/usr/bin/bash
5/bin/tcsh
6/bin/csh
1┌─[root@193_168_88_100] - [/bin] - [二 10月 25, 20:01]
2└─[$] <> ll | grep bash
3 bash
4 bashbug -> bashbug-64
5 bashbug-64
6 sh -> bash
1┌─[root@193_168_88_100] - [/bin] - [二 10月 25, 20:01]
2└─[$] <> echo $SHELL
3/bin/zsh
Shell 入门
脚本格式
第一个 Shell
脚本:helloworld.s
创建一个 Shell 脚本,输出 helloworld
1touch helloworld.sh
2vim helloworld.sh
在 helloworld.sh 中输入如下内容
1#!/bin/bash
2echo "helloworld"
脚本的常用执行方式
第一种
采用 bash 或 sh+脚本的相对路径或绝对路径(不用赋予脚本+x 权限) sh+脚本的相对路径
1sh ./helloworld.sh
2Helloworld
sh+脚本的绝对路径
1sh /home/atguigu/shells/helloworld.sh
2helloworld
bash+脚本的相对路径
1bash ./helloworld.sh
2Helloworld
bash+脚本的绝对路径
1bash /home/atguigu/shells/helloworld.sh
2Helloworld
第二种
采用输入脚本的绝对路径或相对路径执行脚本**(必须具有可执行权限+x)**
首先要赋予 helloworld.sh
脚本的+x 权限
执行脚本
相对路径
1./helloworld.sh
2Helloworld
绝对路径
1/home/atguigu/shells/helloworld.sh
2Helloworld
WARNING
注意:第一种执行方法,本质是 bash 解析器帮你执行脚本,所以脚本本身不需要执行 权限。第二种执行方法,本质是脚本需要自己执行,所以需要执行权限。
第三种
在脚本的路径前加上“.”或者 sourc
有以下脚本
1[atguigu@hadoop101 shells]$ cat test.sh
2#!/bin/bash
3A=5
4echo $A
分别使用 sh,bash,./ 和 . 的方式来执行,结果如下:
1[atguigu@hadoop101 shells]$ bash test.sh
2[atguigu@hadoop101 shells]$ echo $A
35
4[atguigu@hadoop101 shells]$ sh test.sh
5[atguigu@hadoop101 shells]$ echo $A
65
7[atguigu@hadoop101 shells]$ ./test.sh
8[atguigu@hadoop101 shells]$ echo $A
95
10[atguigu@hadoop101 shells]$ . test.sh
11[atguigu@hadoop101 shells]$ echo $A
125
原因:
- 前两种方式都是在当前
shell
中打开一个子 shell
来执行脚本内容,当脚本内容结束,则子 shell
关闭,回到父 shell
中。
- 第三种,也就是使用在脚本路径前加“.”或者
source
的方式,**可以使脚本内容在当前shell
里执行,而无需打开子 shell!
**这也是为什么我们每次要修改完/etc/profile
文件以后,需要 source
一下的原因。
- 开子
shell
与不开子 shell
的区别就在于,环境变量的继承关系,如在子 shell
中设置的当前变量,父 shell
是不可见的
变量
系统预定义变量
常用系统变量 $HOME、$PWD、$SHELL、$USER...
1[atguigu@hadoop101 shells]$ echo $HOME
2/home/atguigu
1[atguigu@hadoop101 shells]$ set
2BASH=/bin/bash
3BASH_ALIASES=()
4BASH_ARGC=()
5BASH_ARGV=()
自定义变量
基本语法
- 定义变量:变量名=变量值,注意,=号前后不能有空格
- 撤销变量:unset 变量名
- 声明静态变量:readonly 变量,注意:不能 unset
变量定义规则
- 变量名称可以由字母、数字和下划线组成,但是不能以数字开头,环境变量名建议大写。
- 等号两侧不能有空格
- 在 bash 中,变量默认类型都是字符串类型,无法直接进行数值运算。
- 变量的值如果有空格,需要使用双引号或单引号括起来
案例实操
1[atguigu@hadoop101 shells]$ A=5
2[atguigu@hadoop101 shells]$ echo $A
35
1[atguigu@hadoop101 shells]$ A=8
2[atguigu@hadoop101 shells]$ echo $A
38
1[atguigu@hadoop101 shells]$ unset A
2[atguigu@hadoop101 shells]$ echo $A
1[atguigu@hadoop101 shells]$ readonly B=2
2[atguigu@hadoop101 shells]$ echo $B
32
4[atguigu@hadoop101 shells]$ B=9
5-bash: B: readonly variable
- 在 bash 中,变量默认类型都是字符串类型,无法直接进行数值运算
1[atguigu@hadoop102 ~]$ C=1+2
2[atguigu@hadoop102 ~]$ echo $C
31+2
1[atguigu@hadoop102 ~]$ D=I love banzhang
2-bash: world: command not found
3[atguigu@hadoop102 ~]$ D="I love banzhang"
4[atguigu@hadoop102 ~]$ echo $D
5I love banzhang
- 可把变量提升为全局环境变量,可供其他 Shell 程序使用,export 变量名
1[atguigu@hadoop101 shells]$ vim helloworld.sh
2
3# 在 helloworld.sh 文件中增加 echo $B
4
5#!/bin/bash
6echo "helloworld"
7echo $B
1[atguigu@hadoop101 shells]$ ./helloworld.sh
2Helloworld
发现并没有打印输出变量 B 的值。
1[atguigu@hadoop101 shells]$ export B
2[atguigu@hadoop101 shells]$ ./helloworld.sh
3helloworld
42
TIP
在子 Shell
中声明或者改变全局的变量都不会影响父 Shell
中的变量
字符串
字符串是 shell 编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。
单引号
单引号字符串的限制:
- 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
- 单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。
双引号
1your_name="runoob"
2str="Hello, I know you are \"$your_name\"! \n"
3echo -e $str
双引号的优点:
数组
bash
支持一维数组**(不支持多维数组),并且没有限定数组的大小**。
类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。
定义数组
在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:
例如:
1array_name=(value0 value1 value2 value3)
或者
1array_name=(
2value0
3value1
4value2
5value3
6)
还可以单独定义数组的各个分量:
1array_name[0]=value0
2array_name[1]=value1
3array_name[n]=valuen
可以不使用连续的下标,而且下标的范围没有限制。
读取数组
读取数组元素值的一般格式是:
例如:
使用 @ 符号可以获取数组中的所有元素,例如:
获取数组的长度
获取数组长度的方法与获取字符串长度的方法相同,例如:
1*# 取得数组元素的个数*
2length=${#array_name[@]}
3*# 或者*
4length=${#array_name[*]}
5*# 取得数组单个元素的长度*
6lengthn=${#array_name[n]}
特殊变量
$n
功能描述:n 为数字,$0 代表该脚本名称,$1-$9
代表第一到第九个参数,十以上的参数,十以上的参数需要用大括号包含,如${10}
1[atguigu@hadoop101 shells]$ touch parameter.sh
2[atguigu@hadoop101 shells]$ vim parameter.sh
3#!/bin/bash
4echo '==========$n=========='
5echo $0
6echo $1
7echo $2
8[atguigu@hadoop101 shells]$ chmod 777 parameter.sh
9[atguigu@hadoop101 shells]$ ./parameter.sh cls xz
10==========$n==========
11./parameter.sh
12cls
13xz
$
功能描述:获取所有输入参数个数,常用于循环,判断参数的个数是否正确以及加强脚本的健壮性
1[atguigu@hadoop101 shells]$ vim parameter.sh
2#!/bin/bash
3echo '==========$n=========='
4echo $0
5echo $1
6echo $2
7echo '==========$#=========='
8echo $#
9[atguigu@hadoop101 shells]$ chmod 777 parameter.sh
10[atguigu@hadoop101 shells]$ ./parameter.sh cls xz
11==========$n==========
12./parameter.sh
13cls
14xz
15==========$#==========
162
$*、$@
$*
这个变量代表命令行中所有的参数,$*
把所有的参数看成一个整体
$@
这个变量也代表命令行中所有的参数,不过$@
把每个参数区分对待
1[atguigu@hadoop101 shells]$ vim parameter.sh
2#!/bin/bash
3echo '==========$n=========='
4echo $0
5echo $1
6echo $2
7echo '==========$#=========='
8echo $#
9echo '==========$*=========='
10echo $*
11echo '==========$@=========='
12echo $@
13[atguigu@hadoop101 shells]$ ./parameter.sh a b c d e f g
14==========$n==========
15./parameter.sh
16a
17b
18==========$#==========
197
20==========$*==========
21a b c d e f g
22==========$@==========
23a b c d e f
$?
$?
最后一次执行的命令的返回状态。如果这个变量的值为 0,证明上一 个命令正确执行;如果这个变量的值为非 0(具体是哪个数,由命令自己来决定),则证明 上一个命令执行不正确了。
判断 helloworld.sh 脚本是否正确执
1[atguigu@hadoop101 shells]$ ./helloworld.sh
2hello world
3[atguigu@hadoop101 shells]$ echo $?
40
运算符
运算符有两种写法
((表达式))
[ 表达式 ]
注意 [] 里面左右要有空格
算术运算符
下表列出了常用的算术运算符,假定变量 a 为 10,变量 b 为 20:
运算符 |
说明 |
举例 |
+ |
加法 |
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 ]。
1#!/bin/bash
2
3a=10
4b=20
5
6val=`expr $a + $b`
7echo "a + b : $val"
8
9val=`expr $a - $b`
10echo "a - b : $val"
11
12val=`expr $a \* $b`
13echo "a * b : $val"
14
15val=`expr $b / $a`
16echo "b / a : $val"
17
18val=`expr $b % $a`
19echo "b % a : $val"
20
21if [ $a == $b ]
22then
23 echo "a 等于 b"
24fi
25if [ $a != $b ]
26then
27 echo "a 不等于 b"
28fi
110 -eq 20: a 不等于 b
210 -ne 20: a 不等于 b
310 -gt 20: a 不大于 b
410 -lt 20: a 小于 b
510 -ge 20: a 小于 b
610 -le 20: a 小于或等于 b
TIP
- 乘号
(*)
前边必须加反斜杠()才能实现乘法运算;
if...then...fi
是条件语句,后续将会讲解。
- 在
MAC
中 shell
的 expr
语法是:$((表达式))
,此处表达式中的 "*"
不需要转义符号 "" 。
关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:
运算符 |
说明 |
举例 |
-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。 |
1#!/bin/bash
2
3a=10
4b=20
5
6if [ $a -eq $b ]
7then
8 echo "$a -eq $b : a 等于 b"
9else
10 echo "$a -eq $b: a 不等于 b"
11fi
12if [ $a -ne $b ]
13then
14 echo "$a -ne $b: a 不等于 b"
15else
16 echo "$a -ne $b : a 等于 b"
17fi
18if [ $a -gt $b ]
19then
20 echo "$a -gt $b: a 大于 b"
21else
22 echo "$a -gt $b: a 不大于 b"
23fi
24if [ $a -lt $b ]
25then
26 echo "$a -lt $b: a 小于 b"
27else
28 echo "$a -lt $b: a 不小于 b"
29fi
30if [ $a -ge $b ]
31then
32 echo "$a -ge $b: a 大于或等于 b"
33else
34 echo "$a -ge $b: a 小于 b"
35fi
36if [ $a -le $b ]
37then
38 echo "$a -le $b: a 小于或等于 b"
39else
40 echo "$a -le $b: a 大于 b"
41fi
110 != 20 : a 不等于 b
210 小于 100 且 20 大于 15 : 返回 true
310 小于 100 或 20 大于 100 : 返回 true
410 小于 5 或 20 大于 100 : 返回 false
TIP
这里我们使用 ((表达式))
来做判断的话。在 ((表达式))
中就可以写 <,>,<=,>=,=
这些数学的运算符号
逻辑运算符
以下介绍 Shell 的逻辑运算符,假定变量 a 为 10,变量 b 为 20:
| 运算符 | 说明 | 举例 |
| ------ | ---------- | ------------------------------------------- | --------- | -------------- | --- | ------------------------ |
| && | 逻辑的 AND | [[$a -lt 100 && $b -gt 100]] 返回 false |
| | | | 逻辑的 OR | [[$a -lt 100 | | $b -gt 100]] 返回 true |
1!/bin/bash
2
3a=10
4b=20
5
6if [[ $a -lt 100 && $b -gt 100 ]]
7then
8 echo "返回 true"
9else
10 echo "返回 false"
11fi
12
13if [[ $a -lt 100 || $b -gt 100 ]]
14then
15 echo "返回 true"
16else
17 echo "返回 false"
18fi
字符串运算符
下表列出了常用的字符串运算符,假定变量 a 为 "abc",变量 b 为 "efg":
运算符 |
说明 |
举例 |
= |
检测两个字符串是否相等,相等返回 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。 |
1#!/bin/bash
2
3a="abc"
4b="efg"
5
6if [ $a = $b ]
7then
8 echo "$a = $b : a 等于 b"
9else
10 echo "$a = $b: a 不等于 b"
11fi
12if [ $a != $b ]
13then
14 echo "$a != $b : a 不等于 b"
15else
16 echo "$a != $b: a 等于 b"
17fi
18if [ -z $a ]
19then
20 echo "-z $a : 字符串长度为 0"
21else
22 echo "-z $a : 字符串长度不为 0"
23fi
24if [ -n "$a" ]
25then
26 echo "-n $a : 字符串长度不为 0"
27else
28 echo "-n $a : 字符串长度为 0"
29fi
30if [ $a ]
31then
32 echo "$a : 字符串不为空"
33else
34 echo "$a : 字符串为空"
35fi
1abc = efg: a 不等于 b
2abc != efg : a 不等于 b
3-z abc : 字符串长度不为 0
4-n abc : 字符串长度不为 0
5abc : 字符串不为空
文件测试运算符
文件测试运算符用于检测 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。 |
其他检查符:
- -S: 判断某文件是否 socket。
- -L: 检测文件是否存在并且是一个符号链接。
1#!/bin/bash
2
3# 变量 file 表示文件 /var/www/runoob/test.sh,它的大小为 100 字节,具有 rwx 权限。下面的代码,将检测该文件的各种属性:
4file="/var/www/runoob/test.sh"
5if [ -r $file ]
6then
7 echo "文件可读"
8else
9 echo "文件不可读"
10fi
11if [ -w $file ]
12then
13 echo "文件可写"
14else
15 echo "文件不可写"
16fi
17if [ -x $file ]
18then
19 echo "文件可执行"
20else
21 echo "文件不可执行"
22fi
23if [ -f $file ]
24then
25 echo "文件为普通文件"
26else
27 echo "文件为特殊文件"
28fi
29if [ -d $file ]
30then
31 echo "文件是个目录"
32else
33 echo "文件不是个目录"
34fi
35if [ -s $file ]
36then
37 echo "文件不为空"
38else
39 echo "文件为空"
40fi
41if [ -e $file ]
42then
43 echo "文件存在"
44else
45 echo "文件不存在"
46fi
1文件可读
2文件可写
3文件可执行
4文件为普通文件
5文件不是个目录
6文件不为空
7文件存在
流程控制(重点)
if else
fi
if 语句语法格式:
1if condition
2then
3 command1
4 command2
5 ...
6 commandN
7fi
写成一行(适用于终端命令提示符):
1if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
TIP
末尾的 fi 就是 if 倒过来拼写,后面还会遇到类似的。
简单的说就是和JS
中的 if() { }
这里的 {}
代表一个区域表示if
成立了执行这个区域里面的代码,但在Shell
中 {}
有这其他的含义,所以 if
代表开始 fi
代表结束把中间的代码框起来表示if
成立后执行的代码。
其中 if 后的语句成功执行就会跳转到 then 后的语句,否则不跳转,then 表示判断成功后要执行的语句。
if else
if else 语法格式:
1if condition
2then
3 command1
4 command2
5 ...
6 commandN
7else
8 command
9fi
if else-if else
if else-if else
语法格式:
1if condition1
2then
3 command1
4elif condition2
5then
6 command2
7else
8 commandN
9fi
if else 的 [...] 判断语句中大于使用 -gt,小于使用 -lt。
1if [ "$a" -gt "$b" ]; then
2 ...
3fi
如果使用 ((...)) 作为判断语句,大于和小于可以直接使用 > 和 <。
1if (( a > b )); then
2 ...
3fi
以下实例判断两个变量是否相等:
1a=10
2b=20
3if [ $a == $b ]
4then
5 echo "a 等于 b"
6elif [ $a -gt $b ]
7then
8 echo "a 大于 b"
9elif [ $a -lt $b ]
10then
11 echo "a 小于 b"
12else
13 echo "没有符合的条件"
14fi
输出结果:
if else 语句经常与 test 命令结合使用,如下所示:
1num1=$[2*3]
2num2=$[1+5]
3if test $[num1] -eq $[num2]
4then
5 echo '两个数字相等!'
6else
7 echo '两个数字不相等!'
8fi
输出结果:
case ... esac
case ... esac
为多选择语句,与其他语言中的 switch ... case
语句类似,是一种多分支选择结构,每个 case
分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case ... esac
语句,esac
(就是 case
反过来)作为结束标记。
可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。
case ... esac
语法格式如下:
1case 值 in
2模式1)
3 command1
4 command2
5 ...
6 commandN
7 ;;
8模式2)
9 command1
10 command2
11 ...
12 commandN
13 ;;
14esac
case
工作方式如上所示,取值后面必须为单词 in,每一模式必须以右括号结束。取值可以为变量或常数,匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。
下面的脚本提示输入 1 到 4,与每一种模式进行匹配:
1echo '输入 1 到 4 之间的数字:'
2echo '你输入的数字为:'
3read aNum
4case $aNum in
5 1) echo '你选择了 1'
6 ;;
7 2) echo '你选择了 2'
8 ;;
9 3) echo '你选择了 3'
10 ;;
11 4) echo '你选择了 4'
12 ;;
13 *) echo '你没有输入 1 到 4 之间的数字'
14 ;;
15esac
输入不同的内容,会有不同的结果,例如:
1输入 1 到 4 之间的数字:
2你输入的数字为:
33
4你选择了 3
for 循环
与其他编程语言类似,Shell
支持for
循环。
写法一
for
循环一般格式为
1for var in item1 item2 ... itemN
2do
3 command1
4 command2
5 ...
6 commandN
7done
写成一行:
1for var in item1 item2 ... itemN; do command1; command2… done;
TIP
这里 do
和 done
就和 { }
一样来确定范围
当变量值在列表里,for
循环即执行一次所有命令,使用变量名获取列表中的当前取值。命令可为任何有效的 shell
命令和语句。in
列表可以包含替换、字符串和文件名。
in
列表是可选的,如果不用它,for 循环使用命令行的位置参数。
例如,顺序输出当前列表中的数字:
1for loop in 1 2 3 4 5
2do
3 echo "The value is: $loop"
4done
输出结果:
1The value is: 1
2The value is: 2
3The value is: 3
4The value is: 4
5The value is: 5
顺序输出字符串中的字符:
1#!/bin/bash
2
3for str in This is a string
4do
5 echo $str
6done
输出结果:
写法二
1for (( 初始值;循环控制条件;变量变化 ))
2do
3程序
4done
从 1 加到 100 ,for1.sh
1#!/bin/bash
2sum=0
3for((i=0;i<=100;i++))
4do
5sum=$[$sum+$i]
6done
7echo $sum
1[atguigu@hadoop101 shells]$ chmod 777 for1.sh
2[atguigu@hadoop101 shells]$ ./for1.sh
35050
while 语句
while
循环用于不断执行一系列命令,也用于从输入文件中读取数据。其语法格式为:
1while condition
2do
3 command
4done
以下是一个基本的 while
循环,测试条件是:如果 int
小于等于 5,那么条件返回真。int
从 1 开始,每次循环处理时,int
加 1。运行上述脚本,返回数字 1 到 5,然后终止。
1#!/bin/bash
2int=1
3while(( $int<=5 ))
4do
5 echo $int
6 let "int++"
7done
运行脚本,输出:
以上实例使用了 Bash let
命令,它用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量,具体可查阅:Bash let 命令 open in new window。
while
循环可用于读取键盘信息。下面的例子中,输入信息被设置为变量FILM
,按<Ctrl-D>
结束循环。
1echo '按下 <CTRL-D> 退出'
2echo -n '输入你最喜欢的网站名: '
3while read FILM
4do
5 echo "是的!$FILM 是一个好网站"
6done
无限循环
无限循环语法格式:
或者
1while true
2do
3 command
4done
或者
until 循环
until
循环执行一系列命令直至条件为 true 时停止。
until
循环与 while
循环在处理方式上刚好相反。
一般 while
循环优于 until
循环,但在某些时候—也只是极少数情况下,until
循环更加有用。
until
语法格式:
1until condition
2do
3 command
4done
condition
一般为条件表达式,如果返回值为 false
,则继续执行循环体内的语句,否则跳出循环。
以下实例我们使用 until
命令来输出 0 ~ 9
的数字:
1#!/bin/bash
2
3a=0
4
5until [ ! $a -lt 10 ]
6do
7 echo $a
8 a=`expr $a + 1`
9done
输出结果为:
跳出循环
在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell 使用两个命令来实现该功能:break 和 continue。
break 命令
break 命令允许跳出所有循环(终止执行后面的所有循环)。
下面的例子中,脚本进入死循环直至用户输入数字大于 5。要跳出这个循环,返回到 shell 提示符下,需要使用 break 命令。
1#!/bin/bash
2while :
3do
4 echo -n "输入 1 到 5 之间的数字:"
5 read aNum
6 case $aNum in
7 1|2|3|4|5) echo "你输入的数字为 $aNum!"
8 ;;
9 *) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
10 break
11 ;;
12 esac
13done
执行以上代码,输出结果为:
1输入 1 到 5 之间的数字:3
2你输入的数字为 3!
3输入 1 到 5 之间的数字:7
4你输入的数字不是 1 到 5 之间的! 游戏结束
continue
continue
命令与 break
命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。
对上面的例子进行修改:
1#!/bin/bash
2while :
3do
4 echo -n "输入 1 到 5 之间的数字: "
5 read aNum
6 case $aNum in
7 1|2|3|4|5) echo "你输入的数字为 $aNum!"
8 ;;
9 *) echo "你输入的数字不是 1 到 5 之间的!"
10 continue
11 echo "游戏结束"
12 ;;
13 esac
14done
运行代码发现,当输入大于 5 的数字时,该例中的循环不会结束,语句 echo "游戏结束" 永远不会被执行。
函数
函数定义
linux shell
可以用户定义函数,然后在 shell 脚本中可以随便调用。
shell
中函数的定义格式如下:
1[ function ] funname [()]
2
3{
4
5 action;
6
7 [return int;]
8
9}
说明:
- 1、可以带
function fun()
定义,也可以直接fun()
定义,不带任何参数。
- 2、参数返回,可以显示加:
return
返回,如果不加,将以最后一条命令运行结果,作为返回值。 return
后跟数值n(0-255)
下面的例子定义了一个函数并进行调用:
1#!/bin/bash
2
3demoFun(){
4 echo "这是我的第一个 shell 函数!"
5}
6echo "-----函数开始执行-----"
7demoFun
8echo "-----函数执行完毕-----"
输出结果:
1-----函数开始执行-----
2这是我的第一个 shell 函数!
3-----函数执行完毕-----
下面定义一个带有return
语句的函数:
1#!/bin/bash
2
3funWithReturn(){
4 echo "这个函数会对输入的两个数字进行相加运算..."
5 echo "输入第一个数字: "
6 read aNum
7 echo "输入第二个数字: "
8 read anotherNum
9 echo "两个数字分别为 $aNum 和 $anotherNum !"
10 return $(($aNum+$anotherNum))
11}
12funWithReturn
13echo "输入的两个数字之和为 $? !"
输出类似下面:
1这个函数会对输入的两个数字进行相加运算...
2输入第一个数字:
31
4输入第二个数字:
52
6两个数字分别为 1 和 2 !
7输入的两个数字之和为 3 !
函数返回值在调用该函数后通过 $?
来获得。
TIP
注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell
解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
函数参数
在Shell
中,调用函数时可以向其传递参数。在函数体内部,通过 $n
的形式来获取参数的值,例如,$1
表示第一个参数,$2 表示第二个参数...
带参数的函数示例:
1#!/bin/bash
2
3funWithParam(){
4 echo "第一个参数为 $1 !"
5 echo "第二个参数为 $2 !"
6 echo "第十个参数为 $10 !"
7 echo "第十个参数为 ${10} !"
8 echo "第十一个参数为 ${11} !"
9 echo "参数总数有 $# 个!"
10 echo "作为一个字符串输出所有参数 $* !"
11}
12funWithParam 1 2 3 4 5 6 7 8 9 34 73
输出结果:
1第一个参数为 1 !
2第二个参数为 2 !
3第十个参数为 10 !
4第十个参数为 34 !
5第十一个参数为 73 !
6参数总数有 11 个!
7作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !
TIP
注意,$10
不能获取第十个参数,获取第十个参数需要${10}
。当n>=10
时,需要使用${n}
来获取参数。
另外,还有几个特殊字符用来处理参数:
参数处理 |
说明 |
$# |
传递到脚本或函数的参数个数 |
$* |
以一个单字符串显示所有向脚本传递的参数 |
$$ |
脚本运行的当前进程 ID 号 |
$! |
后台运行的最后一个进程的 ID 号 |
$@ |
与$*相同,但是使用时加引号,并在引号中返回每个参数。 |
$- |
显示 Shell 使用的当前选项,与 set 命令功能相同。 |
$? |
显示最后命令的退出状态。0 表示没有错误,其他任何值表明有错误。 |
获取函数的返回值
1#!/bin/bash
2
3getsum(){
4 local sum=0 #局部变量
5 for((i=$1; i<=$2; i++)); do
6 ((sum+=i))
7 done
8 echo $sum
9 return $? # 这里$?就是 echo $sum 执行的结果
10}
11read m
12read n
13total=$(getsum $m $n)
14echo "The sum is $total"
TIP
函数没有return
的时候返回值是最后一行代码的执行结果。
正侧表达式
基本正侧表达式
正则表达式和通配符的区别:
- 正则表达式用来在文件中匹配符合条件的字符串,正则是包含匹配.
grep
、awk
、sed
等命令可以支持正则表达式。
- 通配符
( ?[])
是用来匹配符合条件的文件名*
,通配符是完全匹配。ls
、find
、cp
等命令不支持正则表达式,所有只能使用 shell
自己的通配符来进行匹配了。
元字符 |
作用 |
* |
前一个字符匹配 0 次或者任意多次 |
. |
匹配除了换行符外任意一个字符 |
^ |
匹配行首, |
例如:^hello 会匹配以hello 开头的行。 |
|
$ |
匹配行首, |
例如:hello$ 会匹配以hello 开头的行。 |
|
[ ] |
匹配括号中指定的任意一个字符,只匹配一个字符 |
[ ^] |
匹配除中括号的字符意外的任意一个字符。 |
例如:[^0-9] 匹配任意一个非数字字符。 |
|
\ |
转义符。用于将特殊符号的含义取消 |
{n} |
表示其前面的字符签好出现 n 次。 |
例如:[0-9]{4} 匹配 4 位数字,[1][3-8][0-9]{9} 匹配手机号码 |
|
{n,} |
表示七千亩的字符出现不小于 n 次。 |
例如:[0-9]{2,} 表示两位及以上的数字 |
|
{n,m} |
表示其前面的字符至少出现 n 次,最多出现 m 次。 |
例如:[a-z]{6,8} 匹配 6 到 8 为的小写子母。 |
|
1grep "a*" test_rule.txt
2#匹配所有内容,包括空白行
3grep "aaaa*" test_rule.txt
4#匹配最少包含三个连续a的字符串
5grep "s..d" test_rule.txt
6#匹配在s和d两个子母之间的有两个字符的单词
7grep "s.*d" test_rule.txt
8#匹配在s和d之间有任意字符的单词所在的行
9grep "^M" test_rule.txt
10#匹配以大写子母M开头的行
11grep "n$" test_rule.txt
12#匹配以小写n结尾的行
13grep -n "^$" test_rule.txt
14#匹配空白行
15grep "^[a-z]" test_rule.txt
16#匹配以小写子母开头的行
17grep "\.$" test_rule.txt
18#匹配以.结尾的行
常用正侧表达式
| 字符 | 描述 |
| --- | --- | --- | --- |
| \
| 将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。 |
| 例如:“n”
匹配字符“n”
。“\n”
匹配一个换行符。序列“\\”
匹配“\”
而“\(”
则匹配“(”
。 |
| ^
| 匹配输入字符串的开始位置。如果设置了 RegExp
对象的 Multiline
属性,^
也匹配“\n”
或“\r”
之后的位置。 |
| $
| 匹配输入字符串的结束位置。如果设置了 RegExp
对象的 Multiline
属性,$也匹配“\n”
或“\r”
之前的位置。 |
| *
| 匹配前面的子表达式零次或多次。 |
| 例如:zo*
能匹配“z”
以及“zoo”
。*
等价于{0,}
。 |
| +
| 匹配前面的子表达式一次或多次。 |
| 例如:“zo+”
能匹配“zo”
以及“zoo”
,但不能匹配“z”
。+
等价于{1,}
。 |
| ?
| 匹配前面的子表达式零次或一次。 |
| 例如:“do(es)?”
可以匹配“do”
或“does”
中的“do”
。?
等价于{0,1}
。 |
| {n}
| n
是一个非负整数。匹配确定的 n
次。 |
| 例如:“o{2}”
不能匹配“Bob”
中的“o”
,但是能匹配“food”
中的两个 o。 |
| {n,}
| n
是一个非负整数。至少匹配 n
次。 |
| 例如:“o{2,}”
不能匹配“Bob”
中的“o”
,但能匹配“foooood”
中的所有 o
。“o{1,}”
等价于“o+”
。“o{0,}”
则等价于“o*”
。 |
| {n,m}
| m
和 n
均为非负整数,其中 n<=m
。最少匹配 n
次且最多匹配 m
次。 |
| 例如:“o{1,3}”
将匹配“fooooood”
中的前三个 o
。“o{0,1}”
等价于“o?”
。请注意在逗号和两个数之间不能有空格。 |
| ?
| 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})
后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。 |
| 例如:对于字符串“oooo”
,“o+?”
将匹配单个“o”
,而“o+”
将匹配所有“o”。 |
| .
| 匹配除“\n”
之外的任何单个字符。要匹配包括“\n”
在内的任何字符,请使用像“[.\n]”
的模式。 |
| (pattern)
| 匹配 pattern
并获取这一匹配。所获取的匹配可以从产生的 Matches
集合得到,在 VBScript
中使用 SubMatches
集合,在 JScript
中则使用$0…$9
属性。要匹配圆括号字符,请使用“”或“”或“”。 |
| (?:pattern)
| 匹配 pattern
但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“( | )”
来组合一个模式的各个部分是很有用。 |
| 例如“industr(?:y | ies)”
就是一个比“industry | industries”
更简略的表达式。 |
| (?=pattern)
| 正向预查,在任何匹配 pattern
的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。 |
| 例如:“Windows(?=95 | 98 | NT | 2000)”
能匹配“Windows2000”
中的“Windows”
,但不能匹配“Windows3.1”
中的“Windows”
。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 |
| (?!pattern)
| 负向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。 |
| 例如“Windows(?!95 | 98 | NT | 2000)”
能匹配“Windows3.1”
中的“Windows”
,但不能匹配“Windows2000”
中的“Windows”
。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始 |
| x | y
| 匹配 x 或 y。 |
| 例如:“z | food”
能匹配“z”
或“food”
。“(z | f)ood”
则匹配“zood”
或“food”
。 |
| [xyz]
| 字符集合。匹配所包含的任意一个字符。 |
| 例如:“[abc]”
可以匹配“plain”
中的“a”
。 |
| [^xyz]
| 负值字符集合。匹配未包含的任意字符。 |
| 例如:“[^abc]”
可以匹配“plain”
中的“p”
。 |
| [a-z]
| 字符范围。匹配指定范围内的任意字符。 |
| 例如:“[a-z]”
可以匹配“a”
到“z”
范围内的任意小写字母字符。 |
| [^a-z]
| 负值字符范围。匹配任何不在指定范围内的任意字符。 |
| 例如:“[^a-z]”
可以匹配任何不在“a”
到“z”
范围内的任意字符。 |
| \b
| 匹配一个单词边界,也就是指单词和空格间的位置。 |
| 例如:“er\b”
可以匹配“never”
中的“er”
,但不能匹配“verb”
中的“er”
。 |
| \B
| 匹配非单词边界。“er\B
”能匹配“verb”
中的“er”
,但不能匹配“never”
中的“er”
。 |
| \cx
| 匹配由 x
指明的控制字符。 |
| 例如:\cM
匹配一个 Control-M
或回车符。x
的值必须为 A-Z
或 a-z
之一。否则,将 c
视为一个原义的“c”
字符。 |
| \d
| 匹配一个数字字符。等价于[0-9]
。 |
| \D
| 匹配一个非数字字符。等价于[^0-9]
。 |
| \f
| 匹配一个换页符。等价于\x0c
和\cL
。 |
| \n
| 匹配一个换行符。等价于\x0a
和\cJ
。 |
| \r
| 匹配一个回车符。等价于\x0d
和\cM
。 |
| \s
| 匹配任何空白字符,包括空格、制表符、换页符等等。等价于[\f\n\r\t\v]
。 |
| \S
| 匹配任何非空白字符。等价于[^\f\n\r\t\v]
。 |
| \t
| 匹配一个制表符。等价于\x09
和\cI
。 |
| \v
| 匹配一个垂直制表符。等价于\x0b
和\cK
。 |
| \w
| 匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”
。 |
| \W
| 匹配任何非单词字符。等价于“[^a-za-z0-9_]”
。 |
| \xn
| 匹配 n
,其中 n
为十六进制转义值。十六进制转义值必须为确定的两个数字长。 |
| 例如:“\x41”
匹配“A”
。“\x041”
则等价于“\x04&1”
。正则表达式中可以使用 ASCII
编码。. |
| \num
| 匹配 num
,其中 num
是一个正整数。对所获取的匹配的引用。 |
| 例如:“(.)\1”
匹配两个连续的相同字符。 |
| \n
| 标识一个八进制转义值或一个向后引用。如果\n
之前至少 n
个获取的子表达式,则 n
为向后引用。否则,如果 n
为八进制数字(0-7)
,则 n
为一个八进制转义值。 |
| \nm
| 标识一个八进制转义值或一个向后引用。如果\nm
之前至少有 nm
个获得子表达式,则 nm
为向后引用。如果\nm
之前至少有 n
个获取,则 n
为一个后跟文字 m
的向后引用。如果前面的条件都不满足,若 n
和 m
均为八进制数字(0-7)
,则\nm
将匹配八进制转义值 nm
。 |
| \nml
| 如果 n
为八进制数字(0-3)
,且 m
和 l
均为八进制数字(0-7)
,则匹配八进制转义值 nml
。 |
| \un
| 匹配 n
,其中 n
是一个用四个十六进制数字表示的 Unicode
字符。 |
| 例如:\u00A9
匹配版权符号(?)
。 |
文本处理工具
cut 命令
语法
1cut [-bn] [file]
2cut [-c] [file]
3cut [-df] [file]
使用说明:
cut 命令从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段写至标准输出。
如果不指定 File 参数,cut 命令将读取标准输入。必须指定 -b、-c 或 -f 标志之一。
参数:
- -b :以字节为单位进行分割。这些字节位置将忽略多字节字符边界,除非也指定了 -n 标志。
- -c :以字符为单位进行分割。
- -d :自定义分隔符,默认为制表符。
- -f :与-d 一起使用,指定显示哪个区域。
- -n :取消分割多字节字符。仅和 -b 标志一起使用。如果字符的最后一个字节落在由 -b 标志的 List 参数指示的 范围之内,该字符将被写出;否则,该字符将被排除
实例
当你执行 who 命令时,会输出类似如下的内容:
1$ who
2rocrocket :0 2009-01-08 11:07
3rocrocket pts/0 2009-01-08 11:23 (:0.0)
4rocrocket pts/1 2009-01-08 14:15 (:0.0)
如果我们想提取每一行的第 3 个字节,就这样:
awk 命令
语法
1awk [选项参数] 'script' var=value file(s)
2或
3awk [选项参数] -f scriptfile var=value file(s)
选项参数说明:
-F fs or --field-separator fs
指定输入文件折分隔符,fs 是一个字符串或者是一个正则表达式,如-F:。
-v var=value or --asign var=value
赋值一个用户定义变量。
-f scripfile or --file scriptfile
从脚本文件中读取awk
命令。
-mf nnn and -mr nnn
对nnn
值设置内在限制,-mf
选项限制分配给nnn
的最大块数目;-mr
选项限制记录的最大数目。这两个功能是 Bell 实验室版awk
的扩展功能,在标准awk
中不适用。
-W compact or --compat, -W traditional or --traditional
在兼容模式下运行awk
。所以gawk
的行为和标准的awk
完全一样,所有的awk
扩展都被忽略。
-W copyleft or --copyleft, -W copyright or --copyright
打印简短的版权信息。
-W help or --help, -W usage or --usage
打印全部awk
选项和每个选项的简短说明。
-W lint or --lint
打印不能向传统unix
平台移植的结构的警告。
-W lint-old or --lint-old
打印关于不能向传统unix
平台移植的结构的警告。
-W posix
打开兼容模式。但有以下限制,不识别:/x、函数关键字、func
、换码序列以及当fs
是一个空格时,将新行作为一个域分隔符;操作符和=不能代替^和^=;fflush
无效。
-W re-interval or --re-inerval
允许间隔正则表达式的使用,参考(grep
中的Posix
字符类),如括号表达式[[:alpha:]]
。
-W source program-text or --source program-text
使用 program-text
作为源代码,可与-f 命令混用。
-W version or --version
打印 bug 报告信息的版本。
基本用法
log.txt 文本内容如下:
12 this is a test
23 Do you like awk
3This's a test
410 There are orange,apple,mongo
用法一:
1awk '{[pattern] action}' {filenames} # 行匹配语句 awk '' 只能用单引号
实例:
1# 每行按空格或TAB分割,输出文本中的1、4项
2 $ awk '{print $1,$4}' log.txt
3 ---------------------------------------------
4 2 a
5 3 like
6 This's
7 10 orange,apple,mongo
8 # 格式化输出
9 $ awk '{printf "%-8s %-10s\n",$1,$4}' log.txt
10 ---------------------------------------------
11 2 a
12 3 like
13 This's
14 10 orange,apple,mongo
用法二:
1awk -F #-F相当于内置变量FS, 指定分割字符
实例:
1# 使用","分割
2 $ awk -F, '{print $1,$2}' log.txt
3 ---------------------------------------------
4 2 this is a test
5 3 Do you like awk
6 This's a test
7 10 There are orange apple
8 # 或者使用内建变量
9 $ awk 'BEGIN{FS=","} {print $1,$2}' log.txt
10 ---------------------------------------------
11 2 this is a test
12 3 Do you like awk
13 This's a test
14 10 There are orange apple
15 # 使用多个分隔符.先使用空格分割,然后对分割结果再使用","分割
16 $ awk -F '[ ,]' '{print $1,$2,$5}' log.txt
17 ---------------------------------------------
18 2 this test
19 3 Are awk
20 This's a
21 10 There apple
用法三:
实例:
1$ awk -va=1 '{print $1,$1+a}' log.txt
2 ---------------------------------------------
3 2 3
4 3 4
5 This's 1
6 10 11
7 $ awk -va=1 -vb=s '{print $1,$1+a,$1b}' log.txt
8 ---------------------------------------------
9 2 3 2s
10 3 4 3s
11 This's 1 This'ss
12 10 11 10s
用法四:
实例:
运算符
| 运算符 | 描述 |
| --------------------------- | -------------------------------- | --- | ------ |
| = += -= *= /= %= ^= **= | 赋值 |
| ?: | C 条件表达式 |
| | | | 逻辑或 |
| && | 逻辑与 |
| ~ 和 !~ | 匹配正则表达式和不匹配正则表达式 |
| < <= > >= != == | 关系运算符 |
| 空格 | 连接 |
| + - | 加,减 |
| * / % | 乘,除与求余 |
| + - ! | 一元加,减和逻辑非 |
| ^ *** | 求幂 |
| ++ -- | 增加或减少,作为前缀或后缀 |
| $ | 字段引用 |
| in | 数组成员 |
过滤第一列大于 2 的行
1$ awk '$1>2' log.txt #命令
2#输出
33 Do you like awk
4This's a test
510 There are orange,apple,mongo
过滤第一列等于 2 的行
1$ awk '$1==2 {print $1,$3}' log.txt #命令
2#输出
32 is
过滤第一列大于 2 并且第二列等于'Are'的行
1$ awk '$1>2 && $2=="Are" {print $1,$2,$3}' log.txt #命令
2#输出
33 Are you
内建变量
变量 |
描述 |
$n |
当前记录的第 n 个字段,字段间由FS 分隔 |
$0 |
完整的输入记录 |
ARGC |
命令行参数的数目 |
ARGIND |
命令行中当前文件的位置(从 0 开始算) |
ARGV |
包含命令行参数的数组 |
CONVFMT |
数字转换格式(默认值为%.6g )ENVIRON 环境变量关联数组 |
ERRNO |
最后一个系统错误的描述 |
FIELDWIDTHS |
字段宽度列表(用空格键分隔) |
FILENAME |
当前文件名 |
FNR |
各文件分别计数的行号 |
FS |
字段分隔符(默认是任何空格) |
IGNORECASE |
如果为真,则进行忽略大小写的匹配 |
NF |
一条记录的字段的数目 |
NR |
已经读出的记录数,就是行号,从 1 开始 |
OFMT |
数字的输出格式(默认值是%.6g) |
OFS |
输出字段分隔符,默认值与输入字段分隔符一致。 |
ORS |
输出记录分隔符(默认值是一个换行符) |
RLENGTH |
由match 函数所匹配的字符串的长度 |
RS |
记录分隔符(默认是一个换行符) |
RSTART |
由match 函数所匹配的字符串的第一个位置 |
SUBSEP |
数组下标分隔符(默认值是/034 ) |
1$ awk 'BEGIN{printf "%4s %4s %4s %4s %4s %4s %4s %4s %4s\n","FILENAME","ARGC","FNR","FS","NF","NR","OFS","ORS","RS";printf "---------------------------------------------\n"} {printf "%4s %4s %4s %4s %4s %4s %4s %4s %4s\n",FILENAME,ARGC,FNR,FS,NF,NR,OFS,ORS,RS}' log.txt
2FILENAME ARGC FNR FS NF NR OFS ORS RS
3---------------------------------------------
4log.txt 2 1 5 1
5log.txt 2 2 5 2
6log.txt 2 3 3 3
7log.txt 2 4 4 4
8$ awk -F\' 'BEGIN{printf "%4s %4s %4s %4s %4s %4s %4s %4s %4s\n","FILENAME","ARGC","FNR","FS","NF","NR","OFS","ORS","RS";printf "---------------------------------------------\n"} {printf "%4s %4s %4s %4s %4s %4s %4s %4s %4s\n",FILENAME,ARGC,FNR,FS,NF,NR,OFS,ORS,RS}' log.txt
9FILENAME ARGC FNR FS NF NR OFS ORS RS
10---------------------------------------------
11log.txt 2 1 ' 1 1
12log.txt 2 2 ' 1 2
13log.txt 2 3 ' 2 3
14log.txt 2 4 ' 1 4
15# 输出顺序号 NR, 匹配文本行号
16$ awk '{print NR,FNR,$1,$2,$3}' log.txt
17---------------------------------------------
181 1 2 this is
192 2 3 Are you
203 3 This's a test
214 4 10 There are
22# 指定输出分割符
23$ awk '{print $1,$2,$5}' OFS=" $ " log.txt
24---------------------------------------------
252 $ this $ test
263 $ Are $ awk
27This's $ a $
2810 $ There $
使用正则,字符串匹配
1# 输出第二列包含 "th",并打印第二列与第四列
2$ awk '$2 ~ /th/ {print $2,$4}' log.txt
3---------------------------------------------
4this a
~ 表示模式开始。// 中是模式。
1# 输出包含 "re" 的行
2$ awk '/re/ ' log.txt
3---------------------------------------------
43 Do you like awk
510 There are orange,apple,mongo
忽略大小写
1$ awk 'BEGIN{IGNORECASE=1} /this/' log.txt
2---------------------------------------------
32 this is a test
4This's a test
模式取反
1$ awk '$2 !~ /th/ {print $2,$4}' log.txt
2---------------------------------------------
3Are like
4a
5There orange,apple,mongo
6$ awk '!/th/ {print $2,$4}' log.txt
7---------------------------------------------
8Are like
9a
10There orange,apple,mongo
awk 脚本
关于 awk 脚本,我们需要注意两个关键词 BEGIN 和 END。
- BEGIN{ 这里面放的是执行前的语句 }
- END {这里面放的是处理完所有的行后要执行的语句 }
- {这里面放的是处理每一行时要执行的语句}
假设有这么一个文件(学生成绩表):
1$ cat score.txt
2Marry 2143 78 84 77
3Jack 2321 66 78 45
4Tom 2122 48 77 71
5Mike 2537 87 97 95
6Bob 2415 40 57 62
我们的 awk 脚本如下:
1$ cat cal.awk
2#!/bin/awk -f
3#运行前
4BEGIN {
5 math = 0
6 english = 0
7 computer = 0
8
9 printf "NAME NO. MATH ENGLISH COMPUTER TOTAL\n"
10 printf "---------------------------------------------\n"
11}
12#运行中
13{
14 math+=$3
15 english+=$4
16 computer+=$5
17 printf "%-6s %-6s %4d %8d %8d %8d\n", $1, $2, $3,$4,$5, $3+$4+$5
18}
19#运行后
20END {
21 printf "---------------------------------------------\n"
22 printf " TOTAL:%10d %8d %8d \n", math, english, computer
23 printf "AVERAGE:%10.2f %8.2f %8.2f\n", math/NR, english/NR, computer/NR
24}
我们来看一下执行结果:
1$ awk -f cal.awk score.txt
2NAME NO. MATH ENGLISH COMPUTER TOTAL
3---------------------------------------------
4Marry 2143 78 84 77 239
5Jack 2321 66 78 45 189
6Tom 2122 48 77 71 196
7Mike 2537 87 97 95 279
8Bob 2415 40 57 62 159
9---------------------------------------------
10 TOTAL: 319 393 350
11AVERAGE: 63.80 78.60 70.00
另外一些实例
AWK 的 hello world 程序为:
1BEGIN { print "Hello, world!" }
计算文件大小
1$ ls -l *.txt | awk '{sum+=$5} END {print sum}'
2--------------------------------------------------
3666581
从文件中找出长度大于 80 的行:
打印九九乘法表
1seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"\n":"\t")}'
发送消息案例
1#!/bin/zsh
2
3# 查看用户是否登录
4login_user=$(who | grep -i -m 1 $1 | awk '{ print $1}')
5
6if [ -z $login_user ]
7then
8 echo "$1 不在线"
9 echo “脚本退出”
10 exit
11fi
12
13# 查看用户是否开启了消息功能
14is_allowwed=$(who | grep -i -m 1 $1 | awk '{ print $1}')
15
16if [ $is_allowwed != '+' ]
17then
18 echo "$1 没有开启消息功能"
19 echo “脚本退出”
20 exit
21fi
22
23# 确认是否消息发送
24if [ -z $2 ]
25then
26 echo "没有消息发送"
27 echo “脚本退出”
28 exit
29fi
30
31# 从参数中获取要发送的消息
32whole_msg=$(echo $* | cut -d " " -f 2-)
33
34# 获取用户登录的终端
35user_terminal=$(who | grep -i -m 1 $1 | awk '{print $2}')
36
37# 写入发送的消息
38echo $whole_msg | write $login_user $user_terminal
39
40if [ $? != 0 ]
41then
42 echo "发送失败“
43else
44 echo "发送成功"
45fi
46exit