1、简介
Shell 是一个用 C 语言编写的程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。 Shell 脚本(shell script),是一种为 shell 编写的脚本程序。 业界所说的 shell 通常都是指 shell 脚本,但shell 和 shell script 是两个不同的概念。
2、变量
- 等号两边不能有空格。
- 变量名只能使用英文字母,数字和下划线,首个字符不能以数字开头 。
- 变量名中间不能有空格,可以使用下划线(_)。
- 变量名不能使用标点符号 。
- 变量名不能使用bash里的关键字
2.1、定义变量
name='franky'
#除了显式地直接赋值,还可以用语句给变量赋值
for file in `ls /etc` 或 for file in $(ls /etc)
2.2、使用变量
使用一个定义过的变量,只要在变量名前面加美元符号即可,如:
your_name="frank"
echo $your_name
echo ${your_name}
#变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界。推荐给所有变量加上花括号,这是个好的编程习惯。
2.3、只读变量
#!/bin/bash
myUrl="http://www.google.com"
readonly myUrl
#使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
2.4、删除变量
#!/bin/sh
myUrl="http://www.runoob.com"
unset myUrl
echo $myUrl #没有任何输出
2.5、变量类型
- 局部变量: 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
- 环境变量: 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量,例如:父进程定义的变量在子进程中,通常是无法使用的,但通过export将变量变成环境变量后,就可以使用了。
- shell变量: shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行
2.6、变量的声明(declare)
默认情况下,变量的赋值内容都是字符类型的。 declare命令的使用形式如下:
declare [-aixrfF] variable_name
参数a:将variable定义为数组
参数i:将variable定义为整型(integer)
参数x:将variable设置成环境变量,类似于export的作用
参数r:variable为readonly类型,值不能被更改,不能使用unset。
参数f/F:函数
2.7、变量中切割字符串
- 利用操作符进行字符串切割
1、#
操作符。从左边开始删除第一次出现子字符串即其左边字符,保留右边字符。
2、[franky@localhost ~]$ str='http://www.franky.com/cut-string.html' [franky@localhost ~]$ echo ${str#*//} www.franky.com/cut-string.html
##
操作符。从左边开始删除最后一次出现子字符串即其左边字符,保留右边字符。
3、[franky@localhost ~]$ str='http://www.franky.com/cut-string.html' [franky@localhost ~]$ echo ${str##*/} cut-string.html
%
操作符。从右边开始删除第一次出现子字符串即其右边字符,保留左边字符。
4、[franky@localhost ~]$ str='http://www.franky.com/cut-string.html' [franky@localhost ~]$ echo ${str%/*} http://www.franky.com
%%
操作符。从右边开始删除最后一次出现子字符串即其右边字符,保留左边字符。[franky@localhost ~]$ str='http://www.franky.com/cut-string.html' [franky@localhost ~]$ echo ${str%%/*} http:
- 通过字符的位置切割字符串
1、从左边第几个字符开始以及字符的个数,用法为:start:len,例如:
2、从左边第几个字符开始一直到结束,用法为:start,例如:[franky@localhost ~]$ str='http://www.franky.com/cut-string.html' [franky@localhost ~]$ echo ${str:0:5} http:
3、从右边第几个字符开始以及字符的个数,用法:0-start:len,例如:[franky@localhost ~]$ str='http://www.franky.com/cut-string.html' [franky@localhost ~]$ echo ${str:7} # 其中的 7 表示左边第8个字符开始 www.franky.com/cut-string.html
4、从右边第几个字符开始一直到结束,用法:0-start,例如:[franky@localhost ~]$ str='http://www.franky.com/cut-string.html' [franky@localhost ~]$ echo ${str:0-15:10} #0-15表示右边算起第15个字符开始,10表示截取字符的个数。 cut-string
注意: 左边的第一个字符是用 0 表示,右边的第一个字符用 0-1 表示[franky@localhost ~]$ str='http://www.franky.com/cut-string.html' [franky@localhost ~]$ echo ${str:0-15} #右边算起第15个字符开始,到本字符串结尾 cut-string.html
3、传递参数
参数 | 含义 |
---|---|
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
$0 | 命令行脚本名称 |
$n | 命令行脚本后接第n个参数 |
$# | 命令行参数个数 |
$* | 以"$1 $2 … $n"的形式输出所有参数 |
$@ | 以"$1" “ 2 " … " 2" … " 2"…"n” 的形式输出所有参数 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$_ | 显示Shell使用的当前选项,与set命令功能相同。 |
4、数组
-
Bash Shell 只支持一维数组(不支持多维数组),初始化时不需要定义数组大小 。
-
数组元素的下标由0开始。
-
Shell 数组用括号来表示,元素用"空格"符号分割开,语法格式如下:
array_name=(value1 value2 value3... valuen)
4.1、定义数组
#!/bin/bash
my_array=(A B "C" D)
#也可以使用下标来定义数组:
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
4.2、读取数组
-
一个一读取
echo "第一个元素为: ${my_array[0]}" echo "第二个元素为: ${my_array[1]}" echo "第三个元素为: ${my_array[2]}" echo "第四个元素为: ${my_array[3]}"
-
获取所有元素
echo "数组的元素为: ${my_array[*]}" echo "数组的元素为: ${my_array[@]}"
-
获取数组长度
echo "数组元素个数为: ${#my_array[*]}" echo "数组元素个数为: ${#my_array[@]}"
5、运算符
原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 、let和 expr。expr 是一款表达式计算工具,使用它能完成表达式的求值操作。
例如,两个数相加(注意使用的是反引号` 而不是单引号 '):
#!/bin/bash
val=`expr 2 + 2`
echo "两数之和为 : $val"
- 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2 。
- 完整的表达式要被``包含,注意这个字符不是常用的单引号,在 Esc 键下边。
5.1、算数运算符
下表列出了常用的算术运算符,假定变量 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。 |
注意:
- 条件表达式要放在方括号之间,并且要有空格 。
- 乘号(*)前边必须加反斜杠(\)才能实现乘法运算;在 MAC 中 shell 的 expr 语法是:$((表达式)),此处表达式中的 “*” 不需要转义符号 “\” 。
5.2、关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。 下表列出了常用的关系运算符,假定变量 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。 |
5.3、布尔运算符
下表列出了常用的布尔运算符,假定变量 a 为 10,变量 b 为 20:
运算符 | 说明 | 举例 |
---|---|---|
! | 非运算,表达式为 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。 |
5.4、字符串运算符
下表列出了常用的字符串运算符,假定变量 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。 |
str | 检测字符串是否为空,不为空返回 true。 | [ $a ] 返回 true。 |
5.5、文件测试运算符
运算符 | 说明 | 举例 |
---|---|---|
-b | 检测文件是否是块设备文件,如果是,则返回 true。 | [ -b $file ] 返回 false。 |
-c | 检测文件是否是字符设备文件,如果是,则返回 true。 | [ -c $file ] 返回 false。 |
-d | 检测文件是否是目录,如果是,则返回 true。 | [ -d $file ] 返回 false。 |
-S | 检测文件是否存在且为一个 Socket 档案? | |
-L | 检测文件是否存在且为一个连结档? | |
-f | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | [ -f $file ] 返回 true。 |
-g | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | [ -g $file ] 返回 false。 |
-k | 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 | [ -k $file ] 返回 false。 |
-p | 检测文件是否是有名管道,如果是,则返回 true。 | [ -p $file ] 返回 false。 |
-u | 检测文件是否设置了 SUID 位,如果是,则返回 true。 | [ -u $file ] 返回 false。 |
-r | 检测文件是否可读,如果是,则返回 true。 | [ -r $file ] 返回 true。 |
-w | 检测文件是否可写,如果是,则返回 true。 | [ -w $file ] 返回 true。 |
-x | 检测文件是否可执行,如果是,则返回 true。 | [ -x $file ] 返回 true。 |
-s | 检测文件是否为空(文件大小是否大于0),不为空返回 true。 | [ -s $file ] 返回 true。 |
-e | 检测文件(包括目录)是否存在,如果是,则返回 true。 | [ -e $file ] 返回 true。 |
5.6、两文件之间的比较
如: test file1 -nt file2
参数 | 含义 |
---|---|
-nt | newer than)判断file1 是否比 file2 新 |
-ot | (older than)判断 file1 是否比 file2 旧 |
-ef | 判断 file1 不 file2 是否为同一文件,可用在hard link 的判定上。 主要意义在判定,两个档案是否均指向同一个 inode 。 |
5.7、整数二元比较操作符
[] | (()) or [[]] | 释义 |
---|---|---|
-eq | == | equal |
-ne | != | not equal |
-gt | > | greater than |
-ge | >= | greater equal |
-lt | < | less than |
-le | <= | less equal |
5.8、逻辑操作符
[] | (()) or [[]] | 释义 |
---|---|---|
-a | && | and |
-o | || | or |
! | ! | not |
5.9、test命令
Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。
#!/bin/sh
num1=100
num2=100
if test $num1 -eq $num2; then
echo '两个数相等!'
else
echo '两个数不相等!'
fi
================================= 等价于 =================================
# test $num1 -eq $num2 <==> [ $num1 -eq $num2 ] <==> [[ $num1==$num2 ]] <==> (($num1==$num2))
# 注意:[[]]和[]两端必须有空格,而(())可以没有。比较符两端可以没有空格,但为了书写美观加上空格。
============================================================================
#!/bin/sh
if test -e /etc/hosts -o -e /etc/resolv.conf;then
echo '至少有一个文件存在!'
else
echo '两个文件都不存在'
fi
============================================================================
#!/bin/sh
if [ -e /etc/hosts -o -e /etc/resolv.conf ];then
echo '至少有一个文件存在!'
else
echo '两个文件都不存在'
fi
6、流程控制
6.1、分支
-
if
if test $(ps -ef | grep -c "ssh") -le 10 ; then echo 'num <= 10' elif [ $(ps -ef | grep -c "ssh") -gt 10 -a $(ps -ef | grep -c "ssh") -le 100 ]; then echo '10 < num <= 100' else echo 'num > 100' fi # 这些代码也可以写成一行,不影响执行,注意用分号将每一句隔开。
-
case
#!/bin/sh function start_func() { echo 'start service' } function stop_func() { echo 'stop service' } case $1 in start) start_func ;; stop) stop_func ;; restart) stop_func start_func ;; esac
6.2、循环
-
for
for var in item1 item2 ... itemN do command1 command2 ... commandN done
-
while
while condition do command done # condition 一般为条件表达式,如果返回值为false,则跳出循环。
-
until
- until 循环执行一系列命令直至条件为 true 时停止。
- until 循环与 while 循环在处理方式上刚好相反 。
- 一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。
until condition do command done # condition 一般为条件表达式,如果返回值为true,则跳出循环。
-
无限循环
while : do command done ########################## while true do command done ########################## for (( ; ; )) do command done
-
跳出循环
break
跳出所有循环(终止执行后面的所有循环)
continue
仅仅跳出本次循环 -
例子
#!/bin/sh # 定义变量 declare -a arr declare -i i # 构造数组 i=0 while ((i<10));do arr[i]=num0$i ((i++)) done # 打印数组中各元素 for n in ${arr[@]};do echo $n done # 打印数组中各元素 len=${#arr[@]} for ((i=0; i<len; i++));do echo ${arr[$i]} done
7、函数
Linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。 shell中函数的定义格式如下:
#!/bin/sh
function funname(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "第十个参数为 $10 !"
echo "第十个参数为 ${10} !"
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
return $(($3+$2-$5)) #返回一个0-255的整数
}
funname #执行函数,不传递参。
funname arg1 arg2 arg3 arg4 arg5 #执行函数并传参
- 可以带function fun() 定义,也可以直接fun() 定义, 不带任何参数。
- 参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255)
8、输入/输出
8.1、命令
-
read
- -p 指定读取值时的提示符
- -t 指定读取值时等待的时间(秒)
#!/bin/sh read -t 5 -p "What's your name?" name echo "hello $name,welcome to my script."
-
echo - display a line of text
echo会将输入的字符串送往标准输出。输出的字符串间以空白字符隔开, 并在最后加上换行号。
- -n 不要在最后自动换行
- -e 启用下列反斜杠转义字符的功能
- \a 发出警告声
- \b 删除前一个字符
- \c 最后不加上换行符号
- \f 换行但光标仍旧停留在原来的位置
- \n 换行且光标移至行首
- \r 光标移至行首,但不换行
- \t 插入tab
- \v 与\f相同
- \ 插入\字符
- \nnn 插入nnn(八进制)所代表的ASCII字符
-
printf
printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。 printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。
-
printf 命令的语法
#!/bin/bash printf format-string [arguments...] # format-string: 为格式控制字符串 # arguments: 为参数列表。
-
实例
#!/bin/bash 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 # %s %c %d %f都是格式替代符 # %-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐),任何字符都会被显示在10个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。 # %-4.2f 指格式化为小数,其中.2指保留2位小数。 # %d %s %c %f 格式替代符详解: # d: Decimal 十进制整数 -- 对应位置参数必须是十进制整数,否则报错! # s: String 字符串 -- 对应位置参数必须是字符串或者字符型,否则报错! # c: Char 字符 -- 对应位置参数必须是字符串或者字符型,否则报错! # f: Float 浮点 -- 对应位置参数必须是数字型,否则报错!
-
8.2、重定向
命令 | 说明 |
---|---|
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 > /dev/null
-
执行命令,屏幕不显示标准和错误输出
$ command > /dev/null 2>&1
9、通配符
通配符是shell的内置功能,用于匹配文件名。
符号 | 作用 |
---|---|
* | 匹配任何字符串/文本,包括空字符串;*代表任意字符(0个或多个) ls file * |
? | 匹配任何一个字符(不在括号内时)?代表任意1个字符 ls file 0 |
[abcd] | 匹配abcd中任何一个字符 |
[a-z] | 表示范围a到z,表示范围的意思 []匹配中括号中任意一个字符 ls file 0 |
{…} | 表示生成序列. 以逗号分隔,且不能有空格 |
{} | 利用 {} 来备份 |
[!abcd]或[^abcd] | 表示非,表示不匹配括号里面的任何一个字符 |
-
利用 {} 来备份
[root@localhost test]# touch {ab,ac,ad} && ls ab ac ad [root@localhost test]# cp a{c,fff} && ls ab ac ad afff
-
[]与{}区别
[]
- 只能用来找文件{}
- 用来找文件,或创造文件,生成序列
10、正则表达式
Linux正则表达式不同于通配符, Linux正则表达式为Linux三剑客而生,其它大多数命令不支持Linux正则表达式。
-
基础正则表达式
符号 意义 * 0个或多个在*字符之前的那个普通字符 . 匹配任意一个字符(任意 && 一个) .* 任意个任意字符 ^ 匹配行首,或后面字符的非 。 $ 匹配行尾 ^$ 空行(匹配行首,又匹配行尾,中间无任何字符,故为空行 [] 匹配字符集合;[a-z]:包含a到z中任意;[ab]:包含a或b [^] 取反 \ 转义符,屏蔽一个元字符的特殊意义 <> 精确匹配符 {n} 匹配前面字符出现n次 {n,} 匹配前面字符至少出现n次 {n,m} 匹配前面字符出现n-m次 -
扩展正则表达式
符合 意义 ? 匹配0个或1个在其之前的那个普通字符 + 匹配1个或多个在其之前的那个普通字符 () 表示一个字符集合或用在expr中 | 表示“或”,匹配一组可选的字符 -
预定义的字符类(不属于正则,但正则匹配会用到)
符号 字符表示 [:digit:] 任意数字,相当于0-9 [:lower:] 任意小写字母 [:upper:] 任意大写字母 [:alpha:] 任意大小写字母 [:alnum:] 任意数字或字母 [:blank:] 水平空白字符 [:space:] 水平或垂直空白字符 [:punct:] 标点符号 [:print:] 可打印字符 [:cntrl:] 控制(非打印)字符 [:graph:] 图形字符 [:xdigit:] 十六进制字符
11、Linux三剑客
11.1、grep(文本过滤器)
Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来。grep全称是Global Regular Expression Print,表示全局正则表达式版本,它的使用权限是所有用户。
参数 | 意义 |
---|---|
-v | 排除匹配内容行 |
-n | 显示匹配行及 行号 |
-i | 不区分大小写 |
-c | 只输出匹配行的计数 |
-h | 查询多文件时不显示文件名 |
-l | 查询多文件时只输出包含匹配字符的文件名 |
-s | 不显示不存在或无匹配文本的错误信息 |
-o | 只输出匹配内容 |
-E | 扩展正则表达式(ERE) |
-B n | 匹配行及匹配行前n行 |
-A n | 匹配行及匹配行后n行 |
-C n | 匹配行及匹配行前、后n行 |
[root@localhost scripts]# grep --color=auto -B 2 'nobody' /etc/passwd
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
11.2、sed(文本流编辑器)
sed是一个很好的文件处理工具,本身是一个管道命令,主要是以行为单位进行处理,可以编辑一个或多个文件、简化对文件的反复操作、编写转换程序等。
11.2.1、语法
sed [-hnV][-e<script>][-f<script文件>][文本文件]
参数说明
- -e <script>或–expression=<script> :以选项中指定的script来处理输入的文本文件。
- -f <script文件>或–file=<script文件> :以选项中指定的script文件来处理输入的文本文件。
- -n或–quiet或–silent:使用安静模式,则只有经过sed 特殊处理的那一行(或者动作)才会被列出来。
- -i:直接修改读取的档案内容,而不是由屏幕输出。
- -h或–help:显示帮助。
- -V或–version: 显示版本信息。
动作说明
- a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)
- c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行
- d :删除,因为是删除啊,所以 d 后面通常不接任何咚咚
- i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行)
- p :打印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行
- s :取代。s/old/new/g,g代表全部;
s/old/new/
只将每行中的第一个old改为new,s/old/new/g
将每行中所有old改为new。
11.2.2、实例
-
打印
/etc/passwd
文件中的3~5行[franky@localhost ~]$ sed -n '3,5p' /etc/passwd daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
-
在
/etc/passwd
文件中以root开头的行后新增hello world行[franky@localhost ~]$ sed '/^root/ahello world' /etc/passwd root:x:0:0:root:/root:/bin/bash hello world bin:x:1:1:bin:/bin:/sbin/nologin ...... #若修改文件而非屏幕输出,需要加-i参数 [franky@localhost ~]$ sed -i '/^root/ahello world' /etc/passwd
-
在
/etc/passwd
文件中以root开头的行前插入hello world行[franky@localhost ~]$ sed '/^root/ihello world' /etc/passwd hello world root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin ...... #若修改文件而非屏幕输出,需要加-i参数 [franky@localhost ~]$ sed -i '/^root/ihello world' /etc/passwd
-
把
/etc/passwd
文件中的root修改为franky[franky@localhost ~]$ sed -n 's/root/franky/gp' /etc/passwd franky:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/franky:/sbin/nologin
-
删除
/etc/passwd
文件中的第二行到最后一行[franky@localhost ~]$ sed '2,$d' /etc/passwd root:x:0:0:root:/root:/bin/bash #若修改文件而非屏幕输出,需要加-i参数 [franky@localhost ~]$ sed -i '2,$d' /etc/passwd
-
多点编辑
# 一条sed命令,删除/etc/passwd第三行到末尾的数据,并把bash替换为sh [franky@localhost ~]$ sed -e '3,$d' -e 's/bash/sh/' /etc/passwd root:x:0:0:root:/root:/bin/sh bin:x:1:1:bin:/bin:/sbin/nologin
11.3、awk(文本报告生成器)
awk是一个非常棒的数字处理工具。相比于sed常常作用于一整行的处理,awk则比较倾向于将一行分为数个“字段”来处理。运行效率高,而且代码简单,对格式化的文本处理能力超强。
awk的一般语法格式为:awk [-参数 变量] 'BEGIN{初始化}条件类型1{动作1}条件类型2{动作2}…END{后处理}'
;其中BEGIN后{}中的语句在开始读文件之前执行,而END{}中的语句在结束读文件之后执行,awk是一行一行读取文件。
11.3.1、参数
参数 | 意义 |
---|---|
-F | 指定字段分隔符 |
-v | 用于多变量赋值。 |
-f | 允许awk调用并执行符合awk语法的程序文件 |
11.3.2、变量
-
变量命名规则:以字母或下划线开头,剩下的部分可以是:字母、数字、下划线。
- 以字母开头
- 使用中划线或者下划线做单词的连接
- 同类型的用数字区分
- 对于文件最好加上拓展名
-
内置变量
参数 意义 ARGC 命令行参数的个数 ARGV 命令行参数数组 ARGIND 当前被处理文件的ARGV标志符 NR 已经读出的记录数 FNR 当前文件的记录数 FS 输入字段分隔符(默认空格),相当于-F选项 OFS 输出字段分隔符(默认空格) NF 当前记录中的字段个数,结尾变量 RS 输入记录分隔符,默认为"\n" ORS 输出记录分隔符,默认为换行符,控制每个print语句后的输出符号 [root@CentOS6 ~]$ cat test.txt this is a test. [root@CentOS6 ~]$ awk 'BEGIN{OFS=":"}END{print $1,$2,$3,$4}' test.txt this:is:a:test.
-
自定义变量
[root@CentOS6 ~]$ awk -v test="hello world" 'BEGIN{print test}' # 等号两边不能有空格 hello world [root@CentOS6 ~]$ awk 'BEGIN{test="hello world";print test}' # 等号两边可以有空格 hello world
11.3.3、打印
-
print - print的使用格式:print item1, item2, …
1、各项目之间使用逗号隔开,而输出时则以空白字符分隔;
2、输出的item可以为字符串或数值、当前记录的字段(如$1)、变量或awk的表达式;数值会先转换为字符串,而后再输出;
3、print命令后面的item可以省略,此时其功能相当于print $0awk 'BEGIN{FS=":"}{print}' /etc/passwd awk -F':' '{print $0}' /etc/passwd
4、如果想输出空白行,则需要使用print "
awk '{print ""}' /etc/passwd
-
printf - printf命令的使用格式:printf format, item1, item2, …
要点:- 其与print命令的最大不同是,printf需要指定format;
- format用于指定后面的每个item的输出格式;
- printf语句不会自动打印换行符,需要加\n;
format格式:
- %c: 显示字符的ASCII码;
- %d, %i:十进制整数;
- %e, %E:科学计数法显示数值;
- %f: 显示浮点数;
- %g, %G: 以科学计数法的格式或浮点数的格式显示数值;
- %s: 显示字符串;
- %u: 无符号整数;
- %%: 显示%自身;
修饰符:
- n: 显示宽度,n为正整数;
- -: 左对齐(默认右对齐);
- +:显示数值符号;
实例:
[franky@localhost ~]$ awk 'BEGIN{FS=":"}{printf "%-10s%-10s\n",$1,$3}' /etc/passwd root 0 bin 1 daemon 2 adm 3 lp 4 sync 5 ... [root@CentOS6 ~]$ awk -F: '{printf "%-15s %i\n",$1,$3 > "/dev/stderr" }' /etc/passwd root 0 saslauth 499 ntp 38 mysql 500 [root@CentOS6 ~]$ awk -F: '$3+1>=500{printf "%-15s %i\n",$1,$3 > "/dev/stderr" }' /etc/passwd saslauth 499 mysql 500 ########### 特殊文件描述符 ########### # /dev/stdin:标准输入 # /dev/sdtout: 标准输出 # /dev/stderr: 错误输出 # /dev/fd/N: 某特定文件描述符,如/dev/stdin就相当于/dev/fd/0;
11.3.4、操作符
-
算术操作符
运算符 名称 描述 实例 x+y 加 x 和 y 的和 awk ‘BEGIN{print 3+2}’ x-y 减 x 和 y 的差 awk ‘BEGIN{print 3-2}’ x*y 乘 x 和 y 的积 awk ‘BEGIN{print 3*2}’ x/y 除 x 和 y 的商 awk ‘BEGIN{print 3/2}’ x%y 摸 x 除以 y 的余数 awk ‘BEGIN{print 3%2}’ x**y 幂 x的y次方 awk ‘BEGIN{print 3**2}’ x^y 幂 x的y次方 awk ‘BEGIN{print 3^2}’ -x 取反 x的相反数 awk ‘BEGIN{print -(-2)}’ +x 转换为数值 -
赋值操作符
=,+=,-=,*=,/=,%=,^=, **=,++,–
需要注意的是,如果某模式为=号,此时使用/=/可能会有语法错误,应以/[=]/替代;
-
模式匹配符
~
- 是否匹配,!~
- 是否不匹配 -
逻辑关系符
&&
- 并且,||
- 或者,!
- 非 -
字符串操作符
只有一个,而且不用写出来,用于实现字符串连接; -
布尔值
awk中,任何非0值或非空字符串都为真,反之就为假;
-
比较操作符
<
,<=
,>
,>=
,==
,!=
-
条件表达式(三元运算符)
语法:
selector?if-true-exp:if-false-exp
awk -F: '{print ($3>500?$1:$2)}' /etc/passwd
-
函数调用
function_name (para1,para2)
11.3.5、模式
-
/regular expression/:仅处理被模式匹配到的行
awk '/root/{print}' /etc/passwd
-
relational expression:关系表达式,为"真"时处理 (“真”:结果是非0值或非空字符串)
awk -F ":" '$3>500{print}' /etc/passwd
-
line ranges:行范围 (startline, endline 或 /pat1/, /pat2/)
注意: 此处行范围不支持直接给出数字的格式
awk -F: '(NR>=2&&NR<=10){print $1}' /etc/passwd awk -F: '/^h/,/^s/{print $1}' /etc/passwd
-
BEGIN/END模式
- BEGIN{}: 仅在开始处理文件中的文本之前执行一次
- END{}:仅在文本处理完成之后执行一次
11.3.6、流程控制
-
分支
-
if
awk 'BEGIN{FS=":"}{if($3>500)print $1}' /etc/passwd
-
if…else
awk 'BEGIN{FS=":"}{if($3>500)print $1;else print $2}' /etc/passwd
-
if…elseif…else
awk -F ':' '{if ($1 == "root") print $1;else if ($1 == "seker") print $6;else if ($1 == "zorro") print $7;else print NR}' /etc/passwd
-
switch…case
语法:switch(expression) {case VALUE1 or /REGEXP/: statement; case VALUE2 or /REGEXP2/: statement; ...; default: statement}
-
-
循环
对一行内的多个字段逐一处理时使用;对数组中的各元素逐一处理时使用-
while
- 语法 :
while(condition) statement (条件"真"时进入循环;条件"假"时退出循环)
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg
- 语法 :
-
do-while
- 语法:
do statement while(condition) (至少执行一次循环体)
awk '/^[[:space:]]*linux16/{i=1;do{print $i,length($i);i++} while(i<=NF)}' /etc/grub2.cfg
- 语法:
-
for
-
语法:
for(expr1;expr2;expr3) statement
,
for(variable assignment;condition;iteration process) {for-body}
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg # 迭代循环 awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}'
-
-
break 和 continue
break:终止循环
continue:中断本次循环继续下一轮 -
next
提前结束对本行的处理而直接进入下一行awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
-
11.3.7、数组
-
关联数组
array[index-expression]
index-expression:
① 可使用任意字符串;字符串要使用双引号;
② 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为"空串" -
若要判断数组中是否存在某元素,要使用"index in array"格式进行
-
若要遍历数组中的每个元素,要使用for循环:for(var in array) {for-body}
awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}'
netstat -tan | awk '/^tcp\>/{state[$NF]++}END{for(i in state){ print i,state[i]}}'
awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log
awk '/^UUID/{fs[$3]++}END{for(i in fs){print i,fs[i]}}' /etc/fstab
awk '{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count){print i,count[i]}}' /etc/fstab
netstat -tan | awk '/^tcp\>/{state[$NF]++}END{for(i in state){print i,state[i]}}'
11.3.8、函数
-
内置函数
函数 功能 rand() 返回0和1之间一个随机数 length([s]) 返回指定字符串的长度 sub(r,s,[t]) 以r表示的模式来查找t所表示的字符中的匹配的内容,并将其第一次出现替换为s所表示的内容 gsub(r,s,[t]) 以r表示的模式来查找t所表示的字符中的匹配的内容,并将其所有出现均替换为s所表示的内容 split(s,a,[r]) 以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数组中 -
自定义函数
一个程序包含有多个功能,每个功能我们可以独立一个函数。
函数可以提高代码的复用性。
用户自定义函数的语法格式为:
function function_name(argument1, argument2, ...) { function body }
解析:
- function_name 是用户自定义函数的名称。函数名称应该以字母开头,其后可以是数字、字母或下划线的自由组合。AWK 保留的关键字不能作为用户自定义函数的名称。
- 自定义函数可以接受多个输入参数,这些参数之间通过逗号分隔。参数并不是必须的。我们也可以定义没有任何输入参数的函数。
- function body 是函数体部分,它包含 AWK 程序代码。
以下实例我们实现了两个简单函数,它们分别返回两个数值中的最小值和最大值。我们在主函数 main 中调用了这两个函数。 文件 functions.awk 代码如下:
# 返回最小值 function find_min(num1, num2) { if (num1 < num2) return num1 return num2 } # 返回最大值 function find_max(num1, num2) { if (num1 > num2) return num1 return num2 } # 主函数 function main(num1, num2) { # 查找最小值 result = find_min(10, 20) print "Minimum =", result # 查找最大值 result = find_max(10, 20) print "Maximum =", result } # 脚本从这里开始执行 BEGIN { main(10, 20) }
执行 functions.awk 文件,可以得到如下的结果:
$ awk -f functions.awk Minimum = 10 Maximum = 20
11.3.9、经典案例
-
统计TCP连接各状态数
netstat -n|awk '/^tcp/{++S[$NF]}END{for(n in S)print n,S[n]}'
-
查看IP地址
ip addr|awk -F '[/ ]+' '/inet /{print $3}'
-
将访问量大的IP加入黑名单
#!/bin/sh while true do awk '{print $1}' access.log|grep -v "^$"|sort|uniq -c >/tmp/tmp.log # 读取/tmp/tmp.log文件,按行循环处理 exec </tmp/tmp.log while read line do ip=`echo $line|awk '{print $2}'` count=`echo $line|awk '{print $1}'` if [ $count -gt 30000 -a `iptables -L -n|grep "$ip"|wc -l` -lt 1 ];then iptables -I INPUT -s $ip -j DROP echo "$line is dropped" >>/tmp/droplist.log fi done sleep 5 done
-
统计文件text.awk的第一列中是浮点数的平均值
cat text.awk 1.5 33 1s.11 44 34.5 6 ss 7 8 awk 'BEGIN{total = 0;cnt = 0} {if($1~/^[\d]+\.[\d]+$/){total += $1; cnt++}} END{print total/len}' text.awk # 分析:$1~/^[\d]+\.[\d]+$/表示$1与“/ /”里面的正则表达式进行匹配, # 若匹配,则total加上$1,且len自增,即数目加1.“^[\d]+\.[\d]+$”是个正则表达式, # “^[\d]”表示以数字开头,“\.”是转义的意思,表示“.”为小数点的意思。“[0-9]*”表示0个或多个数字.