马哥Linux SysAdmin学习笔记(四)

sed:编辑器
sed:Stream EDitor,行编辑器
用法:
sed [option]... 'script' inputfile...
script:
'地址命令'
常用选项:
-n:不输出模式中的内容至屏幕
-e:多点编辑
-f /path/to/script_file:从指定文件中读取编辑脚本
-r:支持使用扩展正则表达式
-i:原处编辑
地址定界:
1.不给地址:对全文进行处理
2.单地址:
#: 指定的行
/pattern/:被此处模式所能匹配到的每一行
3.地址范围:
#,#
#,+#
/pat1/,/pat2/
#,/pat1/
4.~:步进
1~2,1开始,步长2
2~2,2开始,步长2
编辑命令:
d:删除
p:显示模式空间中的内容
a \text:在行后面追加文本;支持使用\n实现多行追加
i \text:在行前面追加文本
c \text:替换行为单行或多行文本
w /path/to/somefile:保存模式空间中匹配到的行至指定文件中
r /path/form/somefile:读取指定文件的文本流至模式空间中匹配到的行的行后
=: 为模式空间中的行打印行号
!:取反条件
s///:支持使用其他分隔符,s@@@,s###
替换标记:
g:行内全局替换
p:显示替换成功的行
w /path/to/somefile:替换成功的结果保存至文件中
练习:
删除/boot/grub/grub.conf文件中所有以空白开头的行的行首的空白字符
sed 's@^[[:space:]]\+@@' /etc/grub2.cfg
删除/etc/fstab文件中所有以#开头,后面至少跟一个空白字符的行的行首的#和空白字符
sed 's/^#[[:space:]]\+//' /etc/fstab 
echo一个绝对路径给sed命令,取出其基名,取出其目录名
echo "/etc/sysconfig/" | sed 's@[^/]\+/\?$@@'
高级编辑命令:
h:把模式空间中的内容覆盖至保持空间中
H:把模式空间中的内容追加至保持空间中
g:从保持空间取出数据覆盖至模式空间
G:从保持空间取出内容追加至模式空间
x:把模式空间中的内容与保持空间中的内容进行互换
n:读取匹配到的行的下一行至模式空间
N:追加匹配到的行的下一行至模式空间
d:删除模式空间中的行
D:删除多行模式空间中的所有行
sed -n 'n;p' FILE
sed '1!G;h;$!d' FILE :逆向显示文件内容
sed '$!N;$!D' FILE:取出文件后两行
sed '$!d' FILE:取出文件最后一行
sed 'G' FILE:每行后添加空白行
sed '/^$/d;G' FILE:每行后添加空白行,多个空白行时合并为一个空白行
sed 'n;d' FILE:显示奇数行
sed -n '1!G;h;$p' FILE:逆向显示文件的每一行内容
base脚本编程:
while CONDITION;do
循环体
done
进入条件:CONDATION为true
退出条件:false
until CONDITION;do
循环体
done
进入条件:false
退出条件:true
示例:求100以内所有正整数之和
#!/bin/bash
#
declare -i i=1
declare -i sum=0
until [ $i -gt 100 ];do
let sum+=$i
let i++
done
echo $sum
示例:打印99乘法表
#!/bin/bash
declare -i j=1
declare -i i=1
until [ $j -gt 9 ];do
until [ $i -gt $j ];do
echo -n -e "${i}*${j}=$[$i*$j]\t"
let i++
done
echo
let i=1
let j++
done
循环控制语句(用于循环体中):
continue [N]:提前结束第N层循环
while CONDITION1:do
CMD1
...
if CONDITION2;then
continue
fi
CMDn
...
done
break [N]:提前结束循环
while CONDITION1:do
CMD1
...
if CONDITION2;then
break
fi
CMDn
...
done
示例1:求100以内所有偶数之和;要求循环遍历100以内的所有正整数
#!/bin/bash
declare -i i=0
declare -i sum=0
until [ $i -gt 100 ];do
let i++
if [ $[$i%s] -eq 1 ];then
continue
fi
let sum+=$i
done
echo $sum
创建死循环:
while true;do
until false;do
示例2:每隔3秒钟到系统上获取已经登录的用户的信息,如果docker用户登录了,则记录于日志中,并退出
while循环的特殊用法(遍历文件的每一行):
while read line;do
循环体
done < /path/from/somefile
依次读取文件中的每一行,且将行赋值给line
示例:找出ID号为偶数的所有用户,显示其用户名及ID
for循环的特殊格式:
for ((控制变量初始化;条件判断表达式;控制变量的修正表达式));do
循环体
done
控制变量初始化:仅在运行到循环代码段时执行一次
控制变量的修正表达式:每轮循环结束,会先进行控制变量修正运算,而后再做条件判断
示例:求100以内所有正整数之和
条件判断:case语句
case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
默认分支
;;
esac
练习:写一个脚本,完成如下要求
1.脚本可接受参数:start,stop,restart,status
2.如果参数非此四个之一,提示使用格式后报错退出;
3.如果是start,则创建/var/lock/subsys/SCRIPT_NAME;并显示启动成功
考虑:如果事先已经启动过一次,该如何处理
4.如果是stop,则删除/var/lock/subsys/SCRIPT_NAME,并显示停止完成
考虑:如果事先已经停止过了,该如何处理
5.如果是restart,则先stop,再start
考虑:如果本来没有start,如何处理
6.如果是status
如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示"SCRIPT_NAME is running..."
如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示"SCRIPT_NAME is stopped..."
其中:SCRIPT_NAME为当前脚本名
#!/bin/bash
#
# chkconfig: - 88 12
# description: test 
prog=$(basename $0)
lockfile=/var/lock/subsys/$prog
start(){
if [ -f $lockfile ];then
echo "$prog is aleady running."
return 0
else
touch $lockfile
[ $? -eq 0 ] && echo "Starting $prog finished."
fi
}
stop(){
if [ -f $lockfile ];then
rm -f $lockfile && echo "Stop $prog finished."
else
echo "$prog is stopped ."
fi
}
status(){
if [ -e $lockfile ];then
echo "$prog is running."
else
echo "$prog is stopped."
fi
}
usage(){
echo "usage:$prog [start|stop|restart|status]"
}
if [ $# -lt 1 ];then
usage
exit 1
fi
case $1 in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
status
;;
*)
usage
exit $?
esac
bash脚本编程:
case支持glob风格的通配符:
*:任意长度任意字符
?:任意单个字符
[]:指定范围内的任意单个字符
a|b:a或b
function:函数
过程式编程:代码重用
模块化编程
结构化编程
语法1:
function f_name {
...函数体
}
语法2:
f_name() {
}
调用:函数只有被调用才会执行
调用:给定函数名
函数名出现的地方,会被自动替换为函数代码
函数的生命周期:被调用时创建,返回时终止
return命令返回自定义状态结果
0:成功
1-255:失败
#!/bin/bash
function adduser {
if id $username &> /dev/null;then
echo "$username exists."
else
useradd $username
[ $? -eq 0 ] && echo "Add $username finished/"
fi
}
adduser
for i in {1..10};do
username=myuser$i
adduser
done
函数返回值:
函数的执行结果返回值:
1.使用echo或print命令进行输出
2.函数体中调用命令的执行结果
函数的退出状态码
1.默认取决于函数体中执行的最后一条命令的退出状态码
2.自定义退出状态码
return
函数可以接受参数:
传递参数给函数:调用函数式,在函数名后面以空白分割给定参数列表即可:例如testfunc arg1 arg2 ...
在函数体中,可使用$1,$2...调用这些参数;还可以使用$@,$#等特殊变量
练习:打印NN乘法表,使用函数实现
变量作用域:
本地变量:当前shell进程;为了执行脚本会启动专用的shell进程;因此,本地变量的作用范围是当前shell脚本程序文件
局部变量:函数的声明周期;函数结束时变量被销毁
如果函数有局部变量,其名称同本地变量
在函数中定义局部变量的方法:
local NAME=VALUE
函数递归:
函数直接或间接调用自身:
N!=N(n-1)(n-2)...1
#!/bin/bash
#
fact() {
if [ $1 -eq 0 -o $1 -eq 1];then
echo 1
else
echo $[$1*$(fact $[$1-1])]
fi
}
fact $1
练习:求N阶斐波那契数列
Systemd:
POST --> Boot Sequence --> Bootloader --> kernel + initramfs(initrd) --> rootfs --> /sbin/init
init:
CentOS 5:SysV init
CentOS 6:Upstart
CentOS 7:Systemd
Systemd新特性:
系统引导时实现服务并行启动
按需激活进程
系统状态快照
基于依赖关系定义服务控制逻辑
核心概念:unit
配置文件进行标识和配置:文件中主要包含了系统服务,监听socket,保存的系统快照以及其他与init相关的信息
保存至:
/usr/lib/systemd/system
/run/systemd/system
/etc/systemd/system
unit的类型:
service unit:文件扩展名为.service,用于定义系统服务
target unit:文件扩展名为.target,用于模拟实现运行级别
device unit:.device,用于定义内核识别的设备
mount unit:.mount,定义文件系统挂载点
socket unit:.socket,用于标识进程间通信用的socket文件
snapshot unit:.snapshot,管理系统快照
swap unit:.swap,用于标识swap设备
automount unit:.automount,文件系统的自动挂载点
path unit:.path,用于定义文件系统中的一个文件或目录
关键特性:
基于socket的激活机制:socket与服务程序分离
基于bus的激活机制:
基于device的激活机制:
基于path的激活机制:
系统快照:保存各unit的当前状态信息与持久存储设备中
向后兼容 sysv init脚本:
不兼容:
systemctl命令固定不变
非由systemd启动的服务,systemctl无法与之通信
管理系统服务:
CentOS 7:service unit
注意:能兼容早期的服务脚本
命令:systemctl COMMAND name.service
启动:service name start ==> systemctl start name.service
停止:service name stop  ==> systemctl stop name.service
重启:service name restart ==> systemctl restart name.service
状态:service name status ==> systemctl status name.service
条件式重启:service name condrestart ==> systemctl try-restart name.service
重载或重启服务:systemctl reload-or-restart name.service
重载或条件式重启服务:systemctl reload-or-try-restart name.service
禁止设定为开机自启:systemctl mask name.service
取消禁止设定为开机自启:systemctl unmask name.service
查看某服务当前激活与否: systemctl is-active name.service
查看所有已经激活的服务:
systemctl list-units --type service
查看所有服务:
systemctl list-units --type service --all
chkconfig命令的对应关系:
设定某服务开机自启动:chkconfig name on ==> systemctl enable name.service
禁止:chkconfig name off ==> systemctl disable name.service
查看所有服务的开机自启状态:
chkconfig --list ==> systemctl list-unit-files --type service 
查看服务是否开机自启:systemctl is-enable name.service
其他命令:
查看服务依赖关系:
systemctl list-dependencies name.service
target units:
unit配置文件:.target
运行级别:
0 ==> runlevel0.target,poweroff.target
1 ==> runlevel1.target,rescue.target
2 ==> runlevel2.target,multi-user.target
3 ==> runlevel3.target,multi-user.target
4 ==> runlevel4.target,multi-user.target
5 ==> runlevel5.target,graphical.target
6 ==> runlevel6.target,reboot.target
切换级别:
init N ==> systemctl isolate name.target
查看级别:
runlevel ==> systemctl list-units --type target
获取默认运行级别:
/etc/inittab ==> systemctl get-default
修改默认级别:
/etc/inittab ==> systemctl set-default name.target
切换至紧急救援模式:
systemctl rescue
切换至emergency模式
systemctl emergency
其他常用命令:
关机:systemctl halt,systemctl poweroff
重启:systemctl reboot
挂起:systemctl suspend
快照:systemctl hibernate
快照并挂起:systemctl hybrid-sleep
bash脚本编程:
变量:存储单个元素的内存空间
数组:存储多个元素的连续的内存空间
数组名:
索引:编号从0开始,属于数值索引
注意:索引页可以支持使用自定义的格式,而不仅仅是数值格式
bash的数组支持稀疏格式
引用数组中的元素:${ARRAY_NAME[INDEX]}
声明数组:
declare -a ARRAY_NAME
declare -A ARRAY_NAME:关联数组
数组元素的赋值:
1.一次只赋值一个元素
ARRAY_NAME[INDEX]=VALUE
weekdays[0]="sunday"
weekdays[4]="Thursday"
2.一次赋值全部元素:
ARRAY_NAME=("val1" "val2" ...)
3.只赋值特定元素:
ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
4.read -a ARRAY
引用数组元素:${ARRAY_NAME[INDEX]}
注意:省略[INDEX]表示引用下标为0的元素
数组的长度(元素个数):${#ARRAY_NAME[*]},${#ARRAY_NAME[@]}
示例:生成10个随机数保存于数组中,并找出其最大值和最小值
#!/bin/bash
#
declare -a rand
declare -i max=0
for i in {1..9};do
rand[$i]=$RANDOM
[ ${rand[$i]} -gt $max ] && max=${rand[$i]}
done
echo $max
练习:写一个脚本
定义一个数组,数组中的元素是/var/log目录下所有以.log结尾的文件,要统计其下标为偶数的文件中的行数之和
#!/bin/bash
#
declare -a files
declare -i lines=0
files=(/var/log/*.log)
for i in $(seq 0 $[${#files[@]}-1]);do
if [ $[$i%2] -eq 0 ];then
let lines+=$(wc -l ${files[$i]} | cut -d' ' -f1)
fi
done
echo $lines
引用数组中的元素:
所有元素:${ARRAY[@]},${ARRAY[*]}
数组切片:${ARRAY[@]:offset:number}
offset:要跳过的元素个数
number:要取出的元素个数
向数组中追加元素:
ARRAY[${#ARRAY[*]}]
删除数组中的某元素:
unset ARRAY[INDEX]
关联数组:
declare -A ARRAY_NAME
ARRAY_NAME=([index_name]='val1' [index_name2]='val2' ...)
练习:生成10个随机数,升序或降序排序
bash的字符串处理工具:
字符串切片:${var:offset:number}
取字符串的最右侧几个字符:${var: -length}
注意:冒号后必须有一个空白字符
基于模式取子串:
${var#*word}:其中word可以是指定的任意字符串;功能:自左而右,查找var变量所存储的字符串中,第一次出现的word,删除字符串开头至第一次出现word字符之间的所有字符
${var##*word}:同上,不过删除的是字符串开头至最后一次由word指定的字符串之间的所有内容
file="/var/log/messages"
${file##*/}:messages
${var%word*}:其中word可以是指定的任意字符;功能:自右而左,查找var变量所存储的字符串中,第一次出现的word,删除字符串最后一个字符向左至第一次出现word字符之间的所有字符;
file="/var/log/messages"
${file%/*}:/var/log
${var%%word*}:同上,删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符
示例:url=http://www.magedu.com:80
${url##*:}
${url%%:*}
查找替换:
${var/pattern/substi}:查找var所表示的字符串中,第一次被pattern所匹配到的字符串,将其替换为substi
${var//pattern/substi}:查找var所表示的字符串中,所有被pattern所匹配到的字符串,将其替换为substi
${var/#pattern/substi}:查找var所表示的字符串中,行首被pattern所匹配到的字符串,将其替换为substi
${var/%pattern/substi}:查找var所表示的字符串中,行尾被pattern所匹配到的字符串,将其替换为substi
查找并删除:
${var/pattern}:查找var所表示的字符串中,第一次被pattern所匹配到的字符串,将其删除
${var//pattern}:查找var所表示的字符串中,所有被pattern所匹配到的字符串,将其删除
${var/#pattern}:查找var所表示的字符串中,行首被pattern所匹配到的字符串,将其删除
${var/%pattern}:查找var所表示的字符串中,行尾被pattern所匹配到的字符串,将其删除
字符大小写转换:
$(var^^):var中所有小写转换为大写
$(var,,):var中所有大写转换为小写
变量赋值:
${var:-value}:如果var为空或未设置,那么返回value;否则,返回var的值
${var:=value}:如果var为空或未设置,则返回value并将value赋值给var;否则,返回var的值
${var:+value}:如果var不为空,则返回value
${var:?error_info}:如果var为空或未设置,那么返回error_info;否则,返回var的值
为脚本程序使用配置文件:
定义文本文件,每行定义"name=value"
在脚本中source此文件即可
命令:
mktemp命令:
mktemp [OPTION]... [TEMPLATE]
TEMPLATE:filename.XXX
XXX至少要出现3个
OPTION:
-d:创建临时目录
--tmpdir=/path/to/somedir:指明临时文件位置
install命令:
install [option]… [-T] source dest
install [option]… source… directory
install [option]… -t directory source…
install [option]… -d directory…
选项:
-m MODE
-o OWNER
-g GROUP
练习:写一个脚本
1.提示用户输入一个可执行命令名称
2.获取此命令所依赖到的所有库文件列表
3.复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下
/bin/bash ==> /mnt/sysroot/bin/bash
/usr/bin/passwd ==> /mnt/sysroot/usr/bin/passwd
4.复制此命令依赖到的所有库文件至目标目录下的对应路径下
/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-64.so.2
进一步的:
每次复制完一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能,直到用户输入quit退出
回顾:bash脚本编程数组
数组,字符串处理
数组:
数组:declare -a
index:0-
关联数组:declare -A
字符串处理:
切片,查找替换,查找删除,变量赋值
GNU awk:
文本处理:grep,sed,awk
grep,egrep,fgrep:文本过滤工具;pattern
sed:行编辑器
模式空间,保持空间
awk:报告生成器,格式化文本输出;
AWK:Aho,Weinberger,Kernighan --> New AWK,NAWK
GNU awk,gawk
gawk - pattern scanning and processing language
基本用法:gawk [options] 'program' FILE ...
program:PATTERN{ACTION STATEMENT}
语句之间用分号分割
print,printf
选项:
-F:指明输入时用的字段分隔符
-v var=value:自定义变量
1.print
print item1,item2,...
要点:
1.逗号分隔符
2.输出的各item可以字符串,也可以是数值;当前记录的字段,变量或awk表达式
3.如果省略item,相当于print $0
2.变量
内建变量
FS:input field seperator;默认为空白字符
OFS:output field seperator;默认为空白字符
RS:input record seperator;输入时的换行符
ORS:output record seperator;输出时的换行符
NF:number of field,字段数量
{print NF},{print $NF}
NR:number of record,行数
FNR:各文件分别计数
FILENAME:当前文件名
ARGC:命令行中给定的参数个数
ARGV:数组,保存的是命令行所给定的各参数
自定义变量
1.-v var=value
变量名区分字符大小写
awk -v test='hello gawk' 'BEGIN{print test}'
2.在program中直接定义
awk  '{test="hello";print test}'
3.printf命令
格式化输出:printf FORMAT,item1,item2,...
1.FORMAT必须给出
2.不会自动换行,需要显示给出换行控制符,\n
3.FORMAT中需要分别为后面的每个item指定一个格式化符号
格式符:
%c:显示字符ASCII码
%d,%i:显示十进制整数
%e,%E:科学计数法数值显示
%f:浮点数
%g,%G:以科学计数法或浮点形式显示数值
%s:显示字符串
%u:无符号整数
%%:显示%自身
修饰符:
#[.#]:第一个数字控制显示的宽度,第二个字符表示小数点后的精度,右对齐
awk -F: '{printf "username:%15s,UID:%15d\n",$1,$3}' /etc/passwd
%3.1f:
-#[.#]:左对齐
+:显示数值的符号
4.操作符:
算数操作符:
x+y,x-y,x*y,x/y,x^y,x%y
-x
+x:转换为数值
字符串操作符:没有符号的操作符,字符串连接
=,+=,-=,*=,/=,%=,^=
++,--
比较操作符:
>,>=,<,<=,!=,==
模式匹配符:
~:是否匹配
awk -F: '$NF~/bash$/{print $1,$NF}' /etc/passwd
!~:是否不匹配
逻辑操作符:
&&
||
!
函数调用:
function_name(arg1,arg2,...)
条件表达式:
selector?if-true-expression:if-false-expression
awk -F: '{$3==0?usertype="comm":usertype="sys";printf "%15s:%s\n",$1,usertype}' /etc/passwd
5.PATTERN
1.empty:控模式,匹配每一行
2./regular expression/:
awk '/^UUID/{print $1}' /etc/fstab
3.relational expression:关系表达式;结果有真有假;结果为真会被处理
真:结果为非0值,非空字符串
awk -F: '$3>=300{print $1}' /etc/passwd
4.line ranges:行范围
startline,endline:/pat1/,/pat2/
awk -F: '/^root/,/^mail/{print $1}' /etc/passwd
awk -F: '(NR>=10&&NR<=20){print $1}' /etc/passwd
注意:不支持直接给出数字的格式
5.BEGIN/END模式
BEGIN{}:仅在开始处理文件中的文本之前执行一次
END{}:仅在文本处理完成之后执行一次
awk -F: 'BEGIN{print "\tusername\tuid"}{printf "%s\t%s\n",$1,$3}END{print "END"}' /etc/passwd
6.常用的action
1.Expressions
2.Control statements:if while等
3.compound statements:组合语句
4.input statements
5.output statements
7.控制语句
if(condition){statments}
if(condition){statments} else {statments}
while(condition){statments}
do{statments}while(condition)
for(exp1;exp2;exp3){statments}
break
continue
delete array[index]
delete array
exit
{ statments }
1.if-else
语法:if(condition) statment [else statment]
awk -F: '{if($3>=300){print $1,$3}else{print $1,$NF}}' /etc/passwd
df -h | awk -F[%] '/^\/dev/{print $1}'|awk '{if($NF>=10)print $1}'
使用场景:对awk取得的整行或某个字段做条件判断
2.while循环
语法:while(condition) statment
条件真,进入循环;条件假,退出循环
使用场景:对一行内的多个字段逐一类似处理时使用;对数组中的各元素逐一处理时使用
length()
awk '/^[[:space:]]*root/{i=1;while(i<=NF){print $i,length($i);i++}}' /etc/grub.conf
3.do-while循环
语法:do statement while(condition)
意义:至少执行一次循环体
4.for循环
语法for(expr1;expr2;expr3) statment
for(variable assignment;condition;iteration process){for-body}
awk '/^[[:space:]]*root/{for(i=1;i<=NF;i++){print $i,length($i)}}' /etc/grub.conf 
特殊用法:
能遍历数组中的元素:
语法:for (var in array){for-body}
5.switch语句:
语法:switch(expression){case VALUE1 or /REGEXP/:statement;case VALUE2 or /REGEXP2/:statement;...;default:statement}
6.break和continue
break [n]
continue
7.next
提前结束对本行的处理而直接进入下一行(python里的pass)
awk -F: '{if($3/2!=0)next;print $1,$3}' /etc/passwd
8.array
关联数组:array[index-expression]
index-expression
1.可以使用任意字符串
2.如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为空串
要判断数组中是否存在某元素,要使用"index in array"格式进行;
weekdays["mon"]="Monday"
awk 'BEGIN{weekdays["mon"]="Monday";print weekdays["mon"]}'
要遍历数组中的每一个元素,要使用for循环
for (var in array){for-body}
注意:var会遍历array的每个索引
awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log
练习:
1.统计/etc/fstab文件中,每个文件系统类型出现的次数
awk '/^UUID/{fs[$3]++}END{for(i in fs){print i,fs[$i]}}' /etc/fstab
2.统计指定文件每个单词出现的次数
awk '{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count){print i,count[i]}}' /etc/fstab
9.函数
1.内置函数
数值处理:
rand():返回0和1之间一个随机数
字符串处理:
length([s]):返回指定字符串的长度
sub(r,s,[t]):以r所表示的模式来查找t所表示的字符串中的匹配的内容,并将其第一次出现替换为s所表示的内容
gsub(r,s,[t]):以r所表示的模式来查找t所表示的字符串中的匹配的内容,并将其所有出现均替换为s所表示的内容
split(s,a[,r]):以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数组中
netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (i in count){print i,count[i]}}'
2.自定义函数
posted @ 2017-07-10 14:35  Redheat  阅读(640)  评论(0编辑  收藏  举报