[内容] Shell 常用知识 (转载)

shell脚本总结

shell总结

 

shell脚本的本质是shell命令的有序集合

 

建立shell脚本的步骤:建立shell脚本,编写任意多行操作系统命令或shell命令,增加文件的执行权限,结束

 

shell变量

shell允许用户建立变量存储数据,但不支持数据类型。将任何赋给变量的值都解释为一串字符

shell有如下四种变量:用户自定义变量,位置变量,预定义变量,环境变量

 

用户自定义变量:

定义变量:COUNT=1

使用时前面加$:echo $COUNT

删除变量的赋值:unset COUNT

 

位置变量:

$0  与键入的命令行一样,包含脚本文件名

$1,$2...$9  分别包含第一个到第九个命令行参数

$#  包含命令行参数的个数

$@  包含所有命令行参数:“$1,$2...$9 ”

$?  包含前一个命令的退出状态

$*  包含所有命令行参数:“$1,$2...$9 ”

$$  包含正在执行进程的ID号

 

shell环境变量

CDPATH  用于cd命令的查找路径

HOME  用户主目录

PATH  路径

 

shell 程序和语句

shell程序由0到n条shell语句构成,shell语句包括三大类:功能性语句、结构性语句、说明性语句

 

说明性语句:以#开始的部分,不被解释执行

可以出现在程序的任意位置,可以单独一行,也可以接在执行语句的后面

 

功能性语句:任意操作系统命令、shell内部命令、自编程序、等

 

read  从标准输入读出一行,赋给后面的变量

read var   #把读入的数据全部赋给var

read var1 var2 var3     #把读入一行的第一、二个词分别赋给var1、var2,其他的都赋给var3

如果执行read语句时标准输入无数据,则程序在此停留等候,直到数据到来或终止运行

 

expr  主要用于简单的整数运算,包括 +  -  \*  /   %  等

 

 反撇号用于引用命令的运行结果

 

test  用于测试三种对象:字符串、整数、文件属性

可用[  ]代替,注意左右都至少一个空格

如:test  “$answer”  =  “yes”  #变量answer的值是否为字符串yes

test  $num  -eq  18  #变量num的值是否为整数18

test  -d  tmp    #测试tmp是否为一个目录名

 

 

 

结构性语句:条件测试语句、多路分支语句、循环语句、循环控制语句、后台执行语句

条件测试语句

if...then...else...fi

语法结构:

if  表达式

then  

命令表1  #一条或者若干条命令

else

命令表2  #一条或者若干条命令

fi

如果表达式为真,执行命令表1,否则执行命令表2

例子;

if [ -f S1 ]     #测试参数是否为文件

then

echo “File $1 exists”

fi

 

if [-d $HOME/$1]   #测试参数是否为目录

then

echo “File $1 is a directory”

fi

 

 

多路分支语句

case...esac

语法结构:

case 字符串变量  in     #case语句只能检测字符串变量

模式1)

命令表1

;;

模式2)

命令表2

;;

。。。。

*)                  # *表示其他所有

命令表n

;;

esac

例如:

case $1 in

file1)

echo “file1”

;;

file2)

echo “file2”

;;

*)

echo “others”

;;

esac

 

 

循环语句

for...do...done

当循环次数已知或者确定时,使用for循环语句来多次执行一条或一组命令,循环体由do和done来限定

for  变量名  in  单词表

do

命令表

done

说明:变量依次取单词表中的各个单词,每取依次单词,就执行一次循环体,循环次数为单词表中的单词个数,命令表中可以是一条或由分号隔开的多条命令

如果单词表是命令行上的所有位置参数时,可以省去  in  单词表

list=`ls`

for file in $list

do

if[ $1 = $file ]

then

echo “$file found”;

    exit; #退出shell脚本

fi

done

 

while...do...done

语法结构:

while  命令或表达式

do

命令表

done

说明:while首先测试其后的命令或表达式的值,如果为真,就执行一次循环体中的命令,然后再测试该命令或表达式的值,执行循环体,知道为假,退出循环

 

until...do...done

语法结构:

until 命令或表达式

do

命令表

done

与上面的相反

 

循环控制语句:

break  和  continue

break n   跳出n层

continue 马上跳转到最近一层循环的下一轮循环

continue  n  转到最近n层循环语句的下一轮循环上

 

 

 

shell函数

shell程序中,常把完成固定功能且多次使用的一组命令封装在一个函数中,每当要使用时,调用函数名即可

函数调用前必须先定义,即顺序上先定义函数,再调用

调用程序可以传递参数给函数,函数可以用return语句把运行结果返回调用程序

函数只在当前shell中起作用,不能输出到子shell中

 

shell函数定义格式

function funtion_name ()   #function可以省掉

{

...

}

 

函数调用方式

value_name = `function_name [arg1 arg2 ...]`    #函数结果返回给变量

或

function_name [arg1 arg2 ...]

echo $?           #获取函数返回的状态

 

函数变量的作用域

全局作用域:在脚本的其他地方都可以访问该变量

局部作用域:只能在声明的作用域内访问

 

局部变量的声明: Local  variable_name = value

 

脚本调试:

跟踪脚本执行结果:

在希望开始调试的地方插入 set -x

在希望结束调试的地方插入 set +x

 

 

---------------------------------------------------------------------

---------------------------------------------------------------------

Bash shell结构

1:shbang行:脚本的第一行,告之内核用哪个shell解释shell脚本,由#!加shell的完整路径组成

2:注释:#后面的为注释,可以在一行后

3:shell通配符或元字符:shell中字符意义比较特殊,如* ? [] > < 2> >> |等,防止这些字符被解释,则必须引用他们

4:局部变量:局部变量作用域在本shell中

5:全局变量:又称为环境变量,由内置的export命令创建,作用域在本shell及子shell中

6:提取变量值:$

7:参数:可以从命令行传递参数给脚本

8:命令替换:`A` $(A)  结果可以赋给一个变量或代替所在位置

---------------------------------------------------------------------

正则表达式

正则表达式是一种字符模式,在查找过程中匹配指定的字符,可以用特殊的元字符来控制它们,正则表达式都被置于两个正斜杠//之间

元字符:表达的是不同于字符本身的含义

本书中的元字符有两类:shell元字符、正则表达式元字符,它们各司其职,shell元字符是由shell解析的,就是下面所讲的文件名置换;正则表达式元字符是由各种执行模式操作的程序来解析的,如vi、grep、sed、awk

---------------------------------------------------------------------

正则表达式元字符:

^  行首定位

$  行尾定位

.  匹配单个字符

*  匹配0或多个重复的位于*前的字符

[]  匹配指定组字符中的任意一个 ;如[LlT]

[-] 匹配指定范围内的字符 ;如[x-yA-Z0-8]

[^] 匹配不在指定组内的字符

[^ - ]跟上面的相反

\  转移跟在后面的字符     #转义元字符,使之不被解释

\< 词首定位符     #定位以什么开头的词

\> 词尾定位符     #定位以什么结尾的词

---------------------------------------------------------------------

grep 家族

grep家族由grep、egrep、fgrep组成 

grep命令在文件中全局查找指定的正则表达式,并打印所有包含该表达式的行

一般格式:grep  [选项]   基本正则表达式  [文件]   

选项:-n 显示行号  -i 大小写不敏感  -w表达式作为词来查找,也就是不是一个词的一部分  -r 递归查找

基本正则表达式:可为字符串,如果为字符串,最好用“”如果是模式匹配,用‘’调用变量时,也应该使用“”

文件:可以为多个文件;如 A1 A2

常用举例子

grep -n “your” www    #文件www中查找含有your单词的行,且输出行号

grep -nw “your” www    #文件www中查找含以your做单词的行,且输出行号

grep -nr ‘[a-zA-Z0-9].you’ ./  #递归./ 中查找含有以a-z A-Z 0-9开始的单词的行,且输出行号

---------------------------------------------------------------------

find命令及xargs

find pathname -options [-print -exec -ok]

常用的options为name 此时记住要用引号将文件名模式引起来

find ./  “*org.txt”   #在当前目录及子目录中寻找以org.txt结尾的文件

find 找到文件后可用xargs对其操作

find ./  “*org.txt” | xargs ls

find ./  “*org.txt” | xargs grep “device”  #在结果中搜索device

---------------------------------------------------------------------

启动

系统启动后

->init->getty进程

->/bin/login  初始化环境,启动shell

->/bin/bash 执行/ect/profile,执行~/.bash_profile ~/.bash_login ~/.profile,执行~/.bashrc

->等待用户输入

---------------------------------------------------------------------

环境

一个进程的环境包括:变量等,它定义了可以从一个进程继承到下一个进程的特性

用户的shell配置定义在shell初始化文件中,bash shell有许多启动文件,这些文件可以执行source命令,对一个文件执行source命令会使得这个文件中的所有设置称为当前shell的一部分,也就是说不会创建子shell

登陆时,会执行~/.bash_profile文件执行source命令,如无,则source ~/.bash_login,若无,则source ~/.profile,这三个文件只能source一个,再source ~/.bashrc

~/.bashrc:包含特定的变量和别名,当一个新的bash shell启动或bash脚本启动时会自动执行source ~/.bashrc

在当前提示符下输入shell或bash启动的是子shell

/etc/bashrc:系统级的函数和别名可以在/etc/bashrc文件中设置,主提示符一般在这设置

~/.profile:是一个用户定义的初始化文件,它是被Bourne shell使用的,因此不能包括对bash shell的特定设置,运行bash时只有没找到其他文件时才source此文件,它允许用户定制和修改shell环境,也可以放应用程序的初始化

---------------------------------------------------------------------

source命令或dot命令(.)  和  ./命令

source命令式内置的bash命令,来自C shell

dot命令也就是. 来自Bourne shell

二者完全一样,都是一个脚本名作为参数,shell将在当前的shell环境中执行这个脚本,也就是不启动子shell,脚本中的设置的所有参数将成为当前shell环境的一部分。

./执行脚本时是创建子shell,脚本中的设置参数在脚本退出后就无效了。

 

例子:在家目录下的.bashrc中加入WANG=”ni hao”

然后. .bash.rc

此时执行内容为 echo “$WANG”的 脚本

 

如用 ./sh.sh 则无输出  #./执行时是在起一个子shell

如用source sh.sh或 . sh.sh则输出ni hao  #source是在本shell中执行

---------------------------------------------------------------------

bash shell元字符(通配符)

\  按字面含义解释后面的那个字符

&  在后台处理的进程

; 分隔命令

$  替换命令

*  匹配任意字符

?  匹配任意单个字符

[...] 匹配[]中的任意一个字符

[!...] 不匹配[]中!的任意一个字符

(cmds)  在子shell中执行命令

{cmds}  在当前shell中执行命令

---------------------------------------------------------------------

文件名置换

计算命令行时,shell会用元字符来缩写能够匹配某个特定字符组的文件名或路径名

将元字符展开为文件名或路径名的过程称为文件名替换或globbing

*    匹配文件(夹)名中的任意字符串

如:ls app*

cd cmdd*

?    匹配文件(夹)名中的任意一个字符

[...] 匹配[]中的任意一个字符

[!...] 不匹配[]中!的任意一个字符

如:ls [a-z]*.o   #匹配以a-z开头的已.o结尾的文件

ls [!0-9]*.o     #匹配以非0-9开头的已.o结尾的文件

---------------------------------------------------------------------

命令执行顺序

命令1 && 命令2  #命令1执行成功才会执行命令2

命令1 || 命令2  #命令1执行不成功才会执行命令2

用() 和 {}将命令结合在一起

 

 

---------------------------------------------------------------------

Bash shell变量

变量可分为两种:局部变量和环境变量

局部变量只在创建他们的shell中可用,环境变量作用域可以扩展到子shell中去

变量命名规则:可以是字母、0-9、下划线组成,必须以字母或下划线开头,其他字符都标志着变量名的终止,变量名大小写敏感

有两个内置命令可以用来创建变量,他们是declare、typset,其选项可以控制变量的设置方式,建议使用declare.

格式:declare  [选项]  变量=值

选项:-x  将变量设置成环境变量

      -r 将变量设置成只读变量

      -a 将变量当一个数组

---------------------------------------------------------------------

创建局部变量

1:变量赋值:变量=值    #注意等号周围不能有任何空白字符,如果想赋空,可以在等号后跟上换行符

2:内置命令declare  变量=值  #注意等号周围不能有任何空白字符,如果想赋空,可以在等号后跟上换行符

例子:

设置:round=world 或 round=”world nice”  #“”保护空白符

设置:declare round=world

输出:echo $round

设置:file.bak=”xxxx”   错,变量名中出现非法字符

local函数:函数内创建局部变量可以用local函数

---------------------------------------------------------------------

创建环境变量

环境变量,又被称为全局变量,作用域可以扩展到子shell中,通常环境变量用大写

环境变量是已经用export内置命令导出的变量,如果想设置环境变量,只需在设置变量时或赋值后使用export命令即可,declare 带-x也是设置环境变量

设置环境变量:

1:export 变量=值  #注意等号周围不能有任何空白字符,如果想赋空,可以在等号后跟上换行符

2:变量=值; export 变量    #注意等号周围不能有任何空白字符,如果想赋空,可以在等号后跟上换行符  

3:declare -x 变量=值  #注意等号周围不能有任何空白字符,如果想赋空,可以在等号后跟上换行符

---------------------------------------------------------------------

设置只读变量

只读变量是不能被重新定义或复位的特殊变量,但是如果使用declare定义的变量,可以重定义,不能复位

设置:name=”TOM”

readonly name   #设置只读

unset name  #不能复位

name=Jem  #不能重定义

设置:declare -r city=”shanghai”

unset city #不能复位

declare city=“beijin” #可以

---------------------------------------------------------------------

提取变量的值

在变量前加$

---------------------------------------------------------------------

复位变量

只要不被设置只读,局部变量和环境变量都可以用unset命令复位

格式:unset 变量名

---------------------------------------------------------------------

显示变量值

1:echo [选项]  变量   #echo将他的参数显示到标准输出上

选项:-e   #允许解释转义字符,如echo -e “\a”  \a转义为报警

如:echo The name is $NAME

echo -e “\a”  #报警

2:printf命令

---------------------------------------------------------------------

变量扩展修饰符

可以用一些专用修饰符来测试和修改变量,修饰符首先进行简单的条件测试,检查某变量是否已被设置,然后根据测试结果给变量赋值

${VA:-word}  #如变量VA已经设置且非空,则代入其值,否则代入word   临时替换默认值 

${VA:=word}  #如变量VA已经设置且非空,则代入其值,否则VA设置为word,代入VA永久替换默认值

${VA:+word}  #如变量VA已经设置且非空,则代入word,否则代入空值   临时替换默认值

${VA:?word}  #如变量VA已经设置且非空,则代入其值,否则输出word且从shell退出  基于默认值创建错误信息

 

注:不加冒号时,值为空也被认为已经设置

如:echo ${name-Joe}  #结果为空

echo ${name:-Joe}  #结果为Joe

echo ${namex:?”namex is undefined”}  

echo ${y?}  #如果y未设置,否则输出parameter null or not set

 

${VA:offset}  #获取变量VA值中位置从offset开始的字串

${VA:offset:length}  #获取变量VA值中位置从offset开始长度为length的字串  创建字串

如:var=notebook

echo ${var:0:4}   #结果note

---------------------------------------------------------------------

子串的变量扩展

模式匹配变量用来在串首或串尾截掉串的一部分,常用语删除路径名的头或尾

${变量%模式}  #将变量值的尾部与模式进行最小匹配,并将匹配到的部分删除

${变量%%模式} #将变量值的尾部与模式进行最大匹配,并将匹配到的部分删除

${变量#模式}  #将变量值的头部与模式进行最小匹配,并将匹配到的部分删除

${变量##模式} #将变量值的头部与模式进行最大匹配,并将匹配到的部分删除

${#变量}      #替换为变量中的字符个数,如果是*或@ ,长度则是位置参量的个数

如:

pathname=”/usr/bin/local/bin”

echo ${pathname%/bin*}  #结果/usr/bin/local/

echo ${pathname%%/bin*}  #结果/usr

echo ${pathname#/usr}  #结果/bin/local/bin

echo ${pathname##/usr}  #结果bin

echo ${#pathname}  #结果8

---------------------------------------------------------------------

位置参量

下面这组专用内置变量常称为位置参量,通常被shell脚本用来从命令行接受参数,或被函数用来保存传递给它的参数

$0  #指当前shell脚本的名称

$1-$9  #代表第一个到第9个位置参量

${10} ${11} #第10、11个位置参量,不用$10,$11

$#  其值为位置参量的个数,不含$0

$*  其值为所有的位置参量

$@  同$*

“$*”  其值为“$1 $2 $3”

“$@”  其值为“$1”“$2”“$3”

位置参数可以用set命令来设置、重置或复位

set --   #清除所有的位置参数

---------------------------------------------------------------------

其他特殊变量

$    #当前shell的pid

_    #当前sh的选项设置

?    #已执行的上一条命令的退出值

!    #最后一个进入后台的作用的pid

---------------------------------------------------------------------

引用

用来保护特殊的元字符不被解释和禁止参量扩展

引用有三种方式:反斜杠、单引号、双引号

需要引用的特殊元字符有:

;

&

()

{}

|

< > 

空格 tab 换行符

$ 

* [] ? 

1:单引号和双引号必须成对出现

2:单引号保护特殊元字符免受解释,双引号也能,但双引号允许处理变量替换字符$和命令替换字符``和\

3:双引号可以保护单引号,单引号可以保护双引号

4:如果有不匹配的引号,bash会出现次提示符

反斜杠:反斜杠用于引用(或转义)单个字符,使其免受解释。

单引号中的反斜杠不会被解释

如果在双引号中,反斜杠将保护$ ``和反斜杠免受解释

单引号:成对出现,保护所有元字符不被解释,打印双引号就必须用双引号将其括起来,或反斜杠转义

双引号:同单引号,但是允许进行变量替换$和命令替换``和\

 

简言之:单引号中什么都不被解释。双引号中$ `` \这三个会被解释,如不想解释用\转义

注意:``同$()是一样的,所以$(date)也会展开

---------------------------------------------------------------------

命令替换

命令替换的用处是将命令的输出结果赋给一个变量或将命令的输出结果代入命令所在位置

bash 允许使用两种格式:`命令`   和  $(命令)

执行扩展时,bash先执行命令,然后返回命令的标准输出,输出结果末尾的换行符都将被删除,如果不想删除,可用“”括起,如:echo “$(cal)”

如:echo “this is `date +%H`”   #this is 09

d=$(date)  #d存放命令date的结果

`命令`  \保留字面意思,除非后面跟$ ` \ 且``中$  \生效,可用\转义之,``中再有``的话必须用\转义

$(命令)  中的所有特殊字符都不会解释

命令替换可以被嵌套,``必须使用\`转义,如echo `basename \`pwd\` `  #其中转义后的`pwd`也是当做命令替换的,如:$(basename $(pwd))

---------------------------------------------------------------------

数组

bash 中可以创建一维数组,数组允许将一列词放到一个变量名中,可以用内置命令declare -a创建,或直接给变量名一个下标来创建,如x[0]=5   索引值为从0开始的整数,数组无上限,索引也不必是有序数。

取出数组中某个元素,语法为:${数组名[索引]}

创建数组:

declare 、local 、read-only内置命令也可以带-a选项来声明一个数组。read -a是读取一列词到数组中

declare -a va_name=(item1 item2 item3 ...)

declare -ar va = {item1 item2 item3 ...}

states=(ME [3]=CA [2]=CT)  #数组states第0元素为ME  3元素为CA  2元素为CT

declare -a nums=(45 33 100 65)

name=(TOM Dick Harry)

echo ${va_name[*]}   #输出数组所有元素

echo ${#va_name[*]}  #输出数组大小

取消定义:unset name 或 unset name[2]

---------------------------------------------------------------------

函数

bash函数用于在当前shell环境(不派生子shell)通过函数名执行一组命令,函数常常用于提高脚本的模块化程度。

函数可被重复调用。函数必须定义后才会被调用

定义函数:有两种格式声明一个bash函数

1:函数名后跟一对圆括号,再跟函数定义

function_name () { commands;commands; }

2:function关键词后跟函数名及函数定义,圆括号可选

function function_name { commands;commands;}

function function_name () { commands;commands;}

注意:命令之间用;隔开,最有一条命令必须以分号终结。花括号两侧空格是必须的。

传递给函数的任何参数被当做函数内的位置参数,一个函数的位置参数相对于函数来说是局部的

内置命令local允许在函数定义中创建局部变量

 

函数可以递归调用本身

函数中的命令是在当前shell环境下执行

全局函数:同变量一样,函数作用域可以扩展到子shell,只要用export -f全局化函数

export -f 函数名

说明:当函数命令列在单独的行,可以不用分号,最后一条还是需要。如果列在同一行,必须用分号隔开

如:function welcome { echo “Hi $1 and $2”; }

welcome tom joe  #传递参数给函数,结果为Hi tom and joe

set jane anna lizzy

echo $1 $2

jane anna

unset -f welcome #清除函数定义

 

列出和清除函数

declare -f #列出该shell中定义的所有函数及他们的定义

declare -F #同上,只列出函数名

unset -f   #清除函数定义

---------------------------------------------------------------------

管道

管道(pipe)将管道符左侧命令的输出作为到管道符右侧命令的输入,一条管道线可能不止一个管道

管道符用竖杠 | 表示,一般形式:命令1 | 命令2 | 命令3

---------------------------------------------------------------------

shell内置命令

shell中有许多内置到它的源码中的命令,shell无需到磁盘中定位他们,执行速度速度快很多。

常见内置命令如下:

:   #空命令

.   #在当前进程的环境下执行程序,同source

alias #命令重命名

unalias #删除重命名

bg  #将作业放到后台

cd 

exit

fg #将作业放到前台

jobs #列出放在后台的作业

set #设置选项和位置参数

unset #删除变量值或函数

declare

export

---------------------------------------------------------------------

Bash shell编程

shell脚本:当命令不在命令行上执行,而是通过一个文件执行时,该文件被称为shell脚本,脚本以非交互的方式运行。它先查找环境变量所指定的环境文件,通常为.bashrc,然后从该文件开始执行,然后才开始执行脚本中的命令

创建shell脚本的步骤:

1:第一行:#!/bin/bash

2:编写内容

shell脚本结构:由一组Unix/linux命令、bash shell命令、程序结构控制语句、注释 组成

3:增加执行权限  chmod +x files

---------------------------------------------------------------------

读取用户输入

read命令

read是一个内置命令,用于从终端或文件读取一个输入行,直到换行符,行尾的换行符被转换成一个空字符,如果后面未跟变量名,则读取的行被赋给内置变量REPLY,可以用read中断脚本的运行,直至输入回车键

read var                 #把读入的数据全部赋给var

read var1 var2 var3     #把读入一行的第一、二个词分别赋给var1、var2,其他的都赋给var3

---------------------------------------------------------------------

算术运算

整数运算(declare和let命令)

declare命令:可以用declare -i 定义整形变量,如果给一个整形变量赋一个字符串,则bash将把变量赋值为0,可以对已定义的整形变量执行算术运算(如果未定义为整形变量,内置的let命令也允许算术操作),如果给整形变量赋一个浮点数值,则bash报语法错误。数字可以用不同基数的数字表示

如:declare -i num

num=5 + 5 #错

num=5+5  #对

num=”5 + 5” #对

num=5.6 #错

 

declare -i  将列出所有已经设置的整形变量及其值

 

用不同的基数表示数字

格式:variable=base#number

如:declare -i x=017

x=2#101

x=8#17

let命令:let命令式bash shell内置命令,用来执行整形算术运算和数值表达式测试;可用(())表示let命令

let命令在执行算术运算时,不需要用$来展开变量,参数含有空格则用“”包含

如:let “i = i + 2”

---------------------------------------------------------------------

位置参数

用户可以通过命令行向脚本传递信息,跟在脚本名后的用空白符分隔的每个词都称为参数,可以在脚本中使用位置参数来引用命令行参数

shell环境中的位置参数作用域在本shell,shell脚本执行时跟在后面的参数相当于局部的位置参数,只对本脚本生效

如:脚本内容 echo “$1”

执行时 source sh.sh wang  #则输出wang

如果在命令行set hahaha

执行时 source sh.sh wang  #则输出wang

执行时 source sh.sh  #则输出hahaha

 

set命令与位置参数

可以用set命令设置或重设位置参数

带参数的set命令将重置位置参数,重置的是shell环境中的位置参数。清除所有位置参数,可以用set -- 命令

---------------------------------------------------------------------

条件结构和流程控制

条件结构能够根据某个特定的条件是否满足,来执行相应的任务

bash可以检测两种类型条件:命令成功或失败,表达式为真或假。在任何一种类型的测试中,都要使用退出状态,为0表示命令成功或表达式为真,非0表示命令失败或表达式为假。退出状态保存在状态变量?中
简言之:bash中执行命令成功与否和表达式为真或假都会改变退出状态变量?

 

 

内置命令test

单方括号的test命令:通常使用内置的test命令测试表达式的值,test命令可以被链接到方括号上,这样即可使用单独的test命令,也可通过把表达式用单括号括起来,来测试表达式的值

用test命令或[]测试表达式时,表达式中的shell元字符不会被解释。由于对变量进行单词分离,因此包含空白符的字符串必须用引号括起来

双方括号的test命令:用[[]]来测试表达式的值,其中对变量不进行单词分离,但可以通过元字符扩展进行模式匹配。包含空白符的字符串必须用引号括起来,如果一个字符串(不管含不含空白符)仅仅是在表达式中作为一个普通字符串,而不是一个模式的一部分,则必须用引号括起来

逻辑操作符&&、||代替了与test命令一起使用的-a -o选项

 

说明:test可以用来测试字符串、数字、文件。test命令中表达式等号两侧必须有空格。单方括号第一个方括号后必须有空格,表达式两侧必须有空格。

简言之:

[]的测试表达式:表达式可为字符串比较、数字比较、文件测试。表达式等号两侧必须有空格,方括号必须有空格。表达式中的shell元字符不会被扩展。对变量进行单词分离,所以包含空格时必须用引号括起来

[[]]的测试表达式:表达式可为字符串比较、数字比较、文件测试。表达式等号两侧必须有空格,方括号必须有空格。表达式支持shell元字符,如果为普通字符串,防止误认为模式,必须用双引号括起来。支持逻辑&& || ,对应test中的-a -o   ,其中对变量不进行单词分离,即使变量由多个词组成也不需要双引号括起来

 

let和带双圆括号的算术运算符(())

对于计算算术表达式,可以不用test而用let命令,let命令含有丰富的操作符,let命令等同(())

(())中的变量不需要使用$,操作符两侧必须空格,圆括号两侧必须空格

简言之:let命令更适合整形算术运算和数值表达式测试

---------------------------------------------------------------------

if命令

if 结构后面的命令可以是bash内置命令或可执行程序,如ls

bash shell中跟在if后面的则是一条或一组命令。

 

exit 命令和变量?  exit用于终止脚本并返回到命令行

检查空 检查变量的值是否为空时,必须用双引号把空括起来,否则test命令失败

 

if 命令

then

命令

elif 命令

then

命令

elif 命令

then

命令

fi

---------------------------------------------------------------------

null命令(shell内置命令)

:代替null命令

null命令不做任何事,只是返回退出状态0(注意没退出,不同于exit 0)

---------------------------------------------------------------------

case命令

格式:

case 变量 in

表达式1)

命令组

;;

表达式2)

命令组

;;

...

*)

命令组

;;

esac

变量的值逐一与表达式相比,符合则执行命令组,并跳到esac后继续执行

表达式可以使用shell通配符,还可以使用 | 将两值相或

---------------------------------------------------------------------

循环命令

bash shell提供三种类型的循环:for   while   until

---------------------------------------------------------------------

for命令

格式:

for  变量  in   词表

do

命令组

done

注:词表可以通过执行命令获得,如  for  W  in $(date)

词表支持shell元字符:如 for file in memo[1-5]  

注意词表是$* $@ “$*” “$@”时的区别

如果没有“in 词表”则相当于“ for 变量 in $* ”

如果命令行参数为*,则它表示当前目录下的所有文件。

---------------------------------------------------------------------

while命令

格式:

while 测试命令

do

命令(命令组)

done

注:while 后的测试命令可以用test 或let或其他

---------------------------------------------------------------------

until命令

同while命令,只不过相反,也就是until 后的命令执行不成功时执行命令组

---------------------------------------------------------------------

循环控制命令

shift命令

shift 命令将参数左移指定次数,如:shift 5,没给参数时移动一次,当不够移动时,输出信息。

shift常用于while循环中遍历位置参数列表时。

如:while (( $# > 0 ))

do

echo $1   #$1值不断变化

shift

done

 

ture命令:总是已状态0退出

 

break命令

break用于跳出循环,层数为跳出几层

continue命令 跳过本次循环,继续下次,参数为调到哪一层循环起点,默认为跳到最近一层

---------------------------------------------------------------------

I/O重定向 与 子shell

shell可通过管道或重定向将输入从文件输入改为从循环读取输入,输出到循环改为输出到文件

shell启动一个子shell来处理I/O重定向和管道,循环结束后,在循环中定义的所有变量对脚本的其余部分都是不可见的

重定向一般是到文件,管道一般是到命令

将循环的输出重定向到一个文件:bash循环的输出不仅可以送到标准输出,还可以通过管道送到文件

将循环的输出管道到一个命令

---------------------------------------------------------------------

后台执行循环

循环可以在后台执行,这样就可以继续往下执行其他程序而不必等待循环处理全部完成

---------------------------------------------------------------------

函数

函数在当前环境中运行,函数共享调用它的脚本中的变量,还允许给函数传递参数,可以使用local功能在函数内部创建局部变量

在函数中调用exit将退出整个脚本。

函数中的return语句,返回函数执行的最后一条命令的退出状态,或返回指定参数值

使用export -f把函数导出到子shell

使用declare -f可以列出所有的函数名及其定义

如果函数保存在其他一些文件中,可以用source和dot命令把他们载入到当前脚本中

函数可以递归调用,次数没限制

 

清除函数

从内存中清除某个函数,使用unset命令

unset 函数名

 

函数的参数和返回值

当前shell环境中的变量对函数是可见的,在函数中对环境所做的任何改动也是会对shell环境生效的。

函数可以使用位置参数向函数传递参数,位置参数是函数私有的,也就是说,函数对参数的操作不会影响在函数外使用的任何位置参数

内置local功能:函数私有的局部变量,在函数退出后随即消失。

内置return命令:用来退出函数并将控制回转到程序调用函数的位置,如果未给return赋值,则返回最后一条命令的退出状态,如果赋值了,则该值保存在变量?中。可以是0-255之间整数。

获取函数的结果可以用 命令替换,如:$(函数名)  `函数名`

函数可以定义在文件中,当需要时,调用source或dot命令跟上函数名就能激活文件中的函数,函数会被加载到shell内存空间

---------------------------------------------------------------------

调试

带-n选项的bash命令,能对脚本进行语法检查,但不去运行任何一条命令,如语法错误,则报错,否则不显示任何内容

最常见的调试脚本的手段是用带-x选项的set命令,或是调用带-x选项和脚本名为参数的bash

此时shell对脚本的每条命令处理过程为:先执行替换,接在显示,再去执行它。shell显示脚本中的行时,会在行首添加加号+

调试选项:

bash -x 脚本名  #回显   在变量替换之后,执行命令之前,显示脚本的每一行

bash -v 脚本名  #详细   在执行之前,按输入的原样打印脚本中的各行

bash -n 脚本名  #不执行 解释但不执行命令

set -x  #打开回显  跟踪脚本执行

set +x  #关闭回显  关闭跟踪功能

---------------------------------------------------------------------

调试shell脚本

风格问题

1:加注释

2:定义有意义的变量名

3:确保健壮性

错误类型

---------------------------------------------------------------------
————————————————
版权声明:本文为CSDN博主「wangchaoqun1997」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wangchaoqun1997/article/details/36480131

注明:所有转载内容皆直接从被转载文章网页的标题和内容的文本中复制而来

CC 4.0 BY-SA 版权协议网址:https://creativecommons.org/licenses/by-sa/4.0/deed.z

[工具] Shell 批量检测服务器 TCP 端口的联通状态 (telnet 版)

介绍

基本信息

作者:朱明宇
名称:批量检测服务器 TCP 端口的联通状态
作用:批量检测服务器 TCP 端口的联通状态,并将此服务器无法联通的端口存储到 $checklist 文件里

使用方法

1. 将此脚本和端口清单 $portlist 文件放在同一目录下
2. 端口清单 $portlist 每一个端口占用一行,格式为:<IP address corresponding to the port number to be connected>:<port number to connect>:<port functions>,并和此脚本放在同一目录下
3. 在此脚本的分割线内写入相应的内容
4. 给此脚本添加执行权限
5. 执行此脚本,并将要测试的服务器 IP 地址跟在脚本的后面,例:. <script> <server IP address 1> <server IP address 2> ……

脚本分割线里的变量

1. portlist=tcp_ports.txt #存放要测试的 TCP 端口的文件
2. checklist=tcp_ports_checklist.txt #存放测试结果的文件

注意

1. 此脚本执行前必须要先保证执行本脚本的用户能无密码 ssh 远程这些远程服务器
2. 此脚本会清空 $checklist
3. 执行此脚本前确保 telnet 命令已经安装
4. 执行此脚本可能有些慢

脚本

#!/bin/bash

####################### Separator ########################

portlist=tcp_ports.txt
checklist=tcp_ports_checklist.txt

####################### Separator ########################

echo  > $checklist
maxnum=`cat $portlist | wc -l`

for hosts in $*
do

        echo $hosts >> $checklist

        for i in `seq 1 $maxnum`
        do

                ips=`sed -n $[i]p $portlist | awk -F':' '{print $1}'`
                ports=`sed -n $[i]p $portlist | awk -F':' '{print $2}'`
                remarks=`sed -n $[i]p $portlist | awk -F':' '{print $3}'`

                ssh $hosts "(sleep 1;) | telnet $ips $ports 2>&1" | grep 'timed out' >> $checklist

                if [ $? == 0 ];then
                        echo "`sed -n $[i]p $portlist`" >> $checklist
                        echo >> $checklist
                fi

        done

        echo >> $checklist

done

[命令] Linux 命令 sed (显示或修改文件的行)

内容一:sed 基础

1.1 sed 的工作原理

1) 从第 1 行开始 1 行 1 行地读取文件里的内容
2) 每读取 1 行就将内容存入到 pattern space (模型空间) 里面,pattern space (模型空间) 里的内容默认会自动显示出来
3) 在 pattern space (模型空间) 中执行 sed 命令
4) 再显示 pattern space (模型空间) 中的内容然后将其清空
5) 之后重复以上操作再开始读取文件里的下 1 行
6) pattern space (模型空间) 里的内容可以存储到 hold space 里面

1.2 sed 格式

# sed <option> <parameter> <file>

1.3 sed 选项

1) -n 或 –quiet 或 –silent 仅显示处理后的内容,没处理的内容就不显示了,也就是仅显示非标准输出的内容
2) -i 实现数据的变更
3) -e <script> 或 –expression=<script> 这样可以一次匹配多个参数
4) -r 支持扩展正则
5) -f <script file> 或 –file=<script file> 以指定的脚本文件来处理输入的文件
6) -h 或 –help 显示帮助内容
7) -V 或 –version 显示版本信息


补充:
1) pattern space (模型空间) 相当于处理内容的流水线
2) hold space (保持空间) 相当于暂时存储内容的仓库

1.4 sed 的参数

1) p 先将每行的内容以标准输出的形式显示 1 遍,再将每行的内容以非标准输出的形式再显示 1 遍。不改变是否跳过下 1 行的属性,也就是如果前面有一个 N 参数,则跳过往下第 1 行接着执行往下第 2 行,如果前面没有任何参数,则直接执行往下第 1 行
2) P 先将每行的内容以非标准输出的形式再显示 1 遍,但是如果是 2 行组合在一起的话只显示第 1 行的内容,也就是只显示分行符 “/n” 之前的内容,再将每行的内容以标准输出的形式显示 1 遍。不改变是否跳过下 1 行的属性,也就是如果前面有一个 N 参数,则跳过往下第 1 行接着执行往下第 2 行,如果前面没有任何参数,则直接执行往下第 1 行
3) n 将每行的内容以标准输出的形式显示 1 遍,并提前读取下 1 行的内容,将下 1 行的内容覆盖当前行的 pattern space (模型空间),也就是下 1 行变成了当前行,再将当前行的内容以标准输出的形式显示一遍 (如果此时得到的内容是非标准的形式则不显示)。然后跳过往下第 1 行接着执行往下第 2 行。另外,如果往下 1 行不存在则直接停止所有命令,如果当前面的条件匹配时则放弃所有后面的命令
4) N 提前预读下 1 行,将下 1 行的内容添加到本行的 pattern space (模型空间) 后面,但是本行的内容和下 1 行的内容之间依旧存在分行符 “/n”,再将当前行的内容以标准输出的形式显示一遍。然后跳过下第 1 行接着执行往下第 2 行。另外,如果下 1 行不存在则以标准的形式显示当前 pattern space (模型空间) 里的内容并直接停止所有命令,如果当前面的条件不匹配时则放弃执行 N 命令直接执行后面的命令
5) = 以非标准输出的形式显示每 1 行是第几行,在行数后面以标准输出的形式显示行的内容
6) a 在某 1 行后面添加 1 行的内容
7) i 在某 1 行前面插入 1 行的内容
8) c 替换某 1 行的内容
9) g 将 hold space (保持空间) 中的内容拷贝到 pattern space (模型空间) 中,原来 pattern space (模型空间) 里的内容被清除
10) G 将 hold space (保持空间) 中的内容 append (添加) 到 pattern space (模型空间) 分行符 “/n” 后
11) h 将 pattern space (模型空间) 中的内容拷贝到 hold space (保持空间) 中,原来的 hold space (保持空间)里的内容被清除
12) H 将 pattern space (模型空间) 中的内容 append (添加) 到 hold space (保持空间) 分行符 “/n” 后
13) d 删除 pattern space (模型空间) 中的所有行,并执行下 1 行
14) D 删除 pattern space (模型空间) 中的第 1 行,也就是分行符 “/n” 之前的内容,如果删除后还有内容,则以此内容作为新的 1 行往下执行
15) s 替换某行的某些内容
16) y 替换某行的某些内容,和 s 的作用一样,但是功能没有 s 丰富
17) x 将当前 hold space (保持空间) 和 pattern space (模型空间) 内容互换,hold space (保持空间) 用来保存临时内容, pattern space (模型空间) 是本行的内容,注意 hold space (保持空间) 和 pattern space (模型空间) 的内容互换并不改变这些内容是第几行的,例如在 pattern space (模型空间) 里的内容是第 3 行的,则需要 3p
18) r 读取某一个文件里的内容
19) w 将内容写入一个文件
20) q 退出,前面可以跟退出的条件,后面可以跟退出代码
21) b 参数,设置标签分支,如果条件不成立,则对标签的位置进行跳转,如果标签省略了,则分支会一直到末尾
22) t 参数,设置标签分支,如果条件成立,则对标签的位置进行循环跳转,直到所有可进行的操作都完成后才停止,如果标签省略了则标签分支会一直到头部。在 t 和变迁之间的步骤是会循环执行的区域


注意:
1) 当多个参数进行组合时,多次标准输出最后会合并成只显示 1 次
2) 当多个参数进行组合时,当 1 个参数后面还有参数时,产生的输出不会直接显示而是传输给下 1 个参数进行处理,如果此次显示是非输出的形式,传递给下 1 个参数处理以后也会以非输出的形式输出

内容二:sed 使用 p 参数、P 参数、n 参数、N 参数和 = 参数显示某些行数据的案例

2.1 显示第 2 行的内容

# sed -n 2p test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.2 显示第 2 行到第 4 行的内容

# sed -n 2,4p test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.3 显示不是第 1 行的内容

# sed 1!p test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.4 显示最后 1 行的内容

# sed -n '$p' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

或者:

# sed 'N;D' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.5 显示包含 eternalcenter 的内容

# sed -n '/eternalcenter/p' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.6 显示以 eternalcenter 结尾的内容

# sed -n '/eternalcenter$/p' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.7 显示包含 eternalcenter 的行的行号,并将此行的上一行的内容,本行的内容和下一行的内容显示出来

# sed -n -e '/eternalcenter/{=;x;1!p;g;$!N;p;D;}' -e h test.txt


补充:
1) 当执行到包含 eternalcenter 内容的行时
2) 执行 /eternalcenter/ 判断当前包含 eternalcenter 内容则为真,如果为真,执行 = 参数,以非标准输出的形式传输每 1 行是第几行,在行数后面以标准输出的形式传输行的内容,此时这些内容存放在 pattern space (模型空间) 中并传输给 x 参数
3) 执行 x 参数,将当前 hold space (保持空间) 和 pattern space (模型空间) 内容互换,并传输给 p 参数。在执行 x 参数前 hold space (保持空间) 里的内容是由上一行执行 h 参数时保存的,所以执行 x 参数后 pattern space (模型空间) 的内容是上一行的内容。注意 hold space (保持空间) 和 pattern space (模型空间) 的内容互换并不改变这些内容是第几行的
4) 执行 1! 判断当前行不是第 1 行则为真,如果不是第 1 行,如果执行 p 参数,将此时 pattern space (模型空间) 的内容以标准输出的形式进行显示,再将 pattern space (模型空间) 的内容以非标准输出的形式传输给 g 参数
5) 执行 g 参数,将 hold space (保持空间) 中的内容拷贝到 pattern space (模型空间) 中,原来 pattern space (模型空间) 里的内容被清除,将此时的 pattern space (模型空间) 的内容传输给 N 参数
6) 执行 $! 判断当前行不是最后 1 行则为真,如果为真,执行 N 参数,将下一行的内容添加到本行内容的后面,但是两行之间存在分行符 “/n”,保存到 pattern space (模型空间),将此时的 pattern space (模型空间) 的内容以非标准输出的形式传输给 p 参数
7) 执行 p 参数,将 pattern space (模型空间) 里的内容以标准的形式显示,再将 pattern space (模型空间) 里的内容以非标准的形式传输给 D 参数
8) 执行 D 参数,删除 pattern space (模型空间) 中的第 1 行,也就是分行符 “/n” 之前的内容,并将此时 pattern space (模型空间) 里的内容传输给参数 h
9) 执行 h 参数,将 pattern space (模型空间) 中的内容拷贝到 hold space (保持空间) 中,原来的 hold space (保持空间)里的内容被清除
10) 这里的 test.txt 是要被 sed 操作的测试文件

2.8 显示包含从 2000 到 2007 点行

# sed -n '/2000/,/2007/p' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

(注意:test.txt 里必须要有包含 2000 内容和 2007 内容的行此命令才会生效)

2.9 显示从 2022-01-01 00:00:00 到 2022-02-22 00:00:00 的内容

# sed -n  '/2022-01-01 00:00:00/,/2022-02-22 00:00:00/p' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

(注意:test.txt 里必须要有包含 2022-01-01 00:00:00 内容和 2022-02-22 00:00:00 内容的行此命令才会生效)

2.10 显示从 2022-01-01 00:00:00 到最后的内容

# sed '/2019-01-01/,$' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

(注意:test.txt 里必须要有包含 2019-01-01 内容的行此命令才会生效)

2.11 显示总行数

# sed -n $= test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.12 以非标准输出的方式显示每 1 行是第几行,在行数后面以标准输出的方式显示行的内容,但是不显示第 1 行的内容

# sed -n '{=;1!p}' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.13 以非标准输出的方式显示每 1 行是第几行,在行数后面以标准输出的方式显示行的内容,但是不显示第 1 行的内容

# sed -n '{=;2!p}' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.14 添加行号并显示所有内容

# sed = test.txt | sed 'N;s/\n/:/'

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.15 显示奇数行

# sed -n 'p;n' test.txt


补充:
1) 执行第 1 行,执行 p 参数,将第 1 行的内容以标准输出的形式进行显示,再将第 1 行的内容以非标准输出的形式传输给 n 参数,再执行 n 参数,将第 1 行的内容以标准输出的形式进行显示 (但是这里传输给 n 参数的内容已经是非标准的形式了,所以这里变为以非标准的形式进行显示),再将第 1 行的内容被第 2 行的内容覆盖,再将第 1 行的内容以标准输出的形式显示 1 遍 (但是这里传输给 n 参数的内容已经是非标准的形式了,所以这里不再进行显示),再跳过第 2 行再直接执行第 3 行。前面以标准输出的形式显示的内容被 -n 选项屏蔽
2) 执行第 3 行,执行 p 参数,将第 3 行的内容以标准输出的形式进行显示,再将第 3 行的内容以非标准输出的形式传输给 n 参数,再执行 n 参数,将第 3 行的内容以标准输出的形式进行显示 (但是这里传输给 n 参数的内容已经是非标准的形式了,所以这里变为以非标准的形式进行显示),再将第 3 行的内容被第 4 行的内容覆盖,再将第 1 行的内容以标准输出的形式显示 1 遍 (但是这里传输给 n 参数的内容已经是非标准的形式了,所以这里不再进行显示),再跳过第 4 行再直接执行第 5 行。前面以标准输出的形式显示的内容被 -n 选项屏蔽
3) 以此类推
4) 当执行到最后 1 行时,按照之前的逻辑,如果是奇数行则显示当前行的内容,如果是偶数行则跳过
5) 这里的 test.txt 是要被 sed 操作的测试文件

或者:

# sed -n '$!N;P' test.txt


补充:
1) $! 作用是判断当前行不是最后 1 行时执行后面的命令
2) 执行第 1 行,执行 $! 判断当前行不是最后 1 行则为真,第 1 行不是最后 1 行,执行 N 命令,将第 2 行的内容添加到第 1 行的内容的后面,但是第 1 行和第 2 行之间存在分行符 “/n”,再将当前行的内容以标准输出的形式传输给 P 参数,再执行 P 命令,将此时第 1 行的内容以标准输出的形式进行显示,再将分行符 “/n” 之前的所有内容 (也就是最早是第 1 行的内容) 以非标准输出的形式显示出来,并跳过第 2 行再直接执行第 3 行。前面以标准输出的形式显示的内容被 -n 选项屏蔽
3) 执行第 3 行,执行 $! 判断当前行不是最后 3 行则为真,第 3 行不是最后 1 行,执行 N 命令,将第 4 行的内容添加到第 3 行的内容的后面,但是第 3 行和第 4 行之间存在分行符 “/n”,再将当前行的内容以标准输出的形式传输给 P 参数,再执行 P 命令,将此时第 3 行的内容以标准输出的形式进行显示,再将分行符 “/n” 之前的所有内容 (也就是最早是第 3 行的内容) 以非标准输出的形式显示出来,并跳过第 3 行再直接执行第 4 行。前面以标准输出的形式显示的内容被 -n 选项屏蔽
4) 以此类推
5) 当执行到最后 1 行时,因为已经没有下 1 行了,所以停止命令
6) 这里的 test.txt 是要被 sed 操作的测试文件

或者:

# sed 'n;d' test.txt


补充:
1) 执行第 1 行,执行 n 参数,将第 1 行的内容以标准输出的形式进行显示,再将第 1 行的内容被第 2 行的内容替代,再将被替代的内容传输给 d 参数,再执行 d 参数将第 2 行的内容删除掉。并跳过第 2 行直接执行第 3 行
2) 执行第 3 行,执行 n 参数,将第 3 行的内容以标准输出的形式进行显示,再将第 3 行的内容被第 4 行的内容替代,再将被替代的内容传输给 d 参数,再执行 d 参数将第 4 行的内容删除掉。并跳过第 4 行直接执行第 5 行
3) 以此类推
4) 当执行到最后 1 行时,因为已经没有下 1 行了,所以停止命令
5) 这里的 test.txt 是要被 sed 操作的测试文件

2.16 显示偶数行

# sed -n 'n;p' test.txt


补充:
1) 执行第 1 行,执行 n 参数,将第 1 行的内容以标准输出的形式进行显示,再将第 1 行的内容被第 2 行的内容替代,再将被替代的内容传输给 p 参数,再执行 p 参数,将第 2 行的内容以标准的形式显示,再将第 2 行的内容以非标准的形式显示。并跳过第 2 行再直接执行第 3 行。前面以标准输出的形式显示的内容被 -n 选项屏蔽
2) 执行第 3 行,执行 n 参数,将第 3 行的内容以标准输出的形式进行显示,再将第 3 行的内容被第 4 行的内容替代,再将被替代的内容传输给 p 参数,再执行 p 参数,将第 4 行的内容以标准的形式显示,再将第 4 行的内容以非标准的形式显示。并跳过第 4 行再直接执行第 5 行。前面以标准输出的形式显示的内容被 -n 选项屏蔽
3) 以此类推
4) 当执行到最后 1 行时,因为已经没有下 1 行了,所以停止命令
5) 这里的 test.txt 是要被 sed 操作的测试文件

2.17 显示所有行是第几行,行数后面跟行的内容

# sed = test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.18 显示所有行是第几行,只显示行数

# sed -n test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.19 显示包含 eternalcenter 内容的行是第几行

# sed -n '/eternalcenter/=' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.20 显示所有空行是第几行

# sed -n '/^$/=' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

2.21 合并相同的行,也就是相同行去重

# sort test.txt | sed '$!N;/^\(.*\)\n\1$/!P;D'

(
补充:
1) 执行 sort 将所有的行进行排序将相同的行排在一起,并结果输出给 sed
2) 执行第 1 行,执行 $! 判断当前行不是最后 1 行则为真,第 1 行不是最后 1 行,执行 N 参数,将第 2 行的内容添加到第 1 行的内容的后面,但是第 1 行和第 2 行之间存在分行符 “/n”,再将组合后的内容传输给 P 参数,再执行 /^(.*)\n\1$/! 判断当前行 \n 前面和后面不一样则为真,当为假时则直接不执行将内容传输给 D 参数,当为真时,执行 P 参数,先将第 1 行的内容以非标准输出的形式显示一遍,但是如果是 2 行组合在一起的话只显示第 1 行的内容,也就是只显示分行符 “/n” 之前的内容,再将第 1 行的内容以标准输出的形式传输给 D 参数,执行 D 参数,删除内容中的第 1 行,也就是分行符 “/n” 之前的内容,如果删除后还有内容,则以此内容作为新的 1 行往下执行第 2 行,如果删除后没有内容了,则跳过第 2 行直接执行第 3 行
3) 以此类推
4) 当执行到最后 1 行时,因为已经没有下 1 行了,所以停止命令
5) 这里的 test.txt 是要被 sed 操作的测试文件
)

内容三:sed 使用 a 参数在某 1 行后面添加数据的案例

3.1 在第 2 行后面添加 1 行 eternalcenter

# sed "2a eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

3.2 在 abc 那 1 行后面添加 1 行 eternalcenter

# sed "/abc/a eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

3.3 在 test.txt 文件里在以 a 开头的行后面添加 eternalcenter

# sed "/^a/a eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

3.4 在以 [a] 开头的行后面添加 eternalcenter

# sed "/^\[a\]/a eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

3.5 在最后 1 行后面添加 eternalcenter

# sed "$a eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

3.6 在 eternalcenter 这 1 行后面添加 eternalcentre、mingyuzhu 和 zhumingyu 3 行

# sed '/eternalcenter/a eternalcentre\nmingyuzhu\nzhumingyu' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

内容四:sed 使用 i 参数在某 1 行前面插入数据的案例

4.1 在第 2 行前面插入 1 行 eternalcenter

# sed "2i eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

4.2 在 abc 那 1 行前面插入 1 行 eternalcenter

# sed "/abc/i eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

4.3 在以 a 开头的行前面插入 eternalcenter

# sed "/^a/i eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

4.4 在以 [a] 开头的行前面插入 eternalcenter

# sed "/^\[a\]/i eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

4.5 在最后 1 行后面添加 eternalcenter

# sed "$a eternalcenter" test.txt

或者:

# sed "/$/a eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

内容五:sed 使用 c 参数替换某些行数据的案例

5.1 将第 2 行替换成 eternalcenter

# sed "2c\ eternalcenter" test.txt

或者:

# sed "2ceternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

5.2 将 abc 那 1 行替换成 eternalcenter

# sed "/abc/c\ eternalcenter" test.txt

或者:

# sed "/abc/ceternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

5.3 将以 a 开头的那 1 行替换成 eternalcenter

# sed "/^a/c\ eternalcenter" test.txt

或者:

# sed "/^a/ceternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

5.4 将以 [a] 开头的那 1 行替换成 eternalcenter

# sed "/^\[a\]/c\ eternalcenter" test.txt

或者:

# sed "/^\[a\]/ceternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

5.5 将以 eternalcentre 开头的那 1 行替换成 eternalcenter ALL=(ALL) NOPASSWD: ALL

# sed "/^eternalcentre/c\ eternalcenter\ ALL=\(ALL\)\ NOPASSWD:\ ALL" test.txt

或者:

# sed "/^eternalcentre/c\eternalcenter\ ALL=\(ALL\)\ NOPASSWD:\ ALL" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

内容六:sed 使用 d 参数删除某些行的案例

6.1 删除第 2 行

# sed 2d test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

6.2 删除包含 abc 的那 1 行

# sed /"abc"/d test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

6.3 删除以 a 开头的那 1 行

# sed /"^a"/d test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

6.4 删除以 [a] 开头的那 1 行

# sed /"^\[a\]"/d test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

6.5 删除所有被注释掉的行

# sed '/^#/d' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

6.6 删除第 2 行到第 4 行

# sed '2,4d' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

内容七:sed 使用 s 或者 y 参数替换某些行某些数据的案例

7.1 将所有行的第一个 eternalcentre 换成 eternalcenter

# sed 's/eternalcentre/eternalcneter/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.2 将所有行的所有 eternalcentre 换成 eternalcenter

# sed 's/eternalcentre/eternalcneter/g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.3 将包含 eternalcentre 的行换成 eternalcenter

# sed 's/.*eternalcentre.*/eternalcneter/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.4 将第 3 行的第 2 个 eternalcentre 换成 eternalcenter

# sed '3s/eternalcentre/eternalcenter/2' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.5 在所有行前面添加 eternalcenter

# sed 's/^/eternalcenter/g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

或者:

# sed 's/^/eternalcenter&/g test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.6 在所有行后面添加 eternalcenter

# sed 's/$/eternalcenter/g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

或者:

# s/$/&eternalcenter/g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.7 将第 2 行替换成 eternalcenter

# sed '2s/.*/eternalcenter/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.8 将最后 1 行替换成 eternalcenter

# sed '$s/.*/eternalcenter/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.9 将第 2 到第 3 行替换成 eternalcenter

# sed '2,3s/.*/eternalcenter/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.10 将 eternalcenter 前面的数据和后面的数据对调位置

# sed "s/^\(.*\)\(eternalcenter\)\(.*\)$/\3\2\1/" test.txt

或者:

# sed -r "s/(.*)(eternalcenter)(.*)/\3\2\1/" test.txt

或者:

# sed -r "s/(.*)((eternal)(center))(.*)/\5\2\1/" test.txt

(补充:这里以 (.*) 是 1,((eternal)(center)) 是 2,(eternal) 是 3,(center) 是 4,(.*) 是 5 为例,这里的 test.txt 是要被 sed 操作的测试文件)

7.11 所有行前后字符串互调

# sed -r 's/.*/&\n/g;:a;s/(.*)(.)\n(.*)/\1\n\3\2/g;ta;s/^\n//g' test.txt


补充:
1) 执行 s/.*/&\n/g 在每行行尾添加换行符 “\n”
2) 执行 a 参数和 ta 参数,将从 :a 到 ta 之间的内容以后面的操作作为循环条件只要还能继续执行就一直循环,每次执行 s/(.*)(.)\n(.*)/\1\n\3\2/g,将此时所有行换行符 “\n” 之前的内容里最后一个字母移动到所有行换行符 “\n” 之后到内容里最后一个位置
3) 执行 s/^\n//g 删除所有行的换行符 “\n”
4) 这里的 test.txt 是要被 sed 操作的测试文件

或者:

# sed -r 's/.*/&\n/g;:b;s/(.*)(.)\n(.*)/\1\n\3\2/g;tb;s/^\n//g' test.txt


补充:
1) 执行 s/.*/&\n/g 在每行行尾添加换行符 “\n”
2) 执行 b 参数和 tb 参数,将从 :b 到 tb 之间的内容以后面的操作作为循环条件只要还能继续执行就一直循环,每次执行 s/(.*)(.)\n(.*)/\1\n\3\2/g,将此时所有行换行符 “\n” 之前的内容里最后一个字母移动到所有行换行符 “\n” 之后到内容里最后一个位置
3) 执行 s/^\n//g 删除所有行的换行符 “\n”
4) 这里的 test.txt 是要被 sed 操作的测试文件

7.12 删除每行的第 1 个字符和最后 1 个字符

# sed 's/.//1;s/.$//' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.13 删除每行的第 2 个字符和最后 1 个字符

# sed 's/.//2;s/.$//' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.14 删除所有行的数字和空格

# sed -r 's/[0-9]//g;s/^( )+//g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.15 删除 1 行不是最后 1 行包含 eternalcenter 的行

# sed '/eternalcenter/N;s/.*\n//' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.16 将所有的大写字母都添加括号

# sed 's/[A-Z]/(&)/g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.17 在第 7 行和第 9 行前面添加 “#”

# sed '7,9s/^/#/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.18 删除第 1 个空格

# sed -r 's/( )(.*)/\2/'

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.19 在以打写字母开头,中间是小写字母,以数字结尾的行里在小写字母和数字之间插入 ok

# sed -r 's/^([A-Z]{1,})([a-z]{1,})([0-9]{1,})$/\1\2ok\3/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.20 在以打写字母开头,中间是小写字母,以数字结尾的行里在小写字母和数字之间插入 ok,并将数字替换到最前面

# sed -r 's/^([A-Z]{1,})([a-z]{1,})([0-9]{1,})$/\3 ok \1\2/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.21 删除 2022-01-01 字符里的横杠 “-”

# sed -r 's/([0-9]{1,})\-([0-9]{1,})\-([0-9]{1,})/\1\2\3/' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.22 将日期的年月日以空格分开

# echo 20220222 | sed -r 's/(....)(..)(..)/\1 \2 \3/'

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.23 合并上下行

# sed '$!N;s/\n/ /' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.24 将以反斜杠号 “\” 结尾的行与下 1 行合并在一起并以空格进行分隔 (只合并一次)

# sed '/\\$/N;s/\\\n/ /' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.25 将以反斜杠号 “\” 结尾的行与下 1 行合并在一起并以空格进行分隔 (能合并多少次就合并多少次)

# sed -e :a -e '/\\$/N;s/\\\n/ /; ta' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

7.26 若某行以 = 开头则合并到上 1 行并将等于号 “=” 替换成空格

# sed -e :a -e '$!N;s/\n=/ /;ta' -e 'P;D' num.txt 


补充:
1) 执行 :a 参数和 ta 参数,将从 :a 到 ta 之间的内容以后面的操作作为循环条件只要还能继续执行就一直循环,每次执行 $!N;s/\n=/ /,当不是最后 1 行时,将下 1 行的内容添加到本行后面,但是本行的内容和下 1 行的内容之间依旧存在分行符 “/n”,如果此时存在 \n=,则将其替换成空格,再将最后的结果以非标准输出的方式传输给 P 参数,执行 P 参数,将分行符 “/n” 之前的所有内容 (也就是最初此行的内容) 以非标准输出的形式显示,再将此时的所有内容以非标准输出的形式传输给 D 参数,执行 D 参数,删除前 1 行,也就是分行符 “/n” 之前的内容,如果删除后还有内容,则以此内容作为新的 1 行往下执行,否则跳过往下第 1 行接着执行往下第 2 行
2) 以此类推
3) 执行 s/^\n//g 删除所有行的换行符 “\n”
4) 这里的 test.txt 是要被 sed 操作的测试文件

7.27 行列转换,并且将第 1 行相同的列,行列转换以后合并,也就是行列转换以后没有第 1 列相互重复的行 (指定 1 列是 1 个 5 个字符长度的字节)

# awk '{for(i=1;i<=NF;i++){if(i in arr){arr[i]=arr[i]" "$i}else{arr[i]=$i}}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt | sort | sed -e :a -e '$!N;/^\(.....\).*\n\1.*/s/^\(.*\)\(\n\)\(.....\)\(.*\)/\1\4/g; ta'

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

7.28 行列转换,并且将第 1 行相同的列,行列转换以后合并,也就是行列转换以后没有第 1 列相互重复的行 (指定 1 列是以 .com 结尾)

# awk '{for(i=1;i<=NF;i++){if(i in arr){arr[i]=arr[i]" "$i}else{arr[i]=$i}}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt | sort | sed -e :a -e '$!N;/^\(.*\.com\).*\n\1.*/s/^\(.*\)\(\n\)\(.*\.com\)\(.*\)/\1\4/g; ta'

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

7.29 行列转换,并且将第 1 行相同的列,行列转换以后合并,也就是行列转换以后没有第 1 列相互重复的行 (指定 1 列是以空格 “ ” 结尾)

# > awk '{for(i=1;i<=NF;i++){if(i in arr){arr[i]=arr[i]" "$i}else{arr[i]=$i}}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt | sort | sed -e :a -e '$!N;/^\(.*\ \).*\n\1.*/s/^\(.*\)\(\n\)\(.*\ \)\(.*\)/\1 \4/g; ta'

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

内容八:sed 使用 h 参数、H 参数、g 参数、G 参数、d 参数、D 参数第案例

8.1 sed 的工作原理

1) 从第 1 行开始 1 行 1 行地读取文件里的内容
2) 每读取 1 行就将内容存入到 pattern space (模型空间) 里面,pattern space (模型空间) 里的内容默认会自动显示出来
3) 在 pattern space (模型空间) 中执行 sed 命令
4) 再显示 pattern space (模型空间) 中的内容然后将其清空
5) 之后重复以上操作再开始读取文件里的下 1 行
6) pattern space (模型空间) 里的内容可以存储到 hold space (保持空间) 里面


补充:
1) pattern space (模型空间) 相当于处理内容的流水线
2) hold space (保持空间) 相当于暂时存储内容的仓库

8.2 h 参数、H 参数、g 参数、G 参数、d 参数、D 参数的讲解

8.2.1 h 参数、H 参数、g 参数、G 参数、d 参数、D 参数的作用简介

1) g 将 hold space (保持空间) 中的内容拷贝到 pattern space (模型空间) 中,原来 pattern space (模型空间) 里的内容被清除
2) G 将 hold space (保持空间) 中的内容 append (添加) 到 pattern space (模型空间) 分行符 “/n” 后
3) h 将 pattern space (模型空间) 中的内容拷贝到 hold space (保持空间) 中,原来的 hold space (保持空间)里的内容被清除
4) H 将 pattern space (模型空间) 中的内容 append (添加) 到 hold space (保持空间) 分行符 “/n” 后
5) d 删除 pattern space (模型空间) 中的所有行,并执行下 1 行
6) D 删除 pattern space (模型空间) 中的第 1 行,也就是分行符 “/n” 之前的内容,如果删除后还有内容,则以此内容作为新的 1 行往下执行

8.2.2 h 参数、H 参数、g 参数、G 参数、d 参数、D 参数的功能示意图
P H     P H     P H
1    h  1 1  d    1

P H     P H     P H     P H
2 1  G  2 1  H  2 1  d    1
        1       1 2       2
                  1       1

P H     P H     P H
3 2  G  3 2  h  3 3
  1     2 1     2 2
        1       1 1

P H     P H
3 2  g  2 2
  1     1 1


补充:
1) 这里的 P 指的是 pattern space (模型空间)
2) 这里的 H 指的是 hold space (保持空间)
3) h 其实就是清空现在粘贴板里的内容然后重新复制
4) H 其实就是不清空现在粘贴板里的内容然后再再原来粘贴版的内容基础上再添加复制
5) g 其实就是替换粘贴
6) G 其实就是添加粘贴

8.3 h 参数、H 参数、g 参数、G 参数、d 参数、D 参数的使用案例

8.3.1 将第 1 行的数据添加到第 3 行后面
# sed -e '1h' -e '3G' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

8.3.2 将第 1 行的数据替换第 3 行
# sed -e '1h' -e '3g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

8.3.3 将第 1 行和第 2 行的数据添加到第 3 行后面
# sed -e '1h' -e '2H' -e '3G' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

8.3.4 将第 1 行和第 2 行的数据替换第 3 行
# sed -e '1h' -e '2H' -e '3g' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

8.3.5 将第 1 个以 a 开头的行的数据添加到第 3 行后面
# sed -e '/^a/h' -e '3G' 1.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

8.3.6 将第 1 个和第 2 个以 a 开头的行的数据添加到第 3 行后面
# sed -e '/^a/h' -e'/^a/H' -e '3G' 1.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

8.3.7 将 eternalcenter 的 eternal 和 center 以空格分开
# echo "eternalcenter" | sed -r 'H;s/(.{7}).*/\1/;x;s/.*(.{6})$/\1/;x;G;s/\n/ /'

(
1) 执行 H 参数,将 pattern space (模型空间) 中的内容 append (添加) 到 hold space (保持空间) 分行符 “/n” 后,并将此时的 pattern space (模型空间) 传输给 s 参数
2) 执行 s/(.{7})./\1/,将 pattern space (模型空间) 中的内容只保留前 7 个字符,并将此时的 pattern space (模型空间) 传输给 x 参数 3) 执行 x 参数,将 hold space (保持空间) 和 pattern space (模型空间) 内容互换,,并将此时的 pattern space (模型空间) 传输给 s 参数 4) 执行 s/.(.{6})$/\1/,将 pattern space (模型空间) 中的内容只保留后 6 个字符,并将此时的 pattern space (模型空间) 传输给 x 参数
5) 执行 x 参数,将 hold space (保持空间) 和 pattern space (模型空间) 内容互换,,并将此时的 pattern space (模型空间) 传输给 G 参数
6) 执行 G 参数,将 hold space (保持空间) 中的内容 append (添加) 到 pattern space (模型空间) 分行符 “/n” 后,并将此时的 pattern space (模型空间) 传输给 s 参数
7) 执行 s/(.{7}).*/\1/,将 pattern space (模型空间) 中的内容删除换行符 “\n”
)

8.3.8 显示九九乘法表
# seq 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) 执行 seq 9 命令,第 1 行输出 1,第 2 行输出 2,以此类推直到第 9 行,并将结果传输给 sed 命令
2) 执行 sed 的 H 参数,将 pattern space (模型空间) 中的内容 append (添加) 到 hold space (保持空间) 分行符 “/n” 后,并将此时的 pattern space (模型空间) 传输给 g 参数。当执行到第 1 行时,添加到 hold space (保持空间) 是 1,hold space (保持空间) 里的值是 \n1,当时执行到第 2 行时,添加到 hold space (保持空间) 是 2,hold space (保持空间) 里的值是 \n1\n2,依次类推
3) 执行 sed 的 g 参数,将 hold space (保持空间) 中的内容拷贝到 pattern space (模型空间) 中,原来 pattern space (模型空间) 里的内容被清除。当执行到第 1 行时,hold space (保持空间)里的值是 \n1,pattern space (模型空间) 里的值将是 \n1,显示的结果也会是 1,当执行到第 2 行时,hold space (保持空间)里的值是 \n1\n2,pattern space (模型空间) 里的值将是 \n1\n2,显示的结果也会是 \n1\n2,依次类推,并将结果传输给命令 awk
3) awk,将多行视为 1 行,会执行从 1 到此行列数次循环,变量 i 初始时的值为 1,每执行 1 次则变量 i 到值会加 1,每次循环时会显示:“此时变量 i 的值”*“此行的列数”=“此时变量 i 的值和此行的列数的乘积““当此时变量 i 的值等于此行的列数时则换行,否则的话则显示制表符的空格长度”。当执行到第 1 行时显示的是 1×1=1,当执行到第 2 行时显示的是 1×2=2 2×2=4,以此类推

内容九:sed 一次匹配多个参数的案例

在 abc 和 bbb 那两行后面添加 1 行 eternalcenter

# sed -e "/abc/a eternalcenter" -e "/^bbb/a eternalcenter" test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

内容十:sed r 参数和 w 参数读取和写入文件

10.1 将 test2.txt 文件里的内容添加到 test1.txt 文件里内容的第3 行后面

# sed '3r test2.txt' test1.txt

(补充:这里的 test1.txt 和 test2.txt 是要被 sed 操作的测试文件)

10.2 将 test1.txt 文件里内容的第三行保存到 test2.txt 中

# sed '3w test2.txt' test1.txt

(补充:这里的 test1.txt 和 test2.txt 是要被 sed 操作的测试文件)

内容十一:sed q 参数退出当前执行的内容

11.1 执行完第 5 行后退出

# sed '5 q' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

11.2 执行完包含 eternalcenter 的行后退出

# sed '/eternalcenter/ q' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

11.3 执行完第 3 行后退出,并将退出代码设置为 3

# sed '3 q 3' test.txt

(补充:这里的 test.txt 是要被 sed 操作的测试文件)

内容十二:sed b 参数和 t 参数设置标签分支

12.1 如果某行不包含 eternalcenter 则将此行中内容的 0 换成 1,否则将此行中内容的 0 换成 2

# sed '{/eternalcenter/b lable;s/0/1/;:lable}' test.txt


补充:
1) 在这里 lable 是标签
2) 这里的 test.txt 是要被 sed 操作的测试文件

或者:

# sed '/eternalcenter/ba;s/0/1/;b;:a' test.txt


补充:
1) 在这里 a 是标签
2) 这里的 test.txt 是要被 sed 操作的测试文件

12.2 如果某行包含 eternalcenter 则将此行中内容的 0 换成 1,否则将此行中内容的 0 换成 2

# sed '/eternalcenter/s/0/1/;t;s/0/2/' test.txt


补充:
1) 在这里的标签省略了,所以标签分支会一直到头部
2) 这里的 test.txt 是要被 sed 操作的测试文件

或者:

# sed -e :a -e '/eternalcenter/s/0/1/;ta;s/0/2/' test.txt


补充:
1) 在这里 a 是标签
2) 这里的 test.txt 是要被 sed 操作的测试文件

[命令] Linux 命令 grep (显示文本的行)

案例一:grep 使用 perl 正则表达式匹配

# grep -P '^root' /etc/passwd

或者:

# grep --perl-regexp '^root' /etc/passwd

(补充:这里以匹配 /etc/passwd 文件中开头为 root 的行为例)

案例二:grep 使用正则表达式匹配

# grep -e '^root' /etc/passwd

或者:

# grep --regexp=PATTERN '^root' /etc/passwd

(补充:这里以匹配 /etc/passwd 文件中开头为 root 的行为例)

案例三:grep 使用扩展正则表达式匹配

# grep -E '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# grep --extended-regexp '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# egrep '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 文件里含有 IP 地址的行为例)

案例四:grep 显示行号

4.1 显示某些关键字所在行行号

# egrep -n '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# egrep --line-number '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# egrep --line-buffered '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 文件里含有 IP 地址的行并显示行号为例)

4.2 显示哪些行号是空行

# egrep -n ^$ /etc/resolv.conf

或者:

# egrep --line-number ^$ /etc/resolv.conf

或者:

# egrep --line-buffered ^$ /etc/resolv.conf

(补充:这里以匹配 /etc/resolv.conf 里的空行并显示行号为例)

案例五:grep 取反匹配

5.1 取反不匹配某些关键字

# egrep -v '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# egrep --invert-match '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 文件里不含有 IP 地址的行并显示行号为例)

5.2 取反不显示空行

# egrep -v ^$ /etc/resolve.conf

或者:

# egrep --invert-match ^$ /etc/resolve.conf

(补充:这里以匹配 /etc/resolve.conf 文件里不为空的行为例)

5.3 取反不显示以井号 “#” 开头的行

# grep '^[^#]' /etc/resolve.conf

(补充:这里以匹配 /etc/resolve.conf 文件里不以井号 “#” 开头的行为例)

5.4 取反不显示以井号 “#” 或空格 “ ” 开头的行

# grep '^[^#| ]' /etc/resolve.conf

(补充:这里以匹配 /etc/resolve.conf 文件里不以井号 “#” 和空格 “ ” 开头的行为例)

案例六:grep 只显示匹配的部分

# egrep -o '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# egrep --only-matching '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 里的 IP 地址为例)

(注意:这里匹配后只显示 IP 地址,而不显示 IP 地址所在行里的其他内容)

案例七:grep 匹配时忽略大小写

# egrep -i '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# egrep --ignore-case '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 文件里含有 IP 地址的行并忽略大小写为例)

案例八:grep 统计匹配成功次数

# egrep -c '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

或者:

# egrep --count '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 文件里含有 IP 地址的行的数量为例)

案例九:grep 将匹配成功的部分自动添加颜色

# egrep --color=auto '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 文件里含有 IP 地址的行并自动添加颜色为例)

案例十:grep 将匹配成功的部分自动取消颜色

# egrep --color=no '([1-9][0-9]{0,2}\.){3}[1-9][0-9]{0,2}' /etc/sysconfig/network-scripts/ifcfg-ens192  

(补充:这里以匹配 /etc/sysconfig/network-scripts/ifcfg-ens192 文件里含有 IP 地址的行并取消自动添加颜色为例)

案例十一:grep 一次匹配多个参数

# grep -e root -e zhumingyu /etc/passwd

(补充:这里以匹配 /etc/passwd 文件中包含 root 或 zhumingyu 的行为例)

案例十二:grep 显示匹配内容附近的内容

12.1 显示匹配内容的前 10 行

# grep -a 10 eternalcenter test.txt

或者:

# grep --text 10 eternalcenter test.txt

(补充:这里以匹配 test.txt 文件中包含 eternalcenter 的行并显示其前 10 行为例)

12.2 显示匹配内容的后 10 行

# grep -b 10 eternalcenter test.txt

或者:

# grep --byte-offset 10 eternalcenter test.txt

(补充:这里以匹配 test.txt 文件中包含 eternalcenter 的行并显示其后 10 行为例)

12.3 显示匹配内容的前后 10 行

# grep -a -b 10 eternalcenter test.txt

或者:

# grep --text --byte-offset 10 eternalcenter test.txt

(补充:这里以匹配 test.txt 文件中包含 eternalcenter 的行并显示其前后 10 行为例)

案例十三:grep 安静匹配

# grep -q root /etc/passwd

或者:

# grep --quiet root /etc/passwd

或者:

# grep --silent root /etc/passwd
# echo $?
0


补充:
1) 这种方法主要用于在 Shell 脚本中,随后使用 if 判断判断匹配是否成功
2) 这里以匹配 /etc/passwd 中是否包含 root 用户为例

[命令] Linux 命令 awk (显示文本的列)

内容一:awk 格式

# awk <option> '<condition>{<command>}' <file>

或者:

# awk <option> ' BEGIN{<option> <command>} {<command>} END{<command>}' <file>


补充:在这里
1) BEGIN{ } 里的指令只执行 1 次,并且是最先执行
2) { } 里的指令会一行行地执行文件里的内容
3) END{ } 里指令只执行 1 次,并且是最后执行

内容二:awk 选项

2.1 awk 常用选项

2.1.1 awk 的 -F 选项

awk 默认是以空格分割列,但是也可以通 -F 参数指定分割每一列的符号

2.1.2 awk 的 -v 选项

将变量导入 awk 中,并可以通过 ‘{print (<variable>)}’ 将变量显示出来

2.2 awk 的 RS、ORS、FS、OFS 选项

2.2.1 awk 的 RS、ORS、FS、OFS 选项作用

1) RS 全名为 Record Separator (输入记录分隔符)
2) ORS 全名为 Output Record Separate (输出记录分隔符)
3) FS 全名为 Field Separator (输入字段分隔符)
4) OFS 全名为 Output Field Separator (输出字段分隔符)

2.2.2 RS、ORS、FS、OFS 的常用值

1) RS=”” 每 1 次读取 1 个段落,默认情况下在 awk 中,把没有空行分割的多行会被定义为 1 个段落
2) RS=”\0″ 一次性读取所有内容,但是前提是 \0 字符不能在文件中存在
3) RS=”^$” 一次性读取所有内容
4) RS=”\n+” 按行读取内容,并且忽略空行

2.3 awk 的 sub、gsub 选项

2.3.1 sub 和 gsub 的区别

1) sub 只匹配第一个字符串,类似于 sed ‘s//’
2) gsub 匹配所有字符串,类似于 sed ‘s//g’

2.3.2 sub 和 gsub 的格式
2.3.2.1 sub 的格式
# sub (<original string or regular expression>, <substitution string>):
# sub (<original string or regular expression>, <substitution string>, <target string>)
2.3.2.2 gsub 的格式
# gsub (<original string or regular expression>, <substitution string>):
# gsub (<original string or regular expression>, <substitution string>, <target string>)

内容三:awk 的常用内置变量

1) $0 文本当前行的全部内容
2) $1 文本的第 1 列
3) $2 文本的第 2 列
4) $3 文本的第 3 列

(补充:“$” 号后面的数字以此类推)

5) NR 文本当前行的行数,也就是目前处于第几行
6) NF 文本当前行的列数,也就是目前一共有几列
7) $(NF-1) 文本倒数第 1 列
8) $(NF-2) 文本倒数第 2 列
9) $(NF-3) 文本倒数第 3 列
10) , 代表空格
11) \t 代表 tab 制表位,就是比一个空格更多的空格
12) FILENAME 当前文件名
13) IGNORECASE 是否忽略大小写,当 IGNORECASE=1 时则忽略大小写

内容四:awk 基础显示的案例

4.1 显示第 1 列和第 3 列,但中间没有空格

# awk '{print $1,$3}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

4.2 显示第 1 列和第 3 列

# awk '{print $1,$3}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

4.3 显示第 1 列和第 3 列,但中间有很长的空格空间

# awk '{print $1,$3}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

4.4 显示倒数第 2 列

# awk '{print $(NF-2)}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

4.5 显示每 1 行的行号和列数

# awk -F: '{print NR,NF}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

4.6 显示前 1 个命令结果的第 5 列

# df -h | awk '{print $5}'

4.7 显示前 1 个命令最后 1 行结果的第 5 列

# df -h / | tail -1 | awk '{print $5}'

或者:

# df -h / | awk 'END{print $5}'

4.8 显示第 2 列到最后 1 列

# cat test.txt | awk '{$1="";print}' | sed -r 's/( )(.*)/\2/'


补充:
1)这里的 /etc/passwd 是要被 awk 操作的测试文件
2)这里的 $1=”” 是将第 1 列的内容变为空
3)这里的 print 作用相当于 print $0

4.9 显示每行的行数、列数、第 1 列和最后 1 列,并且将冒号 “:” 视为分割符

# awk -F: '{print NR,NF,$1,$NF}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

4.10 显示总行数减 1 的值

# awk 'END{print --NR}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

4.11 显示总行数加 1 的值

# awk 'END{print ++NR}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

4.12 显示最后 1 行第 1 列字符的长度

# echo eternalcenter | awk 'END{print length($1)}'

4.13 显示第 1 列,并且将冒号 “:” 视为分割符

# awk -F: '{print "\047 $1 \047"}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

(注意:这里的 \047 代表的是单引号 “’”)

4.14 显示第 2 列的第 1 到第 3 个字符

# awk '{print substr($2,1,3)}' test.txt

或者:

# awk '{$a=substr($2,1,3);print $a;}'

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

(注意:在 awk 当中字符索引 substr 都是从第 1 个开始的,不是从第 0 个开始的)

4.15 显示第 2 列的第 1 到第最后 1 个字符

# awk '{print substr($2,1,)}' test.txt

4.16 将第 1 列以横杠 “-” 作为分隔符进行拆分,并将拆分的内容一行行地显示

# echo eternal-center-zmy | awk '{split ($0,a,"-");for (i in a) print a[i]}'

4.17 自定义列并显示第 2 列 (不包含多余的空格)

# awk 'BEGIN{FIELDWIDTHS="2 2:4 2:3"}{print $2}' test.txt


补充:
1) FIELDWIDTH 函数的作用是:按照字符的数量划分列,有 FS 的话也会覆盖 FS 覆盖划分列的方式
2) 这里的 FILEDWITHS=”2 2:4 2:3″ 代表第 1 列有 2 个字符,相隔 2 个字符后第 2 列开始有 4 个字符,再相隔 2 个字符后第 3 列开始有 3 个字符
3) 这里的 test.txt 是要被 awk 操作的测试文件

或者:

# awk -vFIELDWIDTHS="2 2:4 2:3" '{print $2}' test.txt


补充:
1) FIELDWIDTH 的作用是:按照字符的数量划分列,有 FS 的话也会覆盖 FS 覆盖划分列的方式
2) 这里的 FILEDWITHS=”2 2:4 2:3″ 代表第 1 列有 2 个字符,相隔 2 个字符后第 2 列开始有 4 个字符,再相隔 2 个字符后第 3 列开始有 3 个字符
3) 这里的 test.txt 是要被 awk 操作的测试文件

4.18 自定义列并显示第 2 列 (包含多余的空格)

# awk 'BEGIN{FIELDWIDTHS="2 6 5"}{print $2}' test.txt


补充:
1) FIELDWIDTH 函数的作用是:按照字符的数量划分列,有 FS 的话也会覆盖 FS 覆盖划分列的方式
2) 这里的 FILEDWITHS=”2 6 5″ 代表第 1 列有 2 个字符,第 2 列开始有 6 个字符,第 3 列开始有 5 个字符
3) 这里的 test.txt 是要被 awk 操作的测试文件

或者:

# awk -vFIELDWIDTHS="2 6 5" '{print $2}' test.txt


补充:
1) FIELDWIDTH 函数的作用是:按照字符的数量划分列,有 FS 的话也会覆盖 FS 覆盖划分列的方式
2) 这里的 FILEDWITHS=”2 6 5″ 代表第 1 列有 2 个字符,第 2 列开始有 6 个字符,第 3 列开始有 5 个字符
3) 这里的 test.txt 是要被 awk 操作的测试文件

4.19 自定义每 1 列之间的间距 (保留原来的行结构)

# awk '{ printf "%-9s %-8s %-8s\n", $1, $2, $3 }' test.txt


补充:
1) 这里以设置第 1 列和第 2 列之间间隔 9 个空格,第 2 列和第 3 列之间间隔 8 个空格为例
2) 这里的 test.txt 是要被 awk 操作的测试文件

4.20 自定义每 1 列之间的间距 (所有行合为 1 行)

# awk '{ printf "%-4s %-8s %-6s", $1, $2, $3 }' test.txt


补充:
1) 这里以设置第 1 列和第 2 列之间间隔 9 个空格,第 2 列和第 3 列之间间隔 8 个空格,且每行之间间隔 6 个空格为例
2) 这里的 test.txt 是要被 awk 操作的测试文件

4.21 将冒号 “:” 视为分割符

# awk -F: '{print $1,$7}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

4.22 将冒号 “:” 和句号 “.” 视为分割符

# awk -F [:.] '{print $1,$10}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

4.23 将逗号 “,” 作为分隔符但是处于两个双引号 “””” 之间的逗号 “,” 不作为分隔符,并显示其中的第 2 列 (可以将包含特定符号的多列示为 1 列)

# awk 'BEGIN{FPAT="[^,]+|\".*\""}{print $2}' test.txt


补充:
1) FPAT 函数的作用是:使用正则表达式进行匹配,将匹配成功的部分作为列
3) 这里以不包含逗号 “,” 和两个双引号 “” 包括两个双引号 “” 里的内容作为列
2) 这里的 test.txt 是要被 awk 操作的测试文件

4.24 当第 2 列出现多个相同的内容时只显示第 1 次出现的行 (也就是以第 2 列为基准进行去重)

# awk '{arr[$2]=arr[$2]+1;if(arr[$2]==1){print}}' test.txt

或者:

# awk '{arr[$2]++;if(arr[$2]==1){print}}' test.txt

或者:

# awk '{++arr[$2];if(arr[$2]==1){print}}' test.txt

或者:

# awk '!arr[$2]++{print}' test.txt


补充:
1) 当把 arr[$2] 放到大括号 “{}” 外面时它自身就是 1 个判断,当 arr[$2] 第 1 次遭遇某个内容时,arr[$2] 此时的值是 0 ,然后才进行 ++ (也就是加 1),而当 arr[$2] 的值是 0 时,给他取反的 !arr[$2] 就是布尔真,而布尔为真时就执行 {print}
2) 当后来 arr[$2] 不是第 1 次遭遇某个内容时,arr[$2] 的值就不是 0 了,而给他取反的 !arr[$2] 就是布尔假了,也就不会再执行后面的 {print}
3) 以此类推
4) 这里的 test.txt 是要被 awk 操作的测试文件

4.25 显示所有非空的行

# ifconfig | awk 'BEGIN{RS=""}{print}'

4.26 显示第 1 个段落

# ifconfig | awk 'BEGIN{RS=""}NR==1{print}'

4.27 显示 IP 地址

# ifconfig | awk '/inet/&&!($2~/^127/){print $2}'

或者:

# ifconfig | awk 'BEGIN{RS=""};!/127.0.0.1/{print $6}'

或者:

# ifconfig | awk 'BEGIN{RS="";FS="\n"}!/127.0.0.1/{$0=$2;FS=" ";$0=$0;print $2}'


补充:
1) 执行 RS=”” 每次读取 1 个段落
2) 执行 !/lo/ 不读取带 lo 的段落
3) 执行 FS=”\n” 将换行符 “\n” 设置为分隔符
4) 执行 !/127.0.0.1/ 只选择不带 127.0.0.1 的段落
5) 执行 $0=$2 将此段落里的内容换成第 2 行的内容 ($0 代表所有内容),设置了 FS 之后,需要重新给 $0 赋值以后才能让 FS 的新值生效
6) 执行 FS=” ” 将空格 “ ” 设置为分隔符
7) 执行 $0=$0 让所有内容等于所有内容 ($0 代表所有内容),设置了 FS 之后,需要重新给 $0 赋值以后才能让 FS 的新值生效
8) 执行 print $2 打印第 2 列

或者:

# ip a s | awk '/[1-2]?[0-9]{0,2}\.[1-2]?[0-9]{0,2}/&&!/127.0.0.1/{print $2}'

4.28 显示从包含 1.eternalcenter.com 的行到包含 5.eternalcenter.com 的行的所有内容

# awk '~/1.eternalcenter.com/,~/5.eternalcenter.com/' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

4.29 显示正数从 [mysql] 的行开始到下 1 个中括号 “[]” 出现前出现的所有内容

# vim awk.txt

创建以下内容:

index($0, "[mysql]"){
    print
    while( (getline var) > 0 ){
        if(var ~ /\[.*\]/){exit}
        print var
    }
}
# awk -f awk.txt /etc/my.cnf


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 执行 index($0, “[mysql]”),搜索 $0 中内容包含 [mysql] 的行 ($0 代表所有内容)
3) 执行 print 将匹配到的这 1 行显示出来
4) getline 有 3 类返回值,当返回值大于 0 时,表示已经读取到了内容。当返回值等于 0 时,表示没有读取到内容,第一行就是 EOF 结尾。当返回值小于 0 时,则代表没读取内容报错
5) 执行 while( (getline var) > 0 ){print var},从 [mysql] 这 1 行往下第 1 行开始往下 1 行 1 行地读取内容,并形成循环,每读取有 1 行,就将内容赋值给变量 var,且 getline 的返回值就为 1,确定 (getline var) 的返回值大于 0 时将 var 的内容显示出来
6) 执行 if(var ~ /[.*]/){exit} 如果匹配到变量 var 里的内容包含中括号了 “[]” 则显示出来
7) 这里的 /etc/my.cnf 是要被 awk 操作的测试文件

或者:

# echo '[mysql]' ; awk 'index($0, "[mysql]"){while((getline var)>0){if(var ~/\[.*\]/){exit} print var}}' /etc/my.cnf


补充:
1) 执行 echo ‘[mysql]’ 显示 echo ‘[mysql]’
2) 执行 index($0, “[mysql]”),搜索 $0 中内容包含 [mysql] 的行 ($0 代表所有内容)
3) getline 有 3 类返回值,当返回值大于 0 时,表示已经读取到了内容。当返回值等于 0 时,表示没有读取到内容,第一行就是 EOF 结尾。当返回值小于 0 时,则代表没读取内容报错
4) 执行 while( (getline var) > 0 ){print var},从 [mysql] 这 1 行往下第 1 行开始往下 1 行 1 行地读取内容,并形成循环,每读取有 1 行,就将内容赋值给变量 var,且 getline 的返回值就为 1,确定 (getline var) 的返回值大于 0 时将 var 的内容显示出来
5) 执行 if(var ~ /[.*]/){exit} 如果匹配到变量 var 里的内容包含中括号了 “[]” 则显示出来
6) 这里的 /etc/my.cnf 是要被 awk 操作的测试文件

4.30 将上斜号加星号 “/*” 和星号加上斜号 */ 里的内容视为注释,并省略掉里面的内容显示其他内容

# vim awk.txt

创建以下内容:

index($0, "/*"){
    #if in the same line with */
    if (index ($0,"*/")){
        print gensub("^(.*)\\/\\*.*\\*\\/(.*)$","\\1\\2","g",$0)
     }
 
    else {

    #if not in the same line with  */

    #display the content before /*
    print gensub("^(.*)/\\*.*","\\1","g",$0)

    #alaways read until encountered */
        while( (getline var) > 0 ){
            if(index(var,"*/")){
                print gensub("^.*\\*/(.*)","\\1","g",var)
                break
            }
        }
    }
}

!index($0, "/*"){
    print
}
# awk -f awk.txt test.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 这里的 test.txt 是要被 awk 操作的测试文件
3) gensub 函数的格式是:gensub(“”,””,”g”,),作用是:将字符串中的某些字符进行替换
4) getline 有 3 类返回值,当返回值大于 0 时,表示已经读取到了内容。当返回值等于 0 时,表示没有读取到内容,第一行就是 EOF 结尾。当返回值小于 0 时,则代表没读取内容报错
5) 执行 while( (getline var) > 0 ){print var},从 [mysql] 这 1 行往下第 1 行开始往下 1 行 1 行地读取内容,并形成循环,每读取有 1 行,就将内容赋值给变量 var,且 getline 的返回值就为 1,确定 (getline var) 的返回值大于 0 时将 var 的内容显示出来

4.31 将 2 段分别包含 i-order 内容和 fail 内容并以中括号 “[” 结尾的段落视为 1 段并显示

# vim awk.txt

添加以下内容:

BEGIN{
    RS="]\n"
    ORS=RS
}

{
    if($0 ~ /ok/ && prev ~ /order/){
        print prev
        print $0
    }
    prev = $0
}
# awk -f awk.txt test.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 这里的 test.txt 是要被 awk 操作的测试文件

4.32 显示被执行的文件 test.txt

# awk '{print FILENAME}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

内容五:awk 替换显示的案例

5.1 将第 1 列为 root 的行的第 3 列替换成 “1” 后显示,并且将冒号 “:” 视为分割符

# awk -F: '$1=="root"{ $3 = '1';print  }' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

5.2 将第 4 列里的横杠 “-” 替换成空值后显示

# echo "a- b c 2022-01-01 a:d" | awk 'gsub(/-/,"",$4)'

5.3 统计第 4 列里的横杠 “-” 的次数并把第 4 列替换统计后的次数后显示

# echo "z m 7 2022-01-01 a:d" | awk '$4=gsub(/-/,"",$4)'

5.4 统计此行中横杠 “-” 出现的次数并将第 4 列替换统计后的次数后显示

# echo "z- m y 2021-11-11 z:y" | awk '$4=gsub(/-/,"")'
z m y 3 z:y

5.5 将全部横杠 “-” 替换成空值后显示

# echo "z m y z-m-y 2022-01-01 z:y" | awk 'gsub(/-/,"")'
z m y zmy 20211111 z:y

或者:

# echo eternal-center-zmy | awk 'BEGIN{ FS="-";}{print $1,$2,$3}'
eternal center zmy

或者:

# echo eternal-center-zmy | awk '{split ($0,a,"-");print a[1],a[2],a[3]}'
eternal center zmy

5.6 将全部横杠 “-” 和冒号 “:” 替换成空值后显示

# echo "a- b c 2022-01-01 a:d" | awk '{gsub(/-|:/, "");print}'

5.7 将全部横杠 “-” 替换成 3 个星号 “***” 后显示

# echo eternal-center-zmy | awk 'BEGIN{ FS="-";OFS="***"}{print $1,$2,$3}'
eternal***center***zmy

5.8 给每 1 行前面添加行号后显示

# cat test.txt 
eternalcenter.com-ec-myz
eternalcentre.com-ec-myz
eternalcenter.org-ec-myz
eternalcentre.org-ec-myz

# awk 'BEGIN{ OFS="." }{ print NR,$0 }' test.txt 
1.eternalcenter.com-ec-myz
2.eternalcentre.com-ec-myz
3.eternalcenter.org-ec-myz
4.eternalcentre.org-ec-myz

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.9 在 a b c d 中的 b 后面插入 e f g

# echo "a b c d" | awk '{$2=$2" e f g";print}'

(补充:修改字段或 NF 的值后 $0 会进行重建并进行联动效应)

5.10 去除字符串中多余的空格,每个字节只用 1 个空格隔开

# echo "a     b c        d" | awk '{$2=$2;print}'

(补充:OFS 默认等于一个空格 “ ”,这里修改了 $2 的值以后, awk 会根据 OFS 的值重建 $0 ($0 代表所有内容),它会去使用 OFS 的值去链接各个字段)

5.11 去除字符串中多余的空格,每个字节使用多个个空格隔开

# echo "a     b c        d" | awk 'BEGIN{OFS="\t"}{$1=$1;print}'

(补充:OFS 默认等于 1 个空格 “ ”,在这里先试将 OFS 的值修改成了 “\t”,再修改了 $2 的值以,此时 awk 会根据 OFS 的值重建 $0 ($0 代表所有内容),它会去使用 OFS 的值去链接各个字段)

5.12 以竖杠 “|” 作为分割符将第 2 列的数字去掉后显示

# cat test.txt 
eternalcenter.com|201901ec|com
eternalcentre.com|201902ec|com
zhumingyu.com|201903zmy|com
mingyuzhu.com|201904myz|com
eternalcenter.org|201905ec|org
eternalcentre.org|201906ec|org


# awk -F '|' 'BEGIN{ OFS="|" }{sub(/[0-9]+/,"",$2);print$0}' test.txt 
eternalcenter.com|ec|com
eternalcentre.com|ec|com
zhumingyu.com|zmy|com
mingyuzhu.com|myz|com
eternalcenter.org|ec|org
eternalcentre.org|ec|org

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

或者:

# cat test.txt 
eternalcenter.com|201901ec|com
eternalcentre.com|201902ec|com
zhumingyu.com|201903zmy|com
mingyuzhu.com|201904myz|com
eternalcenter.org|201905ec|org
eternalcentre.org|201906ec|org

# awk -F '|' -v OFS='|' '{sub(/[0-9]+/,"",$2);print $0}' test.txt 
eternalcenter.com|ec|com
eternalcentre.com|ec|com
zhumingyu.com|zmy|com
mingyuzhu.com|myz|com
eternalcenter.org|ec|org
eternalcentre.org|ec|org

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.13 将句号 “.” 替换成换行符后显示

# cat test.txt 
m.y.z

# awk 'BEGIN{RS=".";}{print $0}' test.txt 
m
y
z

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.14 将换行符替换成句号 “.” 后显示

# cat test.txt 
m
y
z


# awk 'BEGIN{ORS=".";}{print $0}' test.txt 
m.y.z.

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.15 awk 行列转换 (每 1 行最前面有 1 个空格)

# awk '{for(i=1;i<=NF;i++){arr[i]=arr[i]" "$i}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.16 awk 行列转换

# awk '{for(i=1;i<=NF;i++){if(i in arr){arr[i]=arr[i]" "$i}else{arr[i]=$i}}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.17 行列转换,并且将第 1 行相同的列,行列转换以后合并,也就是行列转换以后没有第 1 列相互重复的行 (多个第 1 列相同的行,后面的行会在去除第 1 列后将剩余的列加在上 1 行的后面)

# awk '{for(i=1;i<=NF;i++){if(i in arr){arr[i]=arr[i]" "$i}else{arr[i]=$i}}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt | sort | sed -e :a -e '$!N;/^\(.....\).*\n\1.*/s/^\(.*\)\(\n\)\(.....\)\(.*\)/\1\4/g; ta'

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.18 行列转换,并且将第 1 行相同的列,行列转换以后合并,也就是行列转换以后没有第 1 列相互重复的行 (指定 1 列是以 .com 结尾)

# awk '{for(i=1;i<=NF;i++){if(i in arr){arr[i]=arr[i]" "$i}else{arr[i]=$i}}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt | sort | sed -e :a -e '$!N;/^\(.*\.com\).*\n\1.*/s/^\(.*\)\(\n\)\(.*\.com\)\(.*\)/\1\4/g; ta'

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

5.19 行列转换,并且将第 1 行相同的列,行列转换以后合并,也就是行列转换以后没有第 1 列相互重复的行 (指定 1 列是以空格 “ ” 结尾)

# awk '{for(i=1;i<=NF;i++){if(i in arr){arr[i]=arr[i]" "$i}else{arr[i]=$i}}}END{for(i=1;i<=NF;i++){print arr[i]}}' test.txt | sort | sed -e :a -e '$!N;/^\(.*\ \).*\n\1.*/s/^\(.*\)\(\n\)\(.*\ \)\(.*\)/\1 \4/g; ta'

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

内容六:awk 导入变量的案例

6.1 将 date 命令的运行结果,通过 getline 赋值给 d ,之后将 d 的值显示出来

# awk 'BEGIN {"date"|getline d; print d}'

6.2 将 eternalcenter 的值赋予给变量 content,并将变量 content 的值显示出来

# content=eternalcenter
# awk '{print "$content"}'
content
# awk '{print '$content'}'
eternalcenter

(注意:awk 在使用变量时必须使用单引号 “””,否则会把变量名称当作字符串)

6.3 将 eternalcenter 的值赋予变量 value,再将变量 value 的值赋予给变量 content,并将变量 content 的值显示出来

# value=eternalcenter
# echo | awk -v content=$value '{print(content)}'
eternalcenter

6.4 将 mingyuzhu 的值赋予变量 n,显示第 1 列等于变量 n 的值的行第 1 列、第 2 列和第 3 列

# gawk -F: '($1 == n) {print $1,$2,$3}' n=mingyuzhu /etc/group
(补充:这里的 /etc/group 是要被 gawk 操作的测试文件)

内容七:awk 变量判断的案例

如果变量 value 的值为 eternalcenter 则显示 0 否则显示 1

# echo eternalcenter | awk -v value="eternalcenter" '{print($1==value)? "0":"1"}'

或者:

# echo eternalcenter | awk -v value="eternalcenter" 'BEGIN{if($1==value){result=0}else(result=1)} END{print result}'

内容八:awk 常量显示的案例

# awk -F: '{print $1,"interpreter:",$7}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

内容九:awk 匹配显示的案例

9.1 显示包含 Failed 的行的第 11 列

# awk '/Failed/{print $11}' /var/log/secure

或者:

# awk 'index($0,"Failed"){print $11}' /var/log/secure

(补充:这里的 /var/log/secure 是要被 awk 操作的测试文件)

9.2 显示以 bash 结尾的行,并且将冒号 “:” 视为分割符

# awk -F: '/bash$/{print}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

9.3 显示以 root 或者 adm 开头的行的第 1 列和第 3 列,并且将冒号 “:” 视为分割符

# awk -F: '/^(root|adm)/{print $1,$3}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

9.4 显示以 r 或者 a 开头的行的第 1 列和第 3 列,并且将冒号 “:” 视为分割符

# awk -F: '/^[ra]/{print $1,$3}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

9.5 显示第 1 列包含 root 的行,并且将冒号 “:” 视为分割符

# awk -F: '$1~/root/' /etc/passwd

或者:

# awk -F: '$1 ~ /root/' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

9.6 显示第 7 列不以 nologin 结尾的行的第 1 列和第 7 列,并且将冒号 “:” 视为分割符

# awk -F: '$7!~/nologin$/{print $1,$7}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

9.7 显示前 1 个命令包含 Rx p 的行的第 5 列

# ifconfig eth0 | awk '/RX p/{print $5}'

9.8 显示前 1 个命令以 / 结尾的行的第 4 列

# df -h | awk '/\/$/{print $4}'

9.9 显示第 1 列以两个数字结尾的行的第 1 列,并且将冒号 “:” 视为分割符

# awk -F: '$1 ~/[0-9][0-9]$/(print $1}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

9.10 显示以 root 开头到以 mysql 开头的所有行的第 1 列,并且将冒号 “:” 视为分割符

# awk -F '/^root/,/^mysql/{print $1}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

内容十:awk 使用比较符号匹配的案例

10.1 显示行号等于 3 的行,并且将冒号 “:” 视为分割符

# awk -F: 'NR==3{print}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.2 显示第 3 列数值等于 1000 的行的第 1 列和第 3 列,并且将冒号 “:” 视为分割符

# awk -F: '$3==1000{print $1,$3}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.3 显示第 3 列数值不等于 1000 的行的第 1 列和第 3 列,并且将冒号 “:” 视为分割符

# awk -F: '$3!=1000{print $1,$3}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.4 显示第 3 列数值大于 1000 的行的第 1 列和第 3 列,并且将冒号 “:” 视为分割符

# awk -F: '$3>=1000{print $1,$3}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.5 显示第 3 列数值小于 10 的行的第 1 列和第 3 列,并且将冒号 “:” 视为分割符

# awk -F: '$3<10{print $1,$3}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.6 显示第 3 列数值大于 10 并且小于 20 的行,并且将冒号 “:” 视为分割符

# awk -F: '$3>10 && $3<20' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.7 显示第 3 列数值大于 1000 或者小于 10 的行,并且将冒号 “:” 视为分割符

# awk -F: '$3>1000 || $3<10' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.8 显示第 3 列乘以第 4 列小于 100 的行,并且将冒号 “:” 视为分割符

# awk -F: '$3 * $4 <100' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.9 显示第 1 列为 root 的行,并且将冒号 “:” 视为分割符

# awk -F: '$1=="root"' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

10.10 显示第 1 列是 test1 且第 2 列是 test2 的行

# awk '{if($1=="test1"&&$2=="test2"){print}}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

10.11 显示第 1 列包含 test1 且第 2 列包含 test2 的行

# awk '$1~/test1/&&$2~/test2/' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

10.12 显示第 1 列是 test1 或者第 2 列是 test2 的行

# awk '{if($1=="test1"||$2=="test2"){print}}' test.txt

或者:

# awk '{if($1=="test1"){print};if($2=="test2"){print}}' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

10.13 显示第 1 列包含 test1 或者第 2 列包含 test2 的行

# awk '$1~/test1/;$2~/test2/' test.txt

或者:

# awk '$1~/test1/||$2~/test2/' test.txt

(补充:这里的 test.txt 是要被 awk 操作的测试文件)

内容十一:awk 计算的案例

11.1 0 + 1

# awk 'BEGIN{print x+1}'

或者:

# awk 'BEGIN{x++;print x}'

(注意:这里的 x 可以不定义,直接用,默认值为 0)

11.2 8 + 2

# awk 'BEGIN{x=8;print x+=2}'

(注意:这里的 x 可以不定义,直接用,默认值为 0)

11.3 8 – 1

# awk 'BEGIN{x=8;x--;print x}'

(注意:这里的 x 可以不定义,直接用,默认值为 0)

11.4 2 + 3

# awk 'BEGIN{print 2+3}'

11.5 3.2 + 3.5

# awk 'BEGIN{print 3.2+3.5}'

11.6 2 * 3

# awk 'BEGIN{print 2*3}'

11.7 2 乘 3 再加 3

# awk 'BEGIN{print 2*3+3}'

11.8 3 加 3 再乘 2

# awk 'BEGIN{print (3+3)*2}'

11.9 求 23 除 8 的余数

# awk 'BEGIN{print 23%8}'

11.10 显示第 3 加上 10 的结果,并且将冒号 “:” 视为分割符

# awk -F: '$3 + 10' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

11.11 找 200 以内 3 的倍数

# seq 200 | awk '$1%3==0'

11.12 找 100 以内 7 的倍数或者包含 7 的数

# seq 100 | awk '$1%7==0||$1~/7/'

11.13 将第 4 列求和后显示,并且将冒号 “:” 视为分割符

# awk -F: 'BEGIN{total=0}{total+=$4}END{print total}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

11.14 当第 2 列中,以横杠 “-” 作为分隔符的第 2 列的内容等于 01,则将此行的第 3 列的数值相加,并以第 1 列作为基准进行显示 (统计员工 1 月份的消费之和)

# cat test.txt 
Tom 2022-01-01 100
John 2022-02-05 300
Tom 2022-03-20 400
John 2022-01-22 200
Tom 2022-01-05 500
# awk '{split($2,a,"-");if(a[2]==01){b[$1]+=$3}}END{for(i in b)print i,b[i]}' test.txt

内容十二:awk 计数的案例

12.1 计算以 bash 结尾的行数

# awk 'BEGIN{x=0}/bash$/{x++} END{print x}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

12.2 计算第 3 列小于等于 1000 的行数,并且将冒号 “:” 视为分割符

# awk -F: '{if($3<=1000){i++}}END{print i}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

12.3 计算 3 列大于 1000 的行数,并且将冒号 “:” 视为分割符

# awk -F: '{if($3>1000){i++}}END{print i}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

12.4 计算第 7 列以 bash 结尾的行数,并且将冒号 “:” 视为分割符

# awk -F: '{if($7~/bash$/){i++}}END{print i}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

12.5 计算第 3 列数值和第 4 列数值的和并赋值给变量,再将变量的值显示出来,并且将冒号 “:” 视为分割符

# awk -F: '{result=$3+$4; printf result}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

12.6 计算第 3 列数值除以第 4 列数值再乘以 100.0

# awk '{print $3/$2 * 100.0}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

12.7 计算第 1 列所有值各自总共出现的次数

# awk '{a[$1]++}END{for(i in a){print i,a[$i]}}' /usr/local/nginx/logs/access.log

(补充:这里的 /usr/local/nginx/logs/access.log 是要被 awk 操作的测试文件)

12.8 统计第 4 列里的横杠 “-” 的次数并把第 4 列替换统计后的次数后显示

# echo "z m 7 2022-01-01 a:d" | awk '$4=gsub(/-/,"",$4)'

12.9 统计此行中横杠 “-” 出现的次数并将第 4 列替换统计后的次数后显示

# echo "z- m y 2021-11-11 z:y" | awk '$4=gsub(/-/,"")'
z m y 3 z:y

案例十三:awk if 判断语句计数的案例

13.1 计算第 3 列小于等于 1000 的行数和第 3 列大于 1000 的行数,并且将冒号 “:” 视为分割符

# awk -F: '{if($3<=1000){i++}else{j++}} END{print i,j}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

13.2 计算第 7 列以 bash 结尾的行数和第 7 列不以 bash 结尾的行数,并且将冒号 “:” 视为分割符

# awk -F: '{if($7~/bash$/){i++}else{j++}} END{print i,j}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

13.3 如果第 3 列等于 0 则显示 “root”,否则显示 “not root”,并且将冒号 “:” 视为分割符

# awk -F: '{print ($3=0 ? "root": not root)}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

13.4 如果第 3 列大于 1000 则显示第 3 列的值,否则显示 “system id”,并且将冒号 “:” 视为分割符

# awk -F: 'BEGIN{max=1000}{id=($3>max ?$3 :"system id");print id}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

13.5 如果第 1 列的字符串的长度大于 3 则显示第 1 列的内容

# awk '{if (length($1) > 3) print $1}' test.txt

13.6 当第 2 列等于大写的 “A” 或者小写的 “a” 时则显示此行的第 2 列

# awk '{IGNORECASE=1;if($2=="a"){print $2}}' test.txt

内容十四:awk for 循环统计的案例

14.1 创建数组 a,其中 a[0]=0 a[1]=11 a[2]=22,并且将数组 a 显示出来

# awk 'BEGIN{a[0]=0;a[1]=11;a[2]=22;for(i in a){print i,a[i]}}'

14.2 统计所有的 IP 地址以及每 1 个 IP 地址访问的次数

# awk '{ip[$1]++} END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log


补充:
1) 这里的 /var/log/httpd/access_log 是要被 awk 操作的测试文件
2) 这里以统计 /var/log/httpd/access_log 文件里第 1 列的 IP 地址为例

14.3 统计所有的 IP 地址以及每 1 个 IP 地址访问的次数,并以从多到少的顺序进行排序

# awk '{ip[$1]++} END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log | sort -k2nr


补充:
1) 这里的 /var/log/httpd/access_log 是要被 awk 操作的测试文件
2) 这里以统计 /var/log/httpd/access_log 文件里第 1 列的 IP 地址为例

14.4 统计所有的 IP 地址以及每 1 个 IP 地址非 200 状态访问的次数

# awk '$8!=200{ip[$1]++}END{for(i in ip){print ip[i],i}}' /var/log/httpd/access_log

(
补充:
1) 这里的 /var/log/httpd/access_log 是要被 awk 操作的测试文件
2) 这里以统计 /var/log/httpd/access_log 文件里第 1 列的 IP 地址为例
)

14.5 统计所有的 IP 地址以及每 1 个 IP 地址非 200 状态访问的次数,并只显示数量最多的前 10 个 IP 地址和其对应的次数

# awk '$8!=200{ip[$1]++}END{for(i in ip){print ip[i],i}}' /var/log/httpd/access_log | sort -k1nr | head -n

(
补充:
1) 这里的 /var/log/httpd/access_log 是要被 awk 操作的测试文件
2) 这里以统计 /var/log/httpd/access_log 文件里第 1 列的 IP 地址为例
)

或者:

# awk '$8!=200{ip[$1]++}END{PROCINFO["sorted_in"]="@val_num_desc";for(i in ip){if(count++=10){exit}print ip[i],i}}' /var/log/httpd/access_log

(
补充:
1) 这里的 /var/log/httpd/access_log 是要被 awk 操作的测试文件
2) 这里以统计 /var/log/httpd/access_log 文件里第 1 列的 IP 地址为例
3) 这里的 PROCINFO[“sorted_in”]=”@val_num_desc” 表示在 for(i in ip) 时通过 ip[i] 的数值进行降序排序,也就是 ip 数组中,最大的一个值会排在前面

14.6 统计 TCP 连接出现的次数

# ss -ntulap 2> /dev/null | awk '/^tcp/{tcp[$6]++} END{for(i in tcp){print i,tcp[i]}}'

或者:

# ss -ntulap 2> | grep 'tcp' | awk '{print $6}' | sort | uniq -c

14.7 统计每个网页被各个 IP 地址访问的次数,并以每个网页和 IP 地址作为文件名创建文件,里面存储着此网页被访问的 IP 地址和对应的访问次数

# vim awk.txt

创建以下内容:

BEGIN{
    FS=" "
}
{
    if(!arr[$1,$2]++){
        arr1[$1]++
    }
}

END{
    for(i in arr){
        print i,arr[i] >(i".txt")
    }
}
# awk -f awk.txt test.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 这里的 test.txt 是要被 awk 操作的测试文件
3) 这里以统计 test.txt 文件里第 1 列的网页和第 2 列第 IP 地址为例
4) 这里当把 arr[$1,$2] 放到 if 的小括号 “()” 里面时,它是 1 个判断,当 arr[$1,$2] 第 1 次遭遇某个内容时,arr[$1,$2] 此时的值是 0 ,然后才进行 ++ (也就是加 1),而当 arr[$1,$2] 的值是 0 时,给他取反的 !arr[$1,$2] 就是布尔真,而布尔为真时就执行 arr1[$1]++
5) 当后来 arr[$1,$2] 不是第 1 次遭遇某个内容时,arr[$1,$2] 的值就不是 0 了,而给他取反的 !arr[$1,$2] 就是布尔假了,也就不会再执行后面的 arr1[$1]++

或者:

# vim awk.txt

创建以下内容:

BEGIN{
    FS=" "
}

!arr[$1,$2]++ {
    arr1[$1]++
}

END{
    for(i in arr){
        print i,arr[i] >(i".txt")
    }
}
# awk -f awk.txt test.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 这里的 test.txt 是要被 awk 操作的测试文件
3) 这里以统计 test.txt 文件里第 1 列的网页和第 2 列第 IP 地址为例
4) 这里当把 arr[$1,$2] 放到大括号 “{}” 外面时它自身就是 1 个判断,当 arr[$1,$2] 第 1 次遭遇某个内容时,arr[$1,$2] 此时的值是 0 ,然后才进行 ++ (也就是加 1),而当 arr[$1,$2] 的值是 0 时,给他取反的 !arr[$1,$2] 就是布尔真,而布尔为真时就执行 arr1[$1]++
5) 当后来 arr[$1,$2] 不是第 1 次遭遇某个内容时,arr[$1,$2] 的值就不是 0 了,而给他取反的 !arr[$1,$2] 就是布尔假了,也就不会再执行后面的 arr1[$1]++

14.8 将每 1 列的值前面加上所属行的行号,按行单独显示出来

# awk -F: '{i=1;while(i<NF) {print NR,$i;i++}}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

或者:

# awk -F: '{for(i=1;i<NF;i++)  {print NR,$i}}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

14.9 显示九九乘法表

# seq 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) 执行 seq 9 命令,第 1 行输出 1,第 2 行输出 2,以此类推直到第 9 行,并将结果传输给 sed 命令
2) 执行 sed 的 H 参数,将 pattern space (模型空间) 中的内容 append (添加) 到 hold space (保持空间) 分行符 “/n” 后,并将此时的 pattern space (模型空间) 传输给 g 参数。当执行到第 1 行时,添加到 hold space (保持空间) 是 1,hold space (保持空间) 里的值是 \n1,当时执行到第 2 行时,添加到 hold space (保持空间) 是 2,hold space (保持空间) 里的值是 \n1\n2,依次类推
3) 执行 sed 的 g 参数,将 hold space (保持空间) 中的内容拷贝到 pattern space (模型空间) 中,原来 pattern space (模型空间) 里的内容被清除。当执行到第 1 行时,hold space (保持空间)里的值是 \n1,pattern space (模型空间) 里的值将是 \n1,显示的结果也会是 1,当执行到第 2 行时,hold space (保持空间)里的值是 \n1\n2,pattern space (模型空间) 里的值将是 \n1\n2,显示的结果也会是 \n1\n2,依次类推,并将结果传输给命令 awk
3) awk,将多行视为 1 行,之后执行从 1 到此行列数次循环,变量 i 初始时的值为 1,每执行 1 次则变量 i 到值会加 1,每次循环时会显示:“此时变量 i 的值”*“此行的列数”=“此时变量 i 的值和此行的列数的乘积““当此时变量 i 的值等于此行的列数时则换行,否则的话则显示制表符的空格长度”。当执行到第 1 行时显示的是 1×1=1,当执行到第 2 行时显示的是 1×2=2 2×2=4,以此类推

内容十五:awk 表格显示的案例

在开始显示自定义的 1 行,在结尾再显示自定义的 1 行,并且将 “:” 视为分割符

# awk -F: 'BEGIN{print "User\tUID\tHome"}{print $1 "\t"  $3  "\t"  $6}END{print "Total",NR,"lines."}' /etc/passwd

(补充:这里的 /etc/passwd 是要被 awk 操作的测试文件)

内容十六:awk 精确时间处理的案例

16.1 将当前系统转换成 epoch 值

# awk 'BEGIN{print systime()}'

16.2 将 2019 年 01 月 01 日 01 时 01 分 01 秒转换成 epoch 值

# awk 'BEGIN{print mktime("2019 01 01 01 01 01")}'

或者:

# awk 'BEGIN{print mktime("2019 1 1 1 1 1")}'

或者:

# vim awk.txt

创建以下内容:

BEGIN{
    str="2019-01-01T01:01:01+8:00"
    patsplit(str,arr,"[0-9]{1,4}")
    Y=arr[1]
    M=arr[2]
    D=arr[3]
    H=arr[4]
    m=arr[5]
    S=arr[6]
    print mktime(sprintf("%s %s %s %s %s",Y,M,D,H,m,S))}
}
# awk -f awk.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) patsplit 函数的格式是:patsplit(<string>,<array>,”<regular expression>”),其中第 1 个是要匹配的字符串,第 2 个是要匹配结果存储进去的数组,第 3 个是正则表达式,通过正则表达式匹配字符串,然后将匹配成功的部分保存到数组中去
3) mktime 函数的格式是:mktime(‘YYYY MM DD HH mm SS [D9T]’),作用是:构建时间,如果构建成功则显示这个时间点秒级的 epoch 值,如果构建失败则返回 -1

16.3 将 2019 年 01 月 01 日 01 时 01 分 01 秒的时间点转换成 epoch 值

# date -d '@1546275661' +"%F %T"

16.4 显示从 2021-01-01T01:01:01+08:00 开始到结尾的所有日志,以时间被中括号扩起来作为时间到格式,例如:[2021-01-01T01:01:01+8:00] (处理只包含数字的时间)

# vim awk.txt

创建以下内容:

BEGIN{
  str="2021-01-01T01:01:01+08:00"
  choose_time = mktime("2021 01 01 01 01 01")
}

function strptime_now(str   ,arr,Y,M,D,H,m,S) {
  patsplit(str,arr,"[0-9]{1,4}")
  Y=arr[1]
  M=arr[2]
  D=arr[3]
  H=arr[4]
  m=arr[5]
  S=arr[6]
  return mktime(sprintf("%s %s %s %s %s %s",Y,M,D,H,m,S))
}

{
  #[2022-01-01T01:01:01+8:00]
  match($0,"^.*\\[(.*)\\].*",arr)
  tmp_time = strptime_now(arr[1])
  if(tmp_time > choose_time){
    print
  }
}
# awk -f awk.txt test.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 这里的 test.txt 是要被 awk 操作的测试文件
3) 默认情况下 Y=arr[1] 等是在定义全局变量,这里的 strptime(str ,arr,Y,M,D,H,m,S) 是指将全局变量转换成局部变量,这样此函数执行完以后变量就被删除了
4) patsplit 函数的格式是:patsplit(<string>,<array>,”<regular expression>”),其中第 1 个是要匹配的字符串,第 2 个是要匹配结果存储进去的数组,第 3 个是正则表达式,通过正则表达式匹配字符串,然后将匹配成功的部分保存到数组中去
5) mktime 函数的格式是:mktime(‘YYYY MM DD HH mm SS [D9T]’),作用是:构建时间,如果构建成功则显示这个时间点秒级的 epoch 值,如果构建失败则返回 -1
6) sprintf 函数的格式是:sprintf(“s%……”,<variable>……),作用是:显示变量里的内容,其中第 1 个是占位符,第 2 个是变量
7) match 函数的格式是:match(<column>,”<regular expression>”,<array>),其中第 1 个是要匹配的列,第 2 个是正则表达式,第 3 个是要匹配结果存储进去的数组,作用是:以正则表达式匹配匹配某列,并将匹配结果存储在数组里

16.5 显示从 01/Jan/2022:21:30:30+8:00 开始到结尾的所有日志,以时间被中括号扩起来作为时间到格式,例如:[01/Jan/2022:21:30:30+8:00] (处理只包含英文月份的时间)

# vim awk.txt

创建以下内容:

BEGIN{
  str="01/Jan/2022:21:30:30+8:00"
  choose_time = mktime("2020 01 01 01 01 01")
}

{
  #[01/Jan/2022:21:30:30+8:00]
  match($0,"^.*\\[(.*)\\].*",arr)
  tmp_time = strptime(arr[1])
  if(tmp_time > choose_time){
    print
  }
}

# 01/Jan/2022:21:30:30+8:00
function strptime(str   ,dt_str,arr,Y,M,D,H,m,S) {
  dt_str = gensub("[/:+-]"," ","g",str)
  # 01 Jan 2020 01 01 01 01 01
  split(dt_str,arr," ")
  Y=arr[3]
  M=mon_map(arr[2])
  D=arr[1]
  H=arr[4]
  m=arr[5]
  S=arr[6]
  return mktime(sprintf("%s %s %s %s %s %s",Y,M,D,H,m,S))
}

function mon_map(str   ,mons){
  mons["Jan"]=1
  mons["Feb"]=2
  mons["Mar"]=3
  mons["Apr"]=4
  mons["May"]=5
  mons["Jun"]=6
  mons["Jul"]=7
  mons["Aug"]=8
  mons["Sep"]=9
  mons["Oct"]=10
  mons["Nov"]=11
  mons["Dec"]=12
  return mons[str]
}
# awk -f awk.txt test.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 这里的 test.txt 是要被 awk 操作的测试文件
3) 默认情况下 Y=arr[1] 等是在定义全局变量,这里的 strptime(str ,arr,Y,M,D,H,m,S) 是指将全局变量转换成局部变量,这样此函数执行完以后变量就被删除了
4) gensub 函数的格式是:gensub(“<original character>”,”<new character>”,”g”,<string>),作用是:将字符串中的某些字符进行替换,其中第 1 个是原字符,第 2 个是新字符,第 3 个是要操作的字符串
5) split 函数的格式是:split(<string>,<array>,”<separator>”),作用是:将字符串里的内容分开存储到数组里,其中第 1 个是要操作的字符串,第 2 个是要存入内容的数组,第 3 个是分隔符
6) mktime 函数的格式是:mktime(‘YYYY MM DD HH mm SS [D9T]’),作用是:构建时间,如果构建成功则显示这个时间点秒级的 epoch 值,如果构建失败则返回 -1
7) sprintf 函数的格式是:sprintf(“s%……”,<variable>……),作用是:显示变量里的内容,其中第 1 个是占位符,第 2 个是变量
8) match 函数的格式是:match(<column>,”<regular expression>”,<array>),其中第 1 个是要匹配的列,第 2 个是正则表达式,第 3 个是要匹配结果存储进去的数组,作用是:以正则表达式匹配匹配某列,并将匹配结果存储在数组里

(注意:在 awk 中反译符号 “\“ 前面还必须要要有一个反译符号 ”\“,例如,如果要显示一个前引号 “””: \”)

内容十七:awk 同时处理 2 个文件的案例

先把第 2 个文件的第 5 列删除,再用第 2 个文件的第 1 列减去第 1 个文件的第 1 列,再把得到的结果粘贴到原来第 2 个文件第 5 列对应的位置

# vim awk.txt

创建以下内容:

{
  f1 = $1
  if( (getline < "test2.txt") >= 0 ){
    $5 = $1 - f1
    print $0
  }
}
# awk -f awk.txt test1.txt


补充:
1) 这里以创建和执行 awk.txt 文件为例
2) 这里的 test1.txt 和 test2.txt 是要被 awk 操作的测试文件
4) getline 有 3 类返回值,当返回值大于 0 时,表示已经读取到了内容。当返回值等于 0 时,表示没有读取到内容,第 1 行就是 EOF 结尾。当返回值小于 0 时,则代表没读取内容报错
5) 执行 getline < “test2.txt” 1 行 1 行地读取 test2.txt 文件里的内容

(注意:这里的 print $0 会根据 OFS 重建 $0,而默认情况下 OFS 是 1 个空格,所以这里重建后,列和列之间也会是 1 个空格)

或者:

# awk 'NR==FNR{arr[FNR]=$1}NR!=FNR{$5 = $1 - arr[FNR];print $0}' test1.txt test2.txt

(补充:这里 NR == FNR 的时候表示处理的是第 1 个文件,只有当处理第 1 个文件中时 NR 等于 FNR)