Shell编程

Shell编程

shell变量

变量的命名规则

1.命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
2.等号左右两侧不能有空格,可以使用下划线“_”,变量的值如果有空格,需要使用单引号或双引号包括。如:“test=“hello world!””。其中双引号括起来的内容“$”,“(”和反引号都拥有特殊含义,而单引号括起来的内容都是普通字符。
3.不能使用标点符号,不能使用bash里的关键字(可用help命令查看保留关键字)。
4.环境变量建议大写,便于区分
5.需要增加变量的值,那么可以进行变量值的叠加。不过变量需要用双引号包含"$变量名"或用${变量名}包含变量名。
关于单双引号的问题:
双引号能够识别变量,双引号能够实现转义(类似于“*”)
单引号是不能识别变量,只会原样输出,单引号是不能转义的

将命令的结果赋值给变量
Shell 也支持将命令的执行结果赋值给变量,常见的有以下两种方式:
variable=`Shell`
variable=$(Shell)

shell中的特殊符号

符号作用
’ ’单引号。在单引号中所有的特殊符号,如"$"和(反引号)都没没有特殊含义。单引号括起来的都是普通字符,会原样输出
“ ”双引号。在双引号中特殊符号都没有特殊含义,但“$“,”`“(Esc键下面)和”"是例外,拥有”调用变量的值“、”引用命令”和“转义符”的特殊含义
``反引号。反引号括起来的内容是系统命令,在Bash中会先执行它。和()作用一样,推荐使用()
$()和反引号作用一样,用来引用系统命令(推荐使用)
()用于一串命令执行时,()中的命令会在shell中运行
{}用于一串命令执行时,{}中的命令会在当前shell中执行。也可以用于变量变形与替换
[]用于变量的测试
#在shell脚本中,#开头的行代表注释
$用于调用变量的值,如需要调用变量name的值时,需要用$name的方式得到变量
\转义符,跟在\之后的特殊符号将失去特殊含义,变为普通字符。如"$"符号,而不当作是变量引用

单引号和双引号

[root@localhost ~]$ name=sc
#定义变量name 的值是sc
[root@localhost ~]$ echo '$name'
$name
#如果输出时使用单引号,则$name原封不动的输出
[root@localhost ~]$ echo "$name"
sc
#如果输出时使用双引号,则会输出变量name的值 sc

[root@localhost ~]$ echo `date`
2018年10月21日星期一18:16:33 CST
#反引号括起来的命令会正常执行
[root@localhost ~]$ echo '`date`'
`date`
#但是如果反引号命令被单引号括起来,那么这个命令不会执行,―date`会被当成普通字符输出
[root@localhost ~]$ echo "`date'"
2018年10月21日星期一18:14:21 CST
#如果是双引号括起来,那么这个命令又会正常执行

反引号

[root@localhost ~]$ echo ls
ls
#如果命令不用反引号包含,命令不会执行,而是直接输出
[root@localhost ~]$ echo `ls`
anaconda-ks.cfginstall.loginstall.log.syslog sh test testfile
#只有用反引号包括命令,这个命令才会执行
[root@localhost ~]$ echo $(date)
2018年10月21日星期一18:25:09 CST
#使用$(命令)的方式也是可以的

变量的分类

1.用户自定义变量: 这种变量是最常见的变量,由用户自由定义变量名和变量的值。

2.环境变量: 这种变量中主要保存的是和系统操作环境相关的数据,比如当前登录用户,用户的家目录,命令的提示符等。不是太好理解吧,那么大家还记得在Windows中,同一台电脑可以有多个用户登录,而且每个用户都可以定义自己的桌面样式和分辨率,这些其实就是Windows的操作环境,可以当做是Windows的环境变量来理解。环境变量的变量名可以自由定义,但是一般对系统起作用的环境变量的变量名是系统预先设定好的。

3.位置参数变量: 这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的。

4.预定义变量: 是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的。

用户自定义变量:

变量定义

[root@localhost ~]$ 2name="shen chao"
-bash: 2name=shen chao: command not found
#变量名不能用数字开头
[root@localhost ~]$ name = "shenchao"
-bash: name: command not found
#等号左右两侧不能有空格
[root@localhost ~]$ name=shen chao
-bash: chao: command not found
#变量的值如果有空格,必须用引号包含

变量查看

[root@localhost ~]$ set [选项]
选项:
-u:如果设定此选项,调用未声明变量时会报错(默认无任何提示)
-x:如果设定此选项,在命令执行之前,会把命令先输出一次
+<参数> :取消某个set曾启动的参数。

[root@localhost ~]$ set
BASH=/bin/bash
…省略部分输出…
name='shen chao'
#直接使用set 命令,会查询系统中所有的变量,包含用户自定义变量和环境变量
[root@localhost ~]$ set -u
[root@localhost ~]$ echo $file
-bash: file: unbound variable
#当设置了-u选项后,如果调用没有设定的变量会有报错。默认是没有任何输出的。
[root@localhost ~]$ set -x
[root@localhost ~]$ ls
+ls --color=auto
anaconda-ks.cfginstall.loginstall.log.syslog sh tdir testtestfile
#如果设定了-x选项,会在每个命令执行之前,先把命令输出一次

[root@localhost ~]$ set +x
#取消启动的x参数


变量删除

[root@localhost ~]$ unset 变量名

环境变量:

环境变量设置

[root@localhost ~]$  export age="18"
#使用export声明的变量即是环境变量

环境变量的查询和删除

env命令和set命令的区别:
set命令可以查看所有变量,而env命令只能查看环境变量。

[root@localhost ~]$ unset gender   #删除环境变量gender
[root@localhost ~]$ env | grep gender

系统默认环境变量

[root@localhost ~]$ env
HOSTNAME=localhost.localdomain      #主机名
SHELL=/bin/bash                     #当前的shell
TERM=linux                          #终端环境
HISTSIZE=1000                       #历史命令条数
SSH_CLIENT=192.168.4.1594824 22     #当前操作环境是用ssh连接的,这里记录客户端ip
SSH_TTY=/dev/pts/1                  #ssh连接的终端时pts/1
USER=root                           #当前登录的用户
..........更多参数可以使用set和env命令查看.............

位置参数变量

位置参数变量作用
$nn为数字,$0表示当前shell脚本程序的每次,-9 代 表 第 一 到 第 九 个 参 数 , 十 以 上 的 参 数 需 要 用 大 括 号 包 含 , 如 9代表第一到第九个参数,十以上的参数需要用大括号包含,如9代表第一到第九个参数,十以上的参数需要用大括号包含,如{10}
$*这个变量代表命令行中所有的参数,$把所有的参数看成一个整体
$@这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待
$#这个变量代表命令行中所有参数的个数
预定义变量
预定义变量作用
$?最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行,如果这个变量的值为非О(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。
$$当前进程的进程号(PID)
$!后台运行的最后一个进程进程号(PID)

只读变量


[root@localhost sh]$ vi readonly.sh
#!/bin/bash
a=10
#语法:readonly 变量名
readonly a
a=20   #会报错readonly variable
echo $a

接受键盘输入
[root@localhost ~]$ read [选项][变量名]
选项:
	-a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
	-p: “提示信息”:在等待read输入时,输出提示信息
	-t: 秒数:read命令会一直等待用户输入,使用此选项可以指定等待时间
	-n: 数字:read命令只接受指定的字符数,就会执行
	-s: 隐藏输入的数据,适用于机密信息的输入
    -d: 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志。
    -e: 在输入的时候可以使用命令补全功能。
变量名:
变量名可以自定义,如果不指定变量名,会把输入保存入默认变量REPLY.
如果只提供了一个变量名,则整个输入行赋予该变量.
如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,而命令行上的最后一个变量取得剩余的所有字

例子:

[root@localhost sh]$ vi read.sh
#!/bin/bash

read -t 30 -p "Please input your name: " name
#提示“请输入姓名”并等待30 秒,把用户的输入保存入变量name 中
echo "Name is $name"
#看看变量“$name”中是否保存了你的输入

read -s -t 30 -p "Please enter your age: " age
#提示“请输入年龄”并等待30秒,把用户的输入保存入变量age中
#年龄是隐私,所以我们用“-s”选项隐藏输入
echo -e "\n"
#调整输出格式,如果不输出换行,一会的年龄输出不会换行
echo "Age is $age"

read -n 1 -t 30 -p "Please select your gender[M/F]:" gender
#提示“请选择性别”并等待30秒,把用户的输入保存入变量gender
#使用“-n1”选项只接收一个输入字符就会执行(都不用输入回车)
echo -e "\n"
echo "Sex is $gender"

shell传递参数

特殊字符处理参数说明

在这里插入图片描述

Shell字符串

三种形式的区别

  1. 由单引号’ '包围的字符串:
    任何字符都会原样输出,在其中使用变量是无效的。
    字符串中不能出现单引号,即使对单引号进行转义也不行。

  2. 由双引号" "包围的字符串:
    如果其中包含了某个变量,那么该变量会被解析(得到该变量的值),而不是原样输出。
    字符串中可以出现双引号,只要它被转义了就行。

  3. 不被引号包围的字符串
    不被引号包围的字符串中出现变量时也会被解析,这一点和双引号" "包围的字符串一样。
    字符串中不能出现空格,否则空格后边的字符串会作为其他变量或者命令解析。

按指定的字符串截取

从左向右截取最后一个string后的字符串
${varible##*string}
从左向右截取第一个string后的字符串
${varible#*string}
从右向左截取最后一个string后的字符串
${varible%%string*}
从右向左截取第一个string后的字符串
${varible%string*}
“*”只是一个通配符可以不要

${varible:n1:n2}:截取变量varible从n1开始的n2个字符,组成一个子字符串。可以根据特定字符偏移和长度,使用另一种形式的变量扩展,来选择特定子字符串。试着在 bash 中输入以下行:

$ EXCLAIM=cowabunga
$ echo ${EXCLAIM:0:3}
cow
$ echo ${EXCLAIM:3:7}
abunga

按照指定要求分割

比如获取后缀名

ls -al | cut -d “.” -f2

判断读取字符串值

${var}变量var的值,与$var相同
${var-DEFAULT}如果var没有被声明,那么就以$DEFAULT作为其值*
${var:-DEFAULT}如果var没有被声明,或者其值为空,那么就以$DEFAULT作为其值*
${var=DEFAULT}如果var没有被声明, 那么就以$DEFAULT作为其值 *
${var:=DEFAULT}如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值 *
${var+OTHER}如果var声明了, 那么其值就是$OTHER, 否则就为null字符串
${var:+OTHER}如果var被设置了, 那么其值就是$OTHER, 否则就为null字符串
${var?ERR_MSG}如果var没被声明, 那么就打印$ERR_MSG *
${var:?ERR_MSG}如果var没被设置, 那么就打印$ERR_MSG *
${!varprefix*}匹配之前所有以varprefix开头进行声明的变量
${!varprefix@}匹配之前所有以varprefix开头进行声明的变量

加入了“*” 不是意思是: 当然, 如果变量var已经被设置的话, 那么其值就是$var.

字符串操作(长度,读取,替换)

${string}$string的长度
${string:position}在string中,从位置position开始提取子串
${string:position:length}在string中,从位置position开始提取长度为$length子串
${string#substring}从变量string的开头,删除最短匹配substring的子串
${string##substring}从变量string的开头,删除最长匹配substring的子串
${string%substring}从变量string的结尾,删除最短匹配substring的子串
${string%%substring}从变量string的结尾,删除最长匹配substring的子串
${string/substring/replacement}使用replacement,来代替第一个匹配的substring
${string//substring/replacement}使用r e p l a c e m e n t , 代 替 ∗ 所 有 ∗ 匹 配 的 replacement, 代替所有匹配的replacement,代替∗所有∗匹配的substring
${string/#substring/replacement}如果s t r i n g 的 ∗ 前 缀 ∗ 匹 配 string的前缀匹配string的∗前缀∗匹配substring, 那么就用r e p l a c e m e n t 来 代 替 匹 配 到 的 replacement来代替匹配到的replacement来代替匹配到的substring
${string/%substring/replacement}如果s t r i n g 的 ∗ 后 缀 ∗ 匹 配 string的后缀匹配string的∗后缀∗匹配substring, 那么就用r e p l a c e m e n t 来 代 替 匹 配 到 的 replacement来代替匹配到的replacement来代替匹配到的substring

Shell字符串截取

在这里插入图片描述

Shell数组

Shell 数组用括号来表示,元素用"空格"符号分割开,语法格式如下:

在这里插入图片描述

创建一个简单的数组 my_array:

在这里插入图片描述

在这里插入图片描述

读取数组

${arry_name[index]}

关联数组

关联数组使用 declare 命令来声明,语法格式如下

declare -A array_name
declare -A site=(["google"]="www.google.com"["taobao"]="www.taobao.com")
等价于:
declare -A site
site["googel"]="www.google.com"
site["taobao"]="www.taobao.com"

访问关联数组元素可以使用指定的键,格式如下:

declare -A site
site["googel"]="www.google.com"
site["taobao"]="www.taobao.com"

访问:
echo ${site["google"]}

获取数组中的所有元素
使用 @ 或 * 可以获取数组中的所有元素,例如:

在这里插入图片描述

注意:*代表的时变成一个整体字符串,而@表示一个一个的

获取数组的长度

echo ${#array1[*]}

删除数组元素和数组

unset array2[2]			# 删除索引数组的第三个元素

unset user_info[age]	# 删除关联数组中索引为age的元素

unset array2			# 删除数组

Shell运算符

Shell 和其他编程语言一样,支持多种运算符,包括:
算数运算符
关系运算符
布尔运算符
字符串运算符
文件测试运算符

算术运算符

在这里插入图片描述

注意:条件表达式要放在方括号之间,并且要有空格,例如: [$a==$b] 是错误的,必须写成 [ $a == $b ]。

关系运算符

在这里插入图片描述

注意:只支持数字,不支持字符串,除非字符串的值是数字

布尔运算符

在这里插入图片描述

(备注:shell中布尔值不能直接输出)

逻辑运算符

在这里插入图片描述

字符串运算符

在这里插入图片描述

Shell echo命令

显示普通字符串:
echo "It is a test"
这里的双引号完全可以省略,以下命令与上面实例效果一致:

echo It is a test
显示转义字符
选项:

-n 不换行输出
-e 启用反斜线转义解释
-E 禁用反斜线转义解释(默认)
 
功能说明:显示文字。
语   法:echo [-ne][字符串]     或   echo [--help][--version]
补充说明:echo会将输入的字符串送往标准输出。输出的字符串间以空白字符隔开, 并在最后加上换行号。
参   数:-n 不要在最后自动换行  。-e 若字符串中出现以下字符,则特别加以处理,而不会将它当成一般文字输出:
\a 发出警告声;
\b 删除前一个字符;
\c 最后不加上换行符号;
\f 换行但光标仍旧停留在原来的位置;
\n 换行且光标移至行首;
\r 光标移至行首,但不换行;
\t 插入tab;
\v 与\f相同;
\\ 插入\字符;
\nnn 插入nnn(八进制)所代表的ASCII字符;
–help 显示帮助
–version 显示版本信息

echo "\"It is a test\""
结果将是:

"It is a test"
同样,双引号也可以省略
显示变量

read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量

#!/bin/sh
read name 
echo "$name It is a test"

以上代码保存为 test.sh,name 接收标准输入的变量,结果将是:

[root@www ~]# sh test.sh
OK                     #标准输入
OK It is a test        #输出
显示换行
echo -e "OK! \n" # -e 开启转义
echo "It it a test"

输出结果:

OK!

It it a test
显示不换行
#!/bin/sh
echo -e "OK! \c" # -e 开启转义 \c 不换行
echo "It is a test"

输出结果:

OK! It is a test
显示结果定向至文件
echo "It is a test" > myfile
原样输出字符串,不进行转义或取变量(用单引号)
echo '$name\"'

输出结果:

$name\"
显示命令执行结果
echo `date`

注意: 这里使用的是反引号 `, 而不是单引号 '。

结果将显示当前日期

Thu Jul 24 10:08:46 CST 2014

read命令

在这里插入图片描述

只读取一个字符

在这里插入图片描述

备注:-n 1表示只读取一个字符。运行脚本后,只要用户输入一个字符,立即读取结束,不用等待用户按下回车键。

在指定时间内输入密码

在这里插入图片描述

printf命令

在这里插入图片描述

在这里插入图片描述

test命令

test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试

数值测试

在这里插入图片描述
在这里插入图片描述

字符串测试
在这里插入图片描述

文件测试

在这里插入图片描述

在这里插入图片描述

Shell流程控制

if-else语句

if 语句语法格式:

在这里插入图片描述

if else 语法格式:

在这里插入图片描述

if else-if else 语法格式
在这里插入图片描述

if else 的 […] 判断语句中大于使用 -gt,小于使用 -lt

在这里插入图片描述

如果使用 ((…)) 作为判断语句,大于和小于可以直接使用 > 和 <。

在这里插入图片描述

for 循环

在这里插入图片描述

while语句

while 循环用于不断执行一系列命令,也用于从输入文件中读取数据。其语法格式为:

在这里插入图片描述
在这里插入图片描述

until 循环

until 循环执行一系列命令直至条件为 true 时停止。

condition 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。

until 循环与 while 循环在处理方式上刚好相反。

一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。

until 语法格式:
在这里插入图片描述

case … esac

case 工作方式如上所示,取值后面必须为单词 in,每一模式必须以右括号结束。取值可以为变量或常数,匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。
实例:提示输入 1 到 4,与每一种模式进行匹配:

在这里插入图片描述

匹配字符串
在这里插入图片描述

shell函数

语法:

function 函数名 () {
	程序
}

案例:接收用户输入的数字,然后从1加到这个数字

[root@localhost ~]$ vi sh/function.sh
#!/bin/bash
#接收用户输入的数字,然后从1加到这个数字

function sum () {
	#定义函数sum
	s=0
	for (( i=0; i<=$num;i=i+1 ))
		#循环直到i大于$1为止。$1是函数sum 的第一个参数
		#在函数中也可以使用位置参数变量,不过这里的$1指的是函数的第一个参数
		do
			s=$(( $i+$s ))
		done
	echo "The sum of 1+2+3...+$1 is :$s"
	#输出1加到$1的和
}

read -p "Please input a number: " -t 30 num
#接收用户输入的数字,并把值赋予变量num
y=$(echo $num | sed 's/[0-9]//g')
#把变量num的值替换为空,并赋予变量y

if [ -z "$y"]
#判断变量y是否为空,以确定变量num中是否为数字
	then
		sum $num
		#调用sum函数,并把变量num的值作为第一个参数传递给sum函数
else
		echo "Error!! Please input a number!"
		#如果变量num 的值不是数字,则输出报错信息
fi

实例2:定义一个带有return语句的函数

在这里插入图片描述

几个特殊字符用来处理参数

在这里插入图片描述

输入输出重定向

在这里插入图片描述

输出重定向

在这里插入图片描述

在这里插入图片描述

输入重定向

在这里插入图片描述

在这里插入图片描述

重定向深入讲解

在这里插入图片描述

在这里插入图片描述

/dev/null 文件

在这里插入图片描述

exit语句

系统是有exit命令的,用于退出当前用户的登录状态。可是在Shell脚本中,exit语句是用来退出当前脚本的。也就是说,在Shell脚本中,只要碰到了exit语句,后续的程序就不再执行,而直接退出脚本

exit [返回值]

如果exit命令之后定义了返回值,那么这个脚本执行之后的返回值就是我们自己定义的返回值。可以通过查询$?这个变量,来查看返回值。如果exit之后没有定义返回值,脚本执行之后的返回值是执行exit 语句之前,最后执行的一条命令的返回值。写一个exit 的例子:

[root@localhost ~]$ vi sh/exit.sh
#!/bin/bash
#演示exit的作用

read -p "Please input a number: " -t 30 num
#接收用户的输入,并把输入赋予变量num
y=$ (echo $num | sed 's/[0-9]//g')
#如果变量num 的值是数字,则把num的值替换为空,否则不替换
#把替换之后的值赋予变量y
[ -n "$y" ] && echo "Error! Please input a number!" && exit 18
#判断变量y的值如果不为空,输出报错信息,退出脚本,退出返回值为18
echo "The number is: $num"
#如果没有退出加班,则打印变量num中的数字

break语句

当程序执行到break语句时,会结束整个当前循环。而continue 语句也是结束循环的语句,不过continue 语句单次当前循环,而下次循环会继续。

[root@localhost ~]$ vi sh/break.sh
#!/bin/bash
#演示break 跳出循环

for (( i=1;i<=10; i=i+1 ))
#循环十次
	do
		if ["$i" -eq 4 ]
		#如果变量i的值等于4
			then
			break
			#退出整个循环
		fi
	echo $i
	#输出变量i的值
	done

执行下这个脚本,因为一旦变量i的值等于4,整个循环都会跳出,所以应该只能循环三次:

[root@localhost ~]$ chmod 755 sh/break.sh
[root@localhost ~]#sh/break.sh
1
2
3

continue语句

continue也是结束流程控制的语句。如果在循环中,continue语句只会结束单次当前循环。

案例:

[root@localhost ~]$ vi sh/break.sh
#!/bin/bash
#演示continue

for (( i=1;i<=10;i=i+1 ))
#循环十次
	do
		if ["$i" -eq 4 ]
		#如果变量i的值等于4
			then
			continue
			#退出换成continue
		fi
	echo $i
	#输出变量i的值
	done

执行下这个脚本:

[root@localhost ~]$ chmod 755 sh/continue.sh
[root@localhost ~]#sh/break.sh
1
2
3
5
6
7
8
9
10
#少了4这个输出

字符截取、替换命令

cut列提取命令

[root@localhost ~]$ cut [选项] 文件名
选项:
-f 列号: 提取第几列
-d 分隔符: 按照指定分隔符分割列
-n	取消分割多字节字符
-c 字符范围: 不依赖分隔符来区分列,而是通过字符范围(行首为0)来进行字段提取。“n-”表示从第n个字符到行尾;“n-m”从第n个字符到第m个字符;“一m”表示从第1个字符到第m个字符。
--complement	补足被选择的字节、字符或字段
--out-delimiter	指定输出内容是的字段分割符


cut命令的默认分隔符是制表符,也就是“tab”键,不过对空格符可是支持的不怎么好啊。我们先建立一个测试文件,然后看看cut命令的作用吧:

[root@localhost ~]$ vi student.txt
id	name	gender	mark
1	liming	m		86
2	sc		m		67
3	tg		n		90

[root@localhost ~]$ cut -f 2 student.txt
#提取第二列内容

那如果想要提取多列呢?只要列号直接用“,”分开,命令如下:

[root@localhost ~]$ cut -f 2,3 student.txt

cut可以按照字符进行提取,需要注意“8-”代表的是提取所有行的第十个字符开始到行尾,而“10-20”代表提取所有行的第十个字符到第二十个字符,而“-8”代表提取所有行从行首到第八个字符:

#提取第八个字符开始到行尾,好像很乱啊,那是因为每行的字符个数不相等啊
1
2
[root@localhost ~]$ cut -d ":" -f 1,3 /etc/passwd
#以“:”作为分隔符,提取/etc/passwd_文件的第一列和第三列
1
2

如果我想用cut命令截取df命令的第一列和第三列,就会出现这样的情况:

[root@localhost~]$ df -h | cut -d " " -f 1,3
Filesystem 
/dev/sda2 
tmpfs 
/dev/sda1
1
2
3
4
5

awk 编程

[root@localhost ~]$ awk‘条件1{动作1} 条件2{动作2}…’ 文件名
条件(Pattern):
	一般使用关系表达式作为条件。这些关系表达式非常多,例如:
	x > 10  判断变量x是否大于10
	x == y  判断变量x是否等于变量y
	A ~ B   判断字符串A中是否包含能匹配B表达式的子字符串
	A !~ B  判断字符串A中是否不包含能匹配B表达式的子字符串
	
动作(Action) :
	格式化输出
	流程控制语句

常用参数:

   -F	指定输入时用到的字段分隔符
   -v	自定义变量
   -f	从脚本中读取awk命令
   -m	对val值设置内在限制



[root@localhost ~]$ awk '{printf $2 "\t" $6 "\n"}’ student.txt
#输出第二列和第六列

awk 的条件

条件的类型条件说明
awk保留字BEGIN在awk程序一开始时,尚未读取任何数据之前执行。BEGIN后的动作只在程序开始时执行一次
awk保留字END在awk程序处理完所有数据,即将结束时执行。END后的动作只在程序结束时执行一次
关系运算符>大于
关系运算符<小于
关系运算符<=小于等于
关系运算符>=大于等于
关系运算符==等于。用于判断两个值是否相等,如果是给变量赋值,请使用“”号
关系运算符!=不等于
关系运算符A~B判断字符串A中是否包含能匹配B表达式的子字符串
关系运算符A!~B判断字符串A中是否不包含能匹配B表达式的子字符串
正则表达式/正则/如果在"//"中可以写入字符,也可以支持正则表达式
BEGIN

BEGIN是awk的保留字,是一种特殊的条件类型。BEGIN的执行时机是“在 awk程序一开始时,尚未读取任何数据之前执行”。一旦BEGIN后的动作执行一次,当awk开始从文件中读入数据,BEGIN的条件就不再成立,所以BEGIN定义的动作只能被执行一次。
例如:

[root@localhost ~]$ awk 'BEGIN{printf "This is a transcript \n" } {printf $2 "\t" $6 "\n"}’ student.txt
#awk命令只要检测不到完整的单引号不会执行,所以这个命令的换行不用加入“|”,就是一行命令
#这里定义了两个动作
#第一个动作使用BEGIN条件,所以会在读入文件数据前打印“这是一张成绩单”(只会执行一次)
#第二个动作会打印文件的第二字段和第六字段

END

END也是awk保留字,不过刚好和BEGIN相反。END是在awk程序处理完所有数据,即将结束时执行。END后的动作只在程序结束时执行一次。例如:

[root@localhost ~]$ awk 'END{printf "The End \n"} {printf $2 "\t" $6 "\n"}’ student.txt
#在输出结尾输入“The End”,这并不是文档本身的内容,而且只会执行一次

关系运算符

举几个例子看看关系运算符。假设我想看看平均成绩大于等于87分的学员是谁,就可以这样输入命令:
例子1:

[root@localhost ~]$ cat student.txt | grep -v Name | awk '$6 >= 87 {printf $2 "\n"}'
#使用cat输出文件内容,用grep取反包含“Name”的行
#判断第六字段(平均成绩)大于等于87分的行,如果判断式成立,则打第六列(学员名$2)

加入了条件之后,只有条件成立动作才会执行,如果条件不满足,则动作则不运行。通过这个实验,大家可以发现,虽然awk是列提取命令,但是也要按行来读入的。这个命令的执行过程是这样的:

1)如果有BEGIN条件,则先执行BEGIN定义的动作。
2)如果没有BEGIN条件,则读入第一行,把第一行的数据依次赋予$0、$1、$2等变量。其中$0代表此行的整体数据,$1代表第一字段,$2代表第二字段。
3)依据条件类型判断动作是否执行。如果条件符合,则执行动作,否则读入下一行数据。如果没有条件,则每行都执行动作。
4)读入下一行数据,重复执行以上步骤。

正则表达式
如果要想让awk 识别字符串,必须使用“//”包含,例如:
例子1:

[root@localhost ~]$ awk '/Liming/ {print}’student.txt
#打印Liming的成绩
1
2

当使用df命令查看分区使用情况是,如果我只想查看真正的系统分区的使用状况,而不想查看光盘和临时分区的使用状况,则可以:

例子2:

[root@localhost ~]$ df -h | awk '/sda[O-9]/ {printf $1 "\t" $5 "\n"}’
#查询包含有sda数字的行,并打印第一字段和第五字段

awk内置函数

在这里插入图片描述

awk常用统计实例

1、打印文件的第一列(域) :
 awk '{print $1}' filename
 
2、打印文件的前两列(域) :
 awk '{print $1,$2}' filename
 
3、打印完第一列,然后打印第二列 : 
awk '{print $1 $2}' filename

4、打印文本文件的总行数 : 
awk 'END{print NR}' filename

5、打印文本第一行 :
awk 'NR==1{print}' filename

6、打印文本第二行第一列 :
sed -n "2, 1p" filename | awk 'print $1'



1. 获取第一列
ps -aux | grep watchdog | awk '{print $1}'

2. 获取第一列,第二列,第三列
ps -aux | grep watchdog | awk '{print $1, $2, $3}'

3. 获取第一行的第一列,第二列,第三列
ps -aux | grep watchdog | awk 'NR==1{print $1, $2, $3}'

4. 获取行数NR
df -h | awk 'END{print NR}'

5. 获取列数NF(这里是获取最后一行的列数,注意每行的列数可能是不同的)
ps -aux | grep watchdog | awk 'END{print NF}'

6. 获取最后一列
ps -aux | grep watchdog | awk '{print $NF}'

7. 对文件进行操作
awk '{print $1}' fileName

8. 指定分隔符(这里以:分割)
ps -aux | grep watchdog |awk  -F':' '{print $1}'

9. 超出范围不报错
ps -aux | grep watchdog | awk '{print $100}'

sed 文本选取、替换、删除、新增的命令

sed主要是用来将数据进行选取、替换、删除、新增的命令。

语法:

[root@localhost ~]$ sed [选项] ‘[动作]’ 文件名

选项:
-n:  一般sed命令会把所有数据都输出到屏幕,如果加入此选择,则只会把经过sed命令处理的行输出到屏幕。
-e:  允许对输入数据应用多条sed命令编辑。
-f 脚本文件名: 从sed脚本中读入sed操作。和awk命令的-f非常类似。
-r:  在sed中支持扩展正则表达式。
-i:  用sed的修改结果直接修改读取数据的文件,而不是由屏幕输出

动作:
num a \: 追加,在当前行后添加一行或多行。添加多行时,除最后一行外,每行末尾需要用“\”代表数据未完结。num表示第几行
c \: 行替换,用c后面的字符串替换原数据行,替换多行时,除最后一行外,每行末尾需用“”代表数据未完结。
num i \: 插入,在当期行前插入一行或多行。插入多行时,除最后一行外,每行末尾需要用“”代表数据未完结。num表示第几行
d ; 删除,删除指定的行。
p : 打印,输出指定的行。
s : 字串替换,用一个字符串替换另外一个字符串。格式为“行范围s/"旧字串/新字串/g”(和vim中的替换格式类似)。

对sed命令大家要注意,sed所做的修改并不会直接改变文件的内容(如果是用管道符接收的命令的输出,这种情况连文件都没有),而是把修改结果只显示到屏幕上,除非使用“-i”选项才会直接修改文件。

提取行数据

我们举几个例子来看看sed命令到底是干嘛的。假设我想查看下student.txt的第二行,那么就可以利用“p”动作了:

[root@localhost ~]$ sed  '2p' student.txt
ID      Name    php   Linux  MySQL   Average
1       AAA      66         66       66           66
2       BBB      77         77       77           77
3       CCC      88         88       88           88

指定输出某行,使用-n选项

[root@localhost ~]$ sed -n  '2p' student.txt
1       AAA      66         66       66           66

删除行数据

[root@localhost ~]$ sed  '2,4d' student.txt
#删除第二行到第四行数据

追加插入行数据

[root@localhost ~]$ sed '2a hello' student.txt
#在第二行后加入 hello

“a”会在指定行后面追加入数据,如果想要在指定行前面插入数据,则需要使用“i”动作:

[root@localhost ~]$ sed '2i hello world' student.txt
#在第二行前插入两行数据

如果是想追加或插入多行数据,除最后一行外,每行的末尾都要加入“\”代表数据未完结。再来看看“-n”选项的作用:

[root@localhost ~]$ sed -n '2i hello world' student.txt
#只查看sed命令操作的数据

替换行数据
“-n”只查看sed命令操作的数据,而不是查看所有数据。
再来看看如何实现行数据替换,假设AAA的成绩太好了,我实在是不想看到他的成绩刺激我,那就可以使用"c"动作:

[root@localhost ~]$ cat student.txt | sed '2c No such person'

sed命令默认情况是不会修改文件内容的,如果我确定需要让 sed命令直接处理文件的内容,可以使用“-i”选项。不过要小心啊,这样非常容易误操作,在操作系统文件时请小心谨慎。可以使用
这样的命令:

[root@localhost ~]$ sed -i '2c No such person' student.txt

字符串替换
“c”动作是进行整行替换的,如果仅仅想替换行中的部分数据,就要使用“s”动作了。g 使得 sed 对文件中所有符合的字符串都被替换, 修改后内容会到标准输出,不会修改原文件。

[root@localhost ~]$ sed 's/旧字串/新字串/g' 文件名
[root@localhost ~]$ sed '行范围s/旧字串/新字串/g' 文件名

替换的格式和vim非常类似,假设我觉得我自己的PHP成绩太低了,想作弊给他改高点,就可以这样来做:

[root@localhost ~]$ sed '3s/74/99/g' student.txt
#在第三行中,把74换成99

这样看起来就比较爽了吧。如果我想把AAA老师的成绩注释掉,让他不再生效。可以这样做:

[root@localhost ~]$ sed '2s/^/#/g' student.txt
#这里使用正则表达式,“^”代表行首

在sed中只能指定行范围,所以很遗憾我在他们两个的中间,不能只把他们两个注释掉,那么我们可以这样:

[root@localhost ~]$ sed -e 's/AAA//g ; s/BBB//g' student.txt
#同时把“Liming”和“Tg”替换为空

“-e”选项可以同时执行多个sed动作,当然如果只是执行一个动作也可以使用“-e”选项,但是这时没有什么意义。还要注意,多个动作之间要用“;”号或回车分割,例如上一个命令也可以这样写:

[root@localhost ~]$ sed -e 's/Liming//g
>s/Tg//g'’ student.txt

字符处理命令
sort 排序命令
[root@localhost~]$ sort [选项] 文件名
选项:

-f: 忽略大小写
-b: 忽略每行前面的空白部分
-n: 以数值型进行排序,默认使用字符串型排序
-r: 反向排序
-u: 删除重复行。就是uniq命令
-t: 指定分隔符,默认是分隔符是制表符
-k n[,m]: ―按照指定的字段范围排序。从第n字段开始,m字段结束(默认到行尾)

案例:

sort命令默认是用每行开头第一个字符来进行排序的,比如:

[root@localhost~]$ sort /etc/passwd
#排序用户信息文件

如果想要反向排序,请使用“-r”选项

[root@localhost~]$ sort -r/etc/passwd
#反向排序

如果想要指定排序的字段,需要使用“-t”选项指定分隔符,并使用“-k”选项指定字段号。加入我想要按照UID字段排序/etc/passwd文件

[root@localhost~]$ sort -t ":" -k 3,3 /etc/passwd
#指定分隔符是“:”,用第三字段开头,第三字段结尾排序,就是只用第三字段排序

因为sort默认是按照字符排序,前面用户的UID的第一个字符都是1,所以这么排序。要想按照数字排序,请使用“-n”选项:

[root@localhost~]$ sort -n -t ":" -k 3,3 /etc/passwd

当然“-k”选项可以直接使用“-k 3”,代表从第三字段到行尾都排序(第一个字符先排序,如果一致,第二个字符再排序,知道行尾)。

uniq 取消重复行
[root@localhost~]$ uniq [选项] 文件名
选项:
	-i:忽略大小写

wc 统计命令
[root@localhost~]$ wc [选项] 文件名
选项:
	-l:只统计行数
	-w:只统计单词数
	-m:只统计字符数

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值