理解
整理自网络资料,非原创版权,只用于学习理解的笔记和资料,禁止侵权
shell语言的理解:重点在于它是一个解释型语言,学习的shell语法写出的shell脚本,最终由linux服务器的解释器逐句解释运行
1.编程工具,脚本语言,解释型语言,执行时翻译机器码,翻译一句执行一句,跨平台,使用posix定义的规范
2.shell 命令行解释器,用户和操作系统间通信提供的一种接口,转换为一系列的系统调用送到内核执行
通俗的理解:
Bash 的语法在一些地方是不够严谨的,需要硬记,但是大部分的语法是可以用编译原来来进行理解。
一行命令被解释器读取后,它会去链接到相关的code,构成一段代码,并为这段代码初始化它运行的上下文,所以理解每一行命令的时候,可以假设它有你不知道的隐含在背后的参数,环境变量,code等。
命令就像call(bash_cmd,arg1,arg2) , 由内核+bash解释器去执行对应的
bash_cmd ==> callFunction(arg1,arg2)
所以脚本的第一行是
#!/bin/bash => 这是为了找到当前脚本执行的解释器bash(二进制可执行文件)
文本处理三剑客grep、sed、awk在文章后面均有讲解
变量替换
操作符 | 意义 |
---|---|
${var} | 变量替换,返回变量 var 的值。 |
${var:-word} | 如果变量 var 存在且非空,则返回变量 var 的值;否则返回 word。 |
${var:=word} | 如果变量 var 存在且非空,则返回变量 var 的值;否则将变量 var 赋值为 word,并返回 word。 |
${var:?message} | 如果变量 var 存在且非空,则返回变量 var 的值;否则输出错误信息 message,并退出脚本。 |
${var:+word} | 如果变量 var 存在且非空,则返回 word;否则返回空字符串。 |
${var#pattern} | 从变量 var 的开头开始匹配 pattern,并将匹配的最短部分删除。 |
${var##pattern} | 从变量 var 的开头开始匹配 pattern,并将匹配的最长部分删除。 |
${var%pattern} | 从变量 var 的结尾开始匹配 pattern,并将匹配的最短部分删除。 |
${var%%pattern} | 从变量 var 的结尾开始匹配 pattern,并将匹配的最长部分删除。 |
${var/pattern/string} | 匹配变量 var 中的 pattern,并将其替换为 string。只替换第一个匹配项。 |
${var//pattern/string} | 匹配变量 var 中的 pattern,并将其替换为 string。替换所有匹配项。 |
${#var} | 返回变量 var 的字符长度。 |
${var:offset:length} | 返回从变量 var 的 offset 位置开始,长度为 length 的子字符串。 |
${var: -length} | 返回变量 var 的末尾 length 个字符。 |
${!prefix*} 或 ${!prefix@} | 返回所有以 prefix 开头的变量名列表。 |
# 删除动作 从头或从尾
${var#规则} |${var##规则} |${var%规则} |${var%%规则}|
# 替换动作
|${var/旧字符串/新字符串} |${var//旧字符串/新字符串} |
变量替换总结
贪婪模式
1、${变量#匹配规则} # 从头开始匹配,最短删除
2、${变量##匹配规则} # 从头开始匹配,最长删除
3、${变量%匹配规则} # 从尾开始匹配,最短删除
4、${变量%%匹配规则} # 从尾开始匹配,最长删除
5、${变量/旧字符串/新字符串} # 替换变量内的旧字符串为新字符串,只替换第一个
6、${变量//旧字符串/新字符串} # 替换变量内的旧字符串为新字符串,全部替换
例子1:
variable_1="I love you,Do you love me"
var1=${variable_1#*ov}
var2=${variable_1##*ov}
var3=${variable_1%ov*}
var4=${variable_1%%ov*}
var5=${PATH/bin/BIN}
var6=${PATH//bin/BIN}
变量测试
expr声明为字符串
变量的配置方式 | str没有配置 | str字符为空串 | str字符配置且非空 |
---|---|---|---|
var=${str-expr} | var=expr | var=“” | var=$str |
var=${str:-expr} | var=expr | var=expr | var=$str |
var=${str+expr} | var= | var=expr | var=expr |
var=${str:+expr} | var= | var=“” | var=expr |
var=${str=expr} | var=expr | var=“” | var=$str |
var=${str:=expr} | var=expr | var=expr | var=$str |
开发中用的少,源码shell脚本中出现的多 现查现用:
只有+ 会用expr替换非空字符串,其他的多为
=> 处理空串或者未被赋值过的变量, 即怎么赋初始值或者空串用什么替换
字符串处理
获取子串 字符位置等
语法 | 说明 | |
---|---|---|
字符串长度 | ${#string} | 无 获取字符串长度 |
字符串长度 | expr length “$string” | expr命令 str有空格,则必须加双引号 |
获取子串的索引位置 | expr index $string %substring | 见注解 |
计算子串长度 | expr match $string substring | 匹配到的子串长度 |
注:当sub为字符串时,会将sub拆开为单个字符进行匹配,返回第一个匹配到字符的位置,并非完整匹配子串
string="hello world"
substring="world"
result=$(expr index "$string" "$substring")
echo "$result"
> 3 # 第一个找到的是l 字母
抽取子串
语法 | 说明 | |
---|---|---|
方法1 | ${string:position} | 从string中的position开始 |
方法2 | ${string:position:length} | 从position开始,匹配长度length |
方法3 | ${string: -position} | 从右开始匹配 |
方法4 | ${string:(position)} | 从左开始匹配 |
方法5 | expr substr $str $pos $length | 从position开始,匹配长度length |
字符串处理总结:
1、计算字符串长度
方法一:${#string}
方法二:expr length $string
例子:
var1="Hello World"
len=${#var1}
len=`expr length "$string"`
2、获取字符索引位置
方法:expr index "$string" substr
例子:
var1="quicstart is a app"
ind=`expr index "$var1" start`
3、获取子串长度
方法:expr match "$string" substr
例子:
var1="quicstart is a app"
sub_len=`expr match "$var1" app`
> 0 必须从头开始匹配,从中间匹配不到
sub_len=`expr match "$var1" quic`
> 4
4、抽取字符串中的子串
方法一:index从0开始
(1)、${string:position}
(2)、${string:position:length}
(3)、${string: -position} 或者 ${string:(position)}
方法二:index从1开始
expr substr $string $position $length
例子:
var1="kafka hadoop yarn mapreduce"
substr1=${var1:10}
substr2=${var1:10:6}
substr3=${var1: -5}
substr4=${var1: -10:4}
substr5=`expr substr "$var1" 5 10`
> a hadoop y
注意:使用expr,索引计数是从1开始计算;使用${string:position},索引计数是从0开始计数
执行脚本
var1="Hello World"
len1=${#var1}
len2=`expr length "$string"`
echo "${len1} ${len2}"
var2="quicstart is a app"
ind=`expr index "$var2" start`
echo "${ind}"
var3="quicstart is a app"
sub_len=`expr match "$var3" quic`
echo "${sub_len}"
> 4
sub_len=`expr match "$var3" quic.*`
echo "${sub_len}"
> 18
sub_len=`expr match "$var3" quicstart`
echo "${sub_len}"
> 9
var4="kafka hadoop yarn mapreduce"
substr1=${var4:10}
substr2=${var4:10:6}
substr3=${var4: -5}
substr4=${var4: -10:4}
命令替换
语法格式 | 注释 | |
---|---|---|
1 | `command` | 需要用``标志出这是一个命令替换 |
2 | $(command) |
命令替换总结:
有两种方法:
方法一:`command`
方法二:$(command)
例子1:
获取系统得所有用户并输出
#!/bin/bash
#
index=1
for user in `cat /etc/passwd | cut -d ":" -f 1`
do
echo "This is $index user: $user"
index=$(($index + 1))
done
例子2:
根据系统时间计算今年或明年
echo "This is $(date +%Y) year"
echo "This is $(($(date +%Y) + 1)) year"
例子3:
根据系统时间获取今年还剩下多少星期,已经过了多少星期
date +%j
echo "This year have passed $(date +%j) days"
echo "This year have passed $(($(date +%j)/7)) weeks"
echo "There is $((365 - $(date +%j))) days before new year"
echo "There is $(((365 - $(date +%j))/7)) days before new year"
例子4:
判定nginx进程是否存在,若不存在则自动拉起该进程
#!/bin/bash
#
nginx_process_num=$(ps -ef | grep nginx | grep -v grep | wc -l)
if [ $nginx_process_num -eq 0 ];then
systemctl start nginx
fi
总结:``和$()两者是等价的,但推荐初学者使用$(),易于掌握;缺点是极少数UNIX可能不支持,但``都是支持的
$(())主要用来进行整数运算,包括加减乘除,引用变量前面可以加$,也可以不加$
$(( (100 + 30) / 13 ))
num1=20;num2=30
((num++));
((num--))
$(($num1+$num2*2))
变量声明
declare 和 typeset 命令
- declare 和 typeset 命令等价
- 定义变量类型
有类型变量总结:
1、declare -r #声明变量为只读类型
declare -r var="hello"
var="world" -bash: var: readonly variable
2、declare -i #声明变量类型为整型
num1=2001
num2=$num1+1
echo $num2
declare -i num2
num2=$num1+1
echo $num2
3、declare -f 在脚本中显示定义的函数和内容
4、declare -F 在脚本中显示定义的函数
5、declare -a
array=("jones" "mike" "kobe" "jordan")
输出数组内容:
echo ${array[@]} 输出全部内容
echo ${array[1]} 输出下标索引为1的内容
获取数组长度:
echo ${#array} 数组内元素个数
echo ${#array[2]} 数组内下标索引为2的元素长度
给数组某个下标赋值:
array[0]="lily" 给数组下标索引为1的元素赋值为lily
array[20]="hanmeimei" 在数组尾部添加一个新元素
删除元素:
unset array[2] 清除元素
unset array 清空整个数组
分片访问:
${array[@]:1:4} 显示数组下标索引从1开始到3的3个元素,不显示索引为4的元素
内容替换:
${array[@]/an/AN} 将数组中所有元素内包含kobe的子串替换为mcgrady
数组遍历:
for v in ${array[@]}
do
echo $v
done
6、declare -x
声明为环境变量,可以在脚本中直接使用
取消声明的变量:
declare +r
declare +i
declare +a
declare +X
bash 数学运算
expr
语法 | |
---|---|
1 | expr $num1 operator $num2 |
2 | result= ( ( (( ((num1 operator $num2)) |
相等推荐expr
常和命令替换搭配使用 包裹在``中
操作符列表
操作符 | 意义 |
---|---|
+ | 加法 |
- | 减法 |
* | 乘法 |
/ | 除法 |
% | 取模 |
= | 等于 |
!= | 不等于 |
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
: | 正则表达式匹配 |
|| | 逻辑或 |
&& | 逻辑与 |
( ) | 用于分组 |
bash数学运算之expr:
num1=20
num2=100
expr $num1 \| $num2
expr $num1 \& $num2
expr $num1 \< $num2
expr $num1 \< $num2
expr $num1 \<= $num2
expr $num1 \> $num2
expr $num1 \>= $num2
expr $num1 = $num2
expr $num1 != $num2
expr $num1 + $num2
expr $num1 - $num2
expr $num1 \* $num2
expr $num1 / $num2
expr $num1 % $num2
练习例子:
提示用户输入一个正整数num,然后计算1+2+3+...+num的值;必须对num是否为正整数做判断,不符合应当允许再此输入
#!/bin/bash
#
while true
do
read -p "Pls enter a positive integer(num>0): " num
expr $num + 1 &> /dev/null
if [ $? -ne 0 ];then
echo "Error,You must input a interger"
continue
else
if [ `expr $num \> 0` -ne 1 ];then
echo "Error,You must input a postive interger"
continue
else
sum=0
for((i=0;i<=$num;i++))
do
sum=`expr $sum + $i`
done
echo "1+2+3+4+5+...+$num=$sum"
fi
fi
done
bc
bash数学运算之bc:
脚本中使用bc运算的语法:
echo "options;expression" | bc
num1=23.5
num2=50
var1=`echo "scale=2;$num1 * $num2" | bc`
函数
linux shell的函数和大多数编程语言的函数一样——复用代码
函数定义和使用
函数定义和使用:
函数定义的两种语法:
第一种:
name()
{
command1
command2
.....
commandn
}
第二种:
function name
{
command1
command2
.....
commandn
}
函数使用:
调用函数直接使用函数名即可,相当于一条命令,但不显示指定参数
* 函数内部直接使用参数$1 $2 ... $n
* function $1 $2
定义第一个函数:
function hello ==> hello() { echo "xxxxxx"} hello
{
echo "Hello,Zhangsan"
}
hello
定义第二个函数:
function print_num
{
for((i=0;i<=10;i++))
do
echo -n "$i "
done
}
定义第3个函数:
需求描述:写一个监控nginx的脚本;如果Nginx服务宕掉,则该脚本可以检测到并将进程启动;如果正常运行,则不做任何处理
#!/bin/bash
#
this_pid=$$
function nginx_daemon
{
status=$(ps -ef | grep -v $this_pid | grep nginx | grep -v grep &> /dev/null)
if [ $? -eq 1 ];then
systemctl start nginx && echo "Start Nginx Successful" || echo "Failed To Start Nginx"
else
echo "Nginx is RUNNING Well"
sleep 5
fi
}
while true
do
nginx_daemon
done
可选:在终端命令行定义函数:
在脚本中定义好disk_usage函数,然后直接使用. test.sh,再使用declare -F查看,是否可以列出disk_usage函数
function disk_usage
{
if [ $# -eq 0 ];then
df
else
case $1 in
-h)
df -h
;;
-i)
df -i
;;
-ih|-hi)
df -ih
;;
-T)
df -T
;;
*)
echo "Usage: $0 { -h|-i|-ih|-T }"
;;
esac
fi
}
向函数传递参数
调用函数的方式:
- int num =func(n1,n2);
- int num; num = func(n1,n2);
- func n1 n2
向函数传递参数:
函数传参和给脚本传参类似,都是使用$1 $2 $3 $4 $5 $6 $7这种方式
例子1:
需求描述:写一个脚本,该脚本可以实现计算器的功能,可以进行+-*/四种计算。
例如:sh calculate.sh 30 + 40 | sh calculate.sh 30 - 40 | sh calculate.sh 30 * 40
实现:
#!/bin/bash
#
function calculate
{
case "$2" in
+)
echo "$1 + $3 = $(expr $1 + $3)"
;;
-)
echo "$1 + $3 = $(expr $1 - $3)"
;;
\*)
echo "$1 * $3 = $(expr $1 \* $3)"
;;
/)
echo "$1 / $3 = $(expr $1 / $3)"
;;
esac
}
calculate $1 $2 $3
函数返回值
两种返回值
- return
只能返回1-255的整数
函数使用return返回值,通常只是用来供其他地方调用获取状态,通常仅返回0或1;0:成功,1:失败 - echo
使用echo返回任何字符串结果
用于返回数据,比如一个字符串值或者列表值
函数返回值:
使用return返回值:
例子:测试nginx是否在运行 is_nginx_running.sh
#!/bin/bash
#
this_pid=$$
function is_nginx_running
{
ps -ef | grep nginx | grep -v $this_pid | grep -v grep > /dev/null 2>&1
if [ $? -eq 0 ];then # 正常存在nginx
return 0
else
return 1
fi
}
is_nginx_running && echo "Nginx is running" || echo "Nginx is stopped"
使用echo返回值:
例子1:两数字相加 add.sh
#!/bin/bash
#
function add
{
echo "`expr $1 \+ $2`"
}
sum=`add $1 $2`
echo "$1 + $2 = $sum"
例子2:返回Linux上所有的不可登陆用户 unlogin.sh
#!/bin/bash
#
function get_users
{
echo `cat /etc/passwd | awk -F: '/\/sbin\/nologin/{print $1}'`
}
index=1
for user in `get_users`;do
echo "The $index user is $user"
index=$(expr $index + 1)
done
echo
echo "System have $index users(do not login)"
局部变量和全局变量
- 全局变量
Shell脚本中,默认所有变量都是全局变量
即使函数内部定义的变量,一旦被调用后,改变了就将一直存在,直到脚本执行完毕 - 局部变量
定义局部变量,使用local关键字
函数内外若同时存在同名变量,函数内部变量覆盖外部变量 -
编程习惯原则:
1、尽量在函数内部使用local关键字,将变量的作用于限制在函数内部
2、命名变量名时尽可能遵循实义性的,尽量做到见名知意
例子1:
#!/bin/bash
#
variable_1="Global Variable"
function local_func
{
variable_2="Local Variable"
}
echo "variable_1=$variable_1"
echo "variable_2=$variable_2"
local_func
echo "variable_1=$variable_1"
echo "variable_2=$variable_2"
function test_local
{
echo "variable_2=$variable_2"
}
test_local
函数的递归调用
函数递归调用:
经典案例:
求数字阶乘,例如5!
思路分析:
5!= 5 * 4!
= 5 * 4 * 3!
= 5 * 4 * 3 * 2!
= 5 * 4 * 3 * 2 * 1!
= 5 * 4 * 3 * 2 * 1
代码如下:
#!/bin/bash
#
function factial
{
if [ $1 -eq 1 ];then
echo "1"
return 0
fi
local temp=`expr $1 - 1`
local pre=`factial $temp`
result=`expr $1 \* $pre`
echo $result
return 0
}
read -p 'Please input a number: ' NUM
factial $NUM
库函数
将常用的重复代码封装成函数文件
一般不直接执行,而是由其他脚本调用
- 一般使用.lib
- 库文件通常没有可执行选项
- 库文件无需和脚本在同级目录,只需要在脚本中引用时指定
- 第一行一般使用#!/bin/echo,输出警告信息,避免用户执行
> vim base_function
#!/bin/echo Warning:This is a library which should not be executed,only be sourced in you scripts
#
function print_platform
{
local osname=`uname -s`
PLATFORM=UNKNOW
case "$osname" in
"FreeBSD")
PLATFORM="FreeBSD"
;;
"SunOS")
PLATFORM="Solaris"
;;
"Linux")
PLATFORM="Linux"
;;
esac
return 0
}
function add
{
echo `expr $1 + $2`
}
function reduce
{
echo `expr $1 - $2`
}
function multiple
{
echo `expr $1 \* $2`
}
function divide
{
echo `expr $1 / $2`
}
function sys_load {
echo "Memory Info"
echo
free -m
echo
echo "Disk Usage"
echo
df -h
echo
}
> vim cal.sh # 和 base_function 在同一级目录
#!/bin/bash
#
. base_function # 默认搜索路径 . 有指定路径
. /xx/xx/base_function # 使用绝对路径
add 12 23
reduce 90 30
sys_load
find命令
语法格式: find [路径] [选项] [操作]
选项 | 意义 |
---|---|
-name pattern | 按照文件名查找 区分大小写 |
-iname pattern | 按照文件名查找 不区分大小写 |
-type type | 按照文件类型查找 |
-size n[ckMG] | 按照文件大小查找 |
-mtime -n | +n | 按照文件修改时间查找 |
-user name | 按照文件属主查找 |
-group name | 按照文件所属组查找 |
-perm mode | 按照文件权限查找 |
-exec command {} ; | 对搜索到的文件执行特定命令 |
输出搜索到的文件名 | |
-depth | 按深度优先搜索 |
-maxdepth levels | 最大深度 最多搜索到n级子目录 |
-mindepth levels | 最小深度 从几级子目录开始搜索 |
-newer file | 查找比指定文件更新的文件 |
-not | 反转匹配条件 |
-or | 匹配条件之间的逻辑或 |
-and | 匹配条件之间的逻辑与 |
-regex pattern | 按照正则表达式匹配查找 |
-nogroup | 无效属组查找 |
-nouser | 无效属主查找 |
-newer f1 ! f2 | 查找更改时间比file1新但是比f2旧的文件 |
find命令总结:
常用选项:
-name 查找/etc目录下以conf结尾的文件 find /etc -name '*conf'
-iname 查找当前目录下文件名为aa的文件,不区分大小写 find . -iname aa
-user 查找文件属主为hdfs的所有文件 find . -user hdfs
-group 查找文件属组为yarn的所有文件 find . -group yarn
-type
f 文件 find . -type f
d 目录 find . -type d
c 字符设备文件 find . -type c
b 块设备文件 find . -type b
l 链接文件 find . -type l
p 管道文件 find . -type p
-size
-n 大小大于n的文件
+n 大小小于n的文件
n 大小等于n的文件 很少用
例子1:查找/etc目录下小于10000字节的文件 find /etc -size -10000c
例子2:查找/etc目录下大于1M的文件 find /etc -size +1M
-mtime
-n n天以内修改的文件
+n n天以外修改的文件
n 正好n天修改的文件
例子1:查找/etc目录下5天之内修改且以conf结尾的文件 find /etc -mtime -5 -name '*.conf'
例子2:查找/etc目录下10天之前修改且属主为root的文件 find /etc -mtime +10 -user root
-mmin
-n n分钟以内修改的文件
+n n分钟以外修改的文件
例子1:查找/etc目录下30分钟之前修改的文件 find /etc -mmin +30
例子2:查找/etc目录下30分钟之内修改的目录 find /etc -mmin -30 -type d
-mindepth n 表示从n级子目录开始搜索 过滤掉一些目录
例子:在/etc下的3级子目录开始搜索 find /etc -mindepth 3
find /etc -mindepth 3 -type f
-maxdepth n 表示最多搜索到n-1级子目录
例子1:在/etc下搜索符合条件的文件,但最多搜索到2级子目录 find /etc -maxdepth 3 -name '*.conf'
例子2:
find ./etc/ -type f -name '*.conf' -size +10k -maxdepth 2
了解选项:
-nouser 查找没有属主的用户
例子:find . -type f -nouser
-nogroup 查找没有属组的用户
例子:find . -type f -nogroup
-perm 根据文件权限查找
例子:find . -perm 664
-prune 该选项可以排除某些查找目录
搭配 -path 一起使用 [-path ./test -prune] 排除test 目录 -o 链接别的参数 显示指定或or
通常和-path一起使用,用于将特定目录排除在搜索条件之外
例子1:查找当前目录下所有普通文件,但排除test目录
find . -path ./etc -prune -o -type f
例子2:查找当前目录下所有普通文件,但排除etc和opt目录
find . -path ./etc -prune -o -path ./opt -prune -o -type f
例子3:查找当前目录下所有普通文件,但排除etc和opt目录,但属主为hdfs
find . -path ./etc -prune -o -path ./opt -prune -o -type f -a -user hdfs
例子4:查找当前目录下所有普通文件,但排除etc和opt目录,但属主为hdfs,且文件大小必须大于500字节
find . -path ./etc -prune -o -path ./opt -prune -o -type f -a -user hdfs -a -size +500c
-newer file1
例子:find /etc -newer a
操作:
-prin 打印输出
-exec 对搜索到的文件执行特定的操作,格式为-exec 'command' {} \;
例子1:搜索/etc下的文件(非目录),文件名以conf结尾,且大于10k,然后将其删除
find ./etc/ -type f -name '*.conf' -size +10k -exec rm -f {} \;
例子2:将/var/log/目录下以log结尾的文件,且更改时间在7天以上的删除
find /var/log/ -name '*.log' -mtime +7 -exec rm -rf {} \;
例子3:搜索条件和例子1一样,只是不删除,而是将其复制到/root/conf目录下
find ./etc/ -size +10k -type f -name '*.conf' -exec cp {} /root/conf/ \;
-ok 和exec功能一样,只是每次操作都会给用户提示
逻辑运算符:
-a 与
-o 或
-not|! 非
例子1:查找当前目录下,属主不是hdfs的所有文件
find . -not -user hdfs | find . ! -user hdfs
例子2:查找当前目录下,属主属于hdfs,且大小大于300字节的文件
find . -type f -a -user hdfs -a -size +300c
例子3:查找当前目录下的属主为hdfs或者以conf结尾的普通文件
find . -type f -a -user hdfs -o -name '*.conf'
观察find . -type f -a -user hdfs -o -name '*.conf' -exec ls -rlt {} \;
find . -type f -a \( -user hdfs -o -name '*.conf' \) -exec ls -rlt {} \;
完整脚本练习:
需求描述:提示用户输入一个目录,然后继续提示用户输入一个搜索文件的查询条件(文件名、文件大小),然后脚本可以将符合搜索条件的文件打印出来
继续提示用户是拷贝或删除这些文件,如果删除,则执行删除操作,同时将删除的文件记录到一个remove.list文件中;
如果是拷贝,则继续提示用户输入一个目标目录,然后执行拷贝动作
其他文件查找命令
locate
文件查找命令 所属软件包mlocate
不同于find命令是在整块磁盘中搜索,locate命令在数据库文件中查找
- 数据库文件定时更新 ——延迟查询到
- find 默认全部匹配,locate 是默认部分匹配
updatedb - 用户更新/var/lib/mlocate/mlocate.db
- 使用配置文件 /etc/updatedb.conf
- 该命令在后台cron计划任务中定期执行
updatedb # 用户更新/var/lib/mlocate/mlocate.db
locate file_name
whereis
选项 | 含义 |
---|---|
-b | 只返回二进制文件 |
-m | 只返回帮助文档文件 |
-s | 只返回源代码文件 |
whereis mysql
whereis -b mysql
which
仅查找二进制程序文件
-b 只返回二进制文件
which mysql
find 功能最强大 最慢; locate 快功能单一; which 查程序绝对路径; whereis 不常用
grep和egrep
过滤器
grep语法格式:
> grep [option] [pattern] [file1,file2…]
> command | grep [option] [pattern]
必须掌握的选项:
-v 显示不匹配pattern的行
-i 搜索时忽略大小写
-n 显示行号
-E 支持扩展的正则表达式
-F 不支持正则表达式,按字符串的字面意思进行匹配
-r 递归搜索
需了解的选项:
-c 只输出匹配行的数量,不显示具体内容
-w 匹配整词 前后必须有空格
-x 匹配整行
-l 只列出匹配的文件名,不显示具体匹配行内容
grep和egrep:
grep默认不支持扩展正则表达式,只支持基础正则表达式
使用grep -E可以支持扩展正则表达式
使用egrep可以支持扩展正则表达式,与grep -E等价
grep -E "A|a" file
grep -F "py.*" file
grep -r like # 递归在当前文件查找like
sed
stream editor 流编辑器
对标准输出或文件进行处理
语法:
stdout | sed [option] “pattern command”
sed [option] “pattern command” file
**sed 的选项 **
选项 | 含义 |
---|---|
-n | 禁止自动输出模式空间内容 只打印匹配输出行 |
-e script | 在处理输入时将 script 应用到每一行 理解为and |
-f script-file | 在处理输入时将指定的脚本文件应用到每一行 |
-i | 直接修改文件内容,而不是输出到屏幕 |
-r | 启用扩展正则表达式 |
-s | 静默模式,不产生错误或诊断信息 |
-h | 不在输出中显示文件名 |
-V | 显示版本信息 |
sed 默认会打印一边原行信息
sed的内置命令
选项 | 含义 |
---|---|
a\ | 在匹配行后面追加一行文本 |
c\ | 用新文本替换匹配的行 |
d | 删除匹配的行 |
i\ | 在匹配行前面插入一行文本 |
p | 打印模式空间内容 |
s/regexp/replacement/ | 用 replacement 替换模式空间中第一个匹配的 regexp |
s/regexp/replacement/g | 用 replacement 替换模式空间中所有匹配的 regexp |
y/source-chars/dest-chars/ | 将模式空间中所有出现在 source-chars 中的字符替换成 dest-chars 中的对应字符 |
内置命令可以叠加组合 |
sed -n 'p' sed.txt
s/regexp 匹配模式或字符串/replacement 内置命令/
sed '/python/p' sed.txt # 匹配到python的打印
sed -n -e '/python/p' -e '/PYTHON/p' sed.txt
> vim edit.sed
> /python/p
sed -n -f edit.sed sed.txt
sed -n '/python|PYTHON/p' sed.txt # 不行 默认不支持正则 -r 启用扩展
sed -n -r '/python|PYTHON/p' sed.txt
sed -n 's/love/like/g;p' sed.txt # 完整编辑命令 每行处理
sed -i 's/love/like/g;p' sed.txt
# 在原文件内替换修改 可以理解为 vim-i s-search g-global
sed 中的pattern
在sed命令中,pattern模式是用来匹配文本的正则表达式。pattern模式可以用来过滤、选取和修改文本中的内容。
在sed中,pattern模式可以使用基本正则表达式(BRE)或扩展正则表达式(ERE)语法,具体取决于是否使用了选项参数-r或-E。
常用的pattern模式示例:
^pattern:以pattern开头的行。
pattern$:以pattern结尾的行。
/pattern/:包含pattern的行。
!pattern:不包含pattern的行。
n,m:n到m行。
n~m:从第n行开始,每m行执行一次。
除了这些基本的pattern模式外,还可以使用正则表达式中的元字符和修饰符,例如.、*、+、?、[…]、{n,m}等。
匹配模式 | 含义 |
---|---|
10command | 匹配到第10行 |
10,20command | 匹配从第10行,到第20行结束 |
10,+5command | 匹配从第10行,到第15行结束 |
/pattern1/,/parttern2/command | //,//cmd 匹配到p1的行开始,到匹配到p2的行结束 |
10,/parttern1/command | num,//CMD 10行开始,到匹配到p1的行结束,没有匹配到会打到结尾 |
/parttern1/, 10command | //,numCMD 匹配到p1的行开始,到10行结束 |
/parttern1/command | //cmd 对所有行进行匹配 |
sed -n "17p" file # 打印文件到17行
sed -n "10,20p" file # 打印文件10到20行
sed -n "10,+5p" file # 打印文件10到15行
sed -n "/^root/p" file # 打印文件以root开头的行
sed -n "/\/sbin\/nologin/p" file # 匹配 /sbin/nologin 需要转意 //p 中'\/'
sed -n "/^hdfs/,/^yarn/p" /etc/passwd # 从hdfs匹配到yarn匹配结尾
sed中的编辑命令
混合匹配容易出错,不建议使用
| 类别 | 编辑命令 | 含义 |
| ---- | ---- | ---- |
| 查询 | p | 打印 |
| 增加 | a | 行后追加 |
| 增加 | i | 行前追加 |
| 增加 | r | 外部文件读入,行后追加 |
| 增加 | w | 匹配行导出到外部文件 |
| 删除 | d | 删除 |
| 修改 | s/old/new/ | 将行内第一个old替换为new |
| 修改 | s/old/new/g | 将行内全部old替换为new |
| 修改 | s/old/new/2g | 将行内从第2个到最后的old替换为new |
| 修改 | s/old/new/ig | 忽略大小写后,将行内全部old替换为new |
| 显示行号 | s/pattren/= | |
sed -i '1d' passwd # 删除第一行
sed -i '1,3d' passwd # 删除第一到第三行
sed -i '1d' passwd # 删除第一行
# sed -i '//d' file 根据匹配模式删除
sed -i '/\/sbin\/nologin/d' passwd
# sed -i '//a [append_string 有空格也不影响]' file 行后追加
sed -i '/\/bin\/bash/a appending string' passwd
# sed -i '/root/r list[当前路径下的list文件]' passwd
sed '/\/bin\/bash/w /tmp/user_login.txt' passwd
# 替换 sed -i '///g' file
sed -i '/\/bin\/bash/\/BIN\/BASH/g' passwd
sed -i '/hadoop/HADOOP/ig' passwd
sed -i '/hadoop/=' passwd
# 删除空行和注释行
sed -i '/^#/d;/^$/d' xx.conf
# 删除多个空格+# 的注释
sed - '/[:blank:]*#/d' xx.conf
# 在非#开头的行前添加*
sed -i 's/^[^#]/\*&/g' nginx.conf
# 修改用法总结:
1、1s/old/new/
2、5,10s/old/new/
3、10,+10s/old/new/
# 将匹配到的行,在进行new替换old
4、/pattern1/s/old/new/
5、/pattern1/,/pattern2/s/old/new/
6、/pattern1/,20s/old/new/
7、15,/pattern1/s/old/new/
# 修改/etc/passwd中从匹配到以root开头的行,到匹配到行中包含mail的所有行。修改内为将这些所有匹配到的行中的bin改为HADOOP
# sed -i '//,//s/ / /g' /etc/passwd 从框架开始填充
sed -i '/^root/,/mail/s/bin/hadoop/g' /etc/passwd
# 删除数字
sed -i 's/[0-9]*//g' file.txt
## 追加用法总结:
1、a 在匹配行后面追加
2、i 在匹配行前面追加
3、r 将文件内容追加到匹配行后面
4、w 将匹配行写入指定文件
# 将passwd文件从第10行开始,到匹配到hdfs开头的所有行内容追加到/tmp/sed-1.txt
sed -i '10,/^hdfs/w /tmp/sed-1.txt' passwd
sed中的反向引用
在sed中,反向引用是指在替换命令中使用正则表达式捕获组中的内容,然后在替换文本中引用它们。
使用反向引用,可以将模式空间中的一个字符串替换为另一个字符串,并将其中一个字符串的一部分保留下来。
> vim str.txt
hadAAp is a bigdata frame
Spark hadBBp Kafka
Skill on hadCCp
Paper Of hadDDp
Google hadFFp
# 将符合hadxxp的单词变为 hadxxps 此时不是简单的查找替换,而是在原基础上修改 引用
sed -i 's/had..p/&s/g' str.txt
# 此时的 & 指匹配到的单词 word
# &s 拼接s
> hadAAps is a bigdata frame
Spark hadBBps Kafka
Skill on hadCCps
Paper Of hadDDps
Google hadFFps
# 反向引用符号 & 可以用 \1 替换 表示用匹配到的第几部分替换引用
# () 又需要用\(\) 转意
sed -i 's/\(had..ps\)/\1O/g' str.txt
> hadAApsO is a bigdata frame
Spark hadBBpsO Kafka
# 只作部分替换
sed -i 's/\(had\)...../\1doop/g' str.txt
sed -i 's/l\(..e\)/L\1/g' str.txt # 将 lxxe 改为Lxxe
old_str=hadoop
new_str=HADOOP
sed -i 's/'$old_str'/'$new_str'/g' str.txt
sed -i "s/$old_str/$new_str/g" str.txt
- 存在变量时用双引号
- 单引号内 ,自定义变量则也必须要使用单引号
- & 只能引用整个字符串
- \1 可以用()进行部分匹配到的字符串
统计配置文件段参数
#!/bin/bash
#
FILE_NAME=my.cnf
function get_all_segments {
echo "`sed -n '/\[.*\]/p' ${FILE_NAME} | sed -e 's/\[//g' -e 's/\]//g' `"
}
function count_items_in_segment {
# 过滤出[seg1] [seg2] 之间的数据,再grep 掉必须得 # 空白行 [seg]
items=`sed -n '/\['$1'\]/,/\[.*\]/p' ${FILE_NAME} | grep -v "^#" | grep -v "^$" | grep -v "\[.*\]"`
index=1
for item in $items
do
index=`expr $index + 1`
done
echo ${index}
}
num=0
for seg in `get_all_segments`
do
num=`expr num + 1`
item_count=`count_items_in_segment ${seg}`
echo "${num}: ${seg} ${item_count}"
done
awk
awk 工作模式
可以做很复杂的编程,做很精细的结果控制
awk是一个文本处理工具,通常用于处理数据并生成结果报告
语法格式
awk ‘BEGIN{} pattern{commands} END{}’ file_name
standard output | awk ‘BEGIN{} pattern{commands} END{}’
- BEGIN: 类似于初始化, 正式数据处理之前执行
- pattern : 匹配模式
- {commands}: 处理命令,可能多行
- END: 处理完所有匹配数据后执行
awk的内置变量
变量名 | 意义 |
---|---|
$0 | 当前记录整行的文本内容。 |
$1 - $n | 当前记录的第一个到n字段的文本内容。 |
NF number field | 当前行的字段个数,有多少列;每行数据不一定列数相同 |
NR number row | 当前行的行号 从1开始计数,多文件累计计数 |
FNR file number row | 多文件处理时,每个文件的行号单独计数, 从0开始计数 |
FS field separator | 输入字段分隔符 不指定默认 空格或者tab |
RS row separator | 输入行分隔符 默认回车 |
OFS output field separator | 输出字段分隔符,默认 空格 |
ORS output row separator | 输出记录分隔符, 默认回车 |
FILENAME | 当前输入的文件名字 |
ARGC | 命令行参数个数 |
ARGV | 命令行参数数组 |
awk 'BEGIN{} patern {command} END{}'
# 输出每一行数据 省略:BEGIN patern END
awk '{print $0}' /etc/passwd
# 指定分隔符
awk 'BEGIN{FS=":"}{print $3}'/etc/passwd
# 打印每行字NR
awk '{print NF}' list
# 打印行号
awk '{print NR}' list1 list2
awk '{print FNR}' list1 list2
awk 'BEGIN{FS=":";RS="--";ORS="&";OFS=":"}{print $1,$3}'/etc/passwd
printf的格式说明符
格式符 | 含义 |
---|---|
%s | 字符串 |
%d | 十进制整数 |
%f | 浮点数 |
%c | 字符 ascii码 |
%e | 科学计数法 |
%g | 自动选择合适的计数法 |
%o | 八进制整数 |
%x | 十六进制整数(小写字母) |
%X | 十六进制整数(大写字母) |
%% | 百分号 |
- | 左对齐 |
+ | 右对齐 |
# | 显示8进制前面加0,16进制0x |
# 强制指定第一个字段占20个字符,不足补齐,默认右对齐
awk 'BEGIN{FS=":"}{printf "%20s %s\n",$1,$7}' /etc/passwd
# %-20s 左对齐
awk 'BEGIN{FS=":"}{printf "%20s %s\n",$1,$7}' /etc/passwd
awk 'BEGIN{FS=":"}{printf "%s\n",$7}' /etc/passwd
# 16进制加标志 # "%e\n" 科学计数法
awk 'BEGIN{FS=":"}{printf "%#x\n",$3}' /etc/passwd
awk的模式匹配
parren部分的语法
语法格式 | 含义 |
---|---|
RegExp | 按正则表达式匹配 |
关系运算 | 按关系运算匹配 |
关系运算: < > <= >= == ~ 匹配正则表达式 !~ 不符合正则表达式 != || && !
# 只要包含root的行
awk 'BEGIN{FS=":"}/root/{print $0}' /etc/passwd
# yarn 开头的
awk 'BEGIN{FS=":"}/^yarn/{print $0}' /etc/passwd
# uid < 50
awk 'BEGIN{FS=":"}$3<50{print $0}' /etc/passwd
# == "/bin/bash"
awk 'BEGIN{FS=":"}$7=="/bin/bash"{print $0}' /etc/passwd
# uid包含3个数字以上的 : ~ 匹配上 0-9 3次以上
awk 'BEGIN{FS=":"}$3~/[0-9]{3,}/"/bin/bash"{print $0}' /etc/passwd
# 同时包含 hdfs 和yarn的
awk 'BEGIN{FS=":"}$1=="hdfs" ||$1=="yarn" {print $0}' /etc/passwd
# 3字段<50 4字段>50
awk 'BEGIN{FS=":"}$3<50 && $4>50{print $0}' /etc/passwd
# 3字段<50 并 是 /bin/bash 登录的 => 先填充 $7~// => $7~/ /bin/bash /
# 在转义 /bin/bash => \/bin\/bash/
awk 'BEGIN{FS=":"}$3>50 && $7~/\/bin\/bash/ {print $0}' /etc/passwd
awk动作中的表达式用法
动作部分action通常是由一个或多个表达式和操作符组成的:
变量和常量;算术运算符; 关系运算符;逻辑运算符;正则表达式; 函数调用;条件语句;
算术运算符: + - * / % ^或** 乘方 ++x x++
# 可以把BEGIN理解为awk 函数中的初始化代码块,会在遍历每行数据之前执行一次
awk 'BEGIN{var=20;var1="hello";print var,var1}'
awk 'BEGIN{num1=20;num2 +=num1;print num1,num2}'
awk 'BEGIN{num1=20;num2 =30;printf "%0.2f\n", num1/num2}'
# 后置自增 i++ && 后置自减 i-- : 先将变量值传递出去,再修改变量值
# 前置自增 ++i && 前置自减 --i:首先修改变量的值,再将值传递出去
# x++ 是x先赋值,x再++
awk 'BEGIN{x=20;y=x++;print x,y}'
> 21 20
awk 'BEGIN{x=20;y=++x;print x,y}'
> 21 21
# 统计/etc/services的空白行
# 空白行 ^$
# 变量默认初始值为0
awk '/^$/{sum++}END{print sum}' /etc/services
> vim student.txt
zhang,24,90,85,88,94
wang,22,70,80,75,85
li,31,80,90,60,55
mike,22,70,80,75,85
han,31,80,90,60,55
# 计算每个学生的平均分
awk 'BEGIN{FS=","}{total=$2+$3+$4+$5+$6;AVG=total/5;printf "%s,%d,%d,%d,%d,%d,%f\n",$1,$2,$3,$4,$5,$6,AVG}' student.txt
# 调整格式
awk 'BEGIN{FS=","}{total=$2+$3+$4+$5+$6;AVG=total/5;printf "%-8s%-5d%-5d%-5d%-5d%-8d%0.2f\n",$1,$2,$3,$4,$5,$6,AVG}' student.txt
# 加标题
awk 'BEGIN{FS=",";printf "%-8s%-8s%-8s%-8s%-8s%-8s%s\n","姓名","科1","科2","科3","科4","科5","平均分"}{total=$2+$3+$4+$5+$6;AVG=total/5;printf "%-8s%-8d%-8d%-8d%-8d%-8d%0.2f\n",$1,$2,$3,$4,$5,$6,AVG}' student.txt
在上面的基础上增加条件语句
借鉴了c语言语法风格
变量会自动初始化,无需专门定义
代码块用{ } 包裹;每一句语句之间 ; 分割 其他的和常规的语言语法相差不大
条件语句: if(条件表达式) 动作1 else if(条件表达式) 动作2 else 动作3
# uid <50
# 模式匹配
awk 'BEGIN{FS=":"}$3<50{print $0}' /etc/passwd
# 使用条件表达式,省略pattern 部分 过滤掉# 开头的行
awk 'BEGIN{FS=":"}/^[^#]/{if($3<50){printf "%-15s%-15s%-5d\n","小于50的uid",$1,$3}}' /etc/passwd
# 增加对小于100的逻辑
awk 'BEGIN{FS=":"}/^[^#]/{if($3<50){printf "%-15s%-15s%-5d\n","小于50的uid",$1,$3}else if($3<100){printf "%-15s%-15s%-5d\n","50~100的uid",$1,$3}}' /etc/passwd
> vim scripts.awk
BEGIN{FS=":"}
/^[^#]/{
if($3<50){
printf "%-30s%-30s%-5d\n","小于50的uid",$1,$3
}else if($3<100){
printf "%-30s%-30s%-5d\n","50~100的uid",$1,$3
}else {
printf "%-30s%-30s%-5d\n","大于100的uid",$1,$3
}
}
awk -f scripts.awk /etc/passwd
循环语句 do-while
do while
do
动作
while(条件表达式)
循环语句 for
for(初始化计数器;测试计数器;计数器变更)
动作
# 计算1+2+3+4+...+100的和,用while、do while、 for 实现
> vim while.awk
BEGIN{
while(i<=100){
sum+=i
i++
}
print sum
}
awk -f while.awk
>vim do-while.awk
BEGIN{
do{
sum+=i
i++
}while(i<=100)
print sum
}
awk -f do-while.awk
> vim for.awk
BEGIN{
for(i=0;i<=100;i++){
sum+=i
}
print sum
}
awk -f for.awk
# 只打印平均分>=65的信息
> vim student.awk
BEGIN{
FS=",";printf "%-8s%-8s%-8s%-8s%-8s%-8s%s\n","姓名","科1","科2","科3","科4","科5","平均分"
}
{
total=$2+$3+$4+$5+$6;
AVG=total/5;
if(AVG>=65){
printf "%-8s%-8d%-8d%-8d%-8d%-8d%0.2f\n",$1,$2,$3,$4,$5,$6,AVG
}
}
awk -f student.awk student.txt
# 统计每个科目的平均分
> vim student_score.awk
BEGIN{
FS=",";printf "%-8s%-8s%-8s%-8s%-8s%-8s%s\n","姓名","科1","科2","科3","科4","科5","平均分"
}
{
total=$2+$3+$4+$5+$6;
AVG=total/5;
score_1 +=$2
score_2 +=$3
score_3 +=$4
score_4 +=$5
score_5 +=$6
printf "%-8s%-8d%-8d%-8d%-8d%-8d%0.2f\n",$1,$2,$3,$4,$5,$6,AVG
}
END{
printf "%-8s%-8s%-8s%-8s%-8s%-8s\n","科目总分",score_1,score_2,score_3,score_4,score_5
printf "%-8s%-8s%-8s%-8s%-8s%-8s\n","科目平均分",score_1/NR,score_2/NR,score_3/NR,score_4/NR,score_5/NR
}
awk -f student_score.awk student.txt
awk中的字符串函数
内置的字符串函数意味着,在awk的语句中可以直接使用,无须额外定义function
awk字符串函数对照表:
函数名 | 解释 | 函数返回值 |
---|---|---|
length(string) | 字符串的长度 | |
index(str1, str2) | 在str1中查找str2的位置 | 返回str1中位置的索引计数,从1开始 |
substr(str, m, n) | 在ste的m个字符开始,截取n位 | 截取后的子字符串 |
split(string, arr, fs) | 将字符串按fs分隔,存放在数组arr中 | 分隔后的子字符串个数 arr数组下标从1开始 |
tolower(str) | 转为小写 | |
toupper(str) | 转为大写 | |
sub(regexp, RepStr, str) | 在str中搜索符合RE的字串,将其替换为RepStr,只替换第一个 | 被替换的字符串个数 1/0 |
gsub(regexp, RepStr, str) | 在str中搜索符合RE的字串,将其全部替换为RepStr | 被替换的字符串个数 0~N |
match(string, regexp) | 在字符串中按照RE查找,并返回匹配的位置 | 返回索引位置数组(位置和长度) |
# 返回每个字段的长度
> vim field.awk
BEGIN{FS=":"}
{
i=1
while(i<=NF)
{
if(i==NF)
{
printf "%d",length($i)
}
else
{
printf "%d:",length($i)
}
i++
}
printf "\n"
}
awk -f field.awk /etc/passwd
# 搜索’ea‘子串的位置
awk 'BEGIN{str="I have a dream";location=index(str,"ea");print location;}'
awk 'BEGIN{str="I have a dream";location=match(str,"ea");print location;}'
# 转大小写
awk 'BEGIN{str="I Have a Dream";print tolower(str);}'
awk 'BEGIN{str="I have a dream";print toupper(str);}'
# 分割字符并保存到array中 index 是乱序
awk 'BEGIN{str="Hadoop Kafka Spark Storm HDFS YARN Zookeeper";split(str,arr," ");for(idx in arr) print arr[idx];}'
# 第一个数字出现的位置 /[0-9]/ 标志这是一个正则匹配
awk 'BEGIN{str="Tranction 2345 Start";location=match(str,/[0-9]/);print location;}'
# 截取子串
awk 'BEGIN{str="Tranction 2345 Start";print substr(str,4,5);}'
# 将第一个匹配到的数字串替换为"$"
# /[0-9]+/ 匹配多个数字
awk 'BEGIN{str="Tranction 243 Start,Event ID:9002";sub(/[0-9]+/,"$",str);print str}
# 全部数字替换
awk 'BEGIN{str="Tranction 243 Start,Event ID:9002";gsub(/[0-9]+/,"$",str);print str}'
awk中的常用选项
选项 | 解释 |
---|---|
-v | 定义或引用变量 |
-f | 引入指定awk命令文件 |
-F | 指定列的分隔符 |
-V | 查看awk的版本号 |
awk并不能访问到环境变量或者自定义变量,需要用-v 引入
-F “:” 等价于 BEGIN{ FS=“:”}
> num1=100
> var="hello world"
awk 'BEGIN {print num1,var}' > 打印不出来
# -v 后 ${变量名} 能够获取到变量值; num1=xxx 则是定义一个awk上下文内的一个变量num1
awk -v num1=$num1 -v var=$var 'BEGIN {print num1,var}'
awk -F ":" '/root/{print $0}' /etc/passwd
awk中的数组用法
awk 的数组用法和array相差不大
在shell 中数组的用法:
array=("Allen" "Mike" "Messi" "Jerry" "Hanmeimei" "Wang")
# 打印元素:
echo ${array[2]}
# 打印元素个数: 可以把 # 理解为计算长度的符号 @理解为展开数组中的所有元素
echo ${#array[@]}
echo ${#str} # 字符串长度
# 打印元素的长度: 第三个元素 messi的长度
echo ${#array[3]}
# 给元素赋值:
array[3]="Li" => echo ${#array[3]} > 3
# 删除元素:
unset array[2]
# 删除数组
unset array
# 分片访问:下标从0开始 => 第1个元素开始取3个
echo ${array[@]:1:3}
# 只替换每个元素的第一个e;
${array[@]/e/E}
# 类比: 在sed 命令中 s/old/new/g 替换全部;s/old/new/ 只替换一个
# 每个元素的内容,替换所有的e
${array[@]//e/E}
# 数组的遍历:
for a in ${array[@]//e/E}
do
echo $a
done
awk的数组最重要的特点是:元素下标支持字符串,其实可以理解为兼容数组操作的Map
awk使用demo
模拟日常工作中的日志的处理
思路: 制造假数据log日志 ==> awk 统计计算出结果
vim insert.sh
#!/bin/bash
#
function create_random()
{
min=$1
# 传入的随机数
max=$(($2-$min+1))
# 日期秒数+ 纳秒数的拼接:1681026501723177880
num=$(date +%s%s)
# 返回随机数 [函数的返回值两种类型:整数 或者 用echo拼接一个复杂的字符串返回]
# $(()) 双括号内做数学计算 -> result=$(($num1 operator $num2))
# 数值过大溢出 再次取模 排除负数情况
echo $(( $(( $num % $max + $max )) % $max ))
}
INDEX=1
while [ $INDEX -le 2000 ]
do
# 数组内容直接展开 arr=["allen" "mike" "jerry" "tracy" "han" "lilei"]
for user in allen mike jerry tracy han lilei
do
# shell内置随机数 echo ${RANDOM}
COUNT=$RANDOM
NUM1=`create_random 1 $COUNT`
NUM2=`expr $COUNT - $NUM1`
echo "`date '+%Y-%m-%d %H:%M:%S'` $INDEX Batches: user $user insert $COUNT records into database:product table:detail, insert $NUM1 records successfully,failed $NUM2 records" >> ./db.log.`date +%Y%m%d`
INDEX=`expr $INDEX + 1`
done
done
awk的计算样例
> vim log.awk
BEGIN{
printf "%+10s%+10s%+10s%+10s\n","USER","TOTAL","SUCCESS","FAIL"
}
{ # 按名字统计
TOTAL[$6]+=$8
SUCCESS[$6]+=$14
FAIL[$6]+=$17
# 统计总数
totalSum +=$8
successSUm +=$14
failedSum +=$17
}
END{
for(u in TOTAL){
printf "%+10s%+10s%+10s%+10s\n",u,TOTAL[u],SUCCESS[u],FAIL[u]
}
printf "%+10s%+10s%+10s%+10s\n","",totalSum,successSUm,failedSum
}
awk -f log.awk db.log.20230409
# 输出总数!=成功+失败的 手动改一些
awk '{if($8!=($14+$17))print NR,$0}' db.log.20230409
测试和判断
命令执行为0真,非0为假 => 判断行为称为测试
测试结构
test expression 或
[ 空格 expression 空格] => 以”[” 启动一个测试 ,执行expression ,再以”]”结束一个测试
必须严格的写空格
通常与 if case while 条件判断连用更方便
文件测试
test file_operator file/dir
[空格file_operator空格file/dir空格 ]
[空格 !空格 -e空格"$filename"空格 ]
文件测试符 file_operator
符号 | 说明 |
---|---|
-e | 文件存在 |
-f | 普通文件 |
-d | 目录 |
-h | 符号链接 |
-r | 可读 |
-w | 可写 |
-x | 可执行 |
-s | 文件大小不为0 |
-G | 文件属于当前用户组 |
-O | 文件属于当前用户 |
-nt | 新于某个文件 |
-ot | 旧于某个文件 |
-ef | 与某个文件指向相同的设备和节点号 |
test -e sed.txt
> 0
-e 是否存在
-s 文件是否为空
-nt f1 是否比 f2 新 newer than
-ot f1 是否比 f2 旧 older than
# 可以通过判断文件新旧来增量更新或备份文件
# 判断文件权限脚本
> vim rwx.sh
#! /bin/bash
# 读入屏幕输入数据存储到变量filename中
read -p "input file test:" filename
if [ ! -e "$filename" ];then
echo "The file does not exit"
exit 1
fi
if [ -r "$filename" ];then
echo "this is readable."
fi
if [ -w "$filename" ];then
echo "this is writeable."
fi
if [ -x "$filename" ];then
echo "this is executable."
fi
有错误的地方欢迎指出!!!