sed、awk、grep

各有侧重

文件上传工具:Tiny File Manager的数据备份时,用到sed处理记录文件。

  • sed 是一种流编辑器,主要用于对文本进行替换、删除、插入等操作。它逐行处理输入文本,并输出修改后的结果。
  • awk 是一种以字段为单位的文本处理语言,擅长数据提取、统计和格式化输出。
  • grep 是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。

grep 使用

在目录下,搜索所有文件与子目录的文件:grep -inr 'logdn' ./

过滤配置文件中被注释的行:grep -Ev '^$|^\s*#' /etc/nginx/nginx.conf

查找多个关键字:ps -Ao user,pid,ppid,%mem,rss,%cpu,cmd --sort=+ppid|grep -E 'PID|nginx|php-fpm|mariadb'

常用选项

  • -E:扩展正则表达式
  • -i:忽略大小写进行匹配。
  • -v:反向查找,只打印不匹配的行。
  • -n:显示匹配行的行号。
  • -r:递归查找子目录中的文件。
  • -l:只打印匹配的文件名。
  • -c:只打印匹配的行数。

grep 的一行多个关键字之间 and / or / not 匹配

与(AND)操作

  • 使用管道符将多个grep命令连接起来,这样只有同时满足所有条件的行才会被显示。例如:grep 'pattern1' filename | grep 'pattern2'
  • 使用-E选项,结合正则表达式来实现与操作。例如:grep -E 'pattern1.*pattern2' filename或者grep -E 'pattern1.*pattern2|pattern2.*pattern1' filename,区别在于是否有先后出现顺序。

或(OR)操作

  • 使用转义字符将管道符转为“或”符号:grep 'pattern1\|pattern2' filename
  • 使用-E选项:grep -E 'pattern1|pattern2' filename或者egrep 'pattern1|pattern2' filename
  • -e选项:grep -e pattern1 -e pattern2 filename

非(NOT)操作

  • 使用-v选项,这样可以显示不符合指定模式的所有行。例如:grep -v 'pattern' filename

sed使用

Bash
sed -i '1i\'"${outText}" ${targetDIR}/${logFileName} # 将内容插入到第1行的前面,注意变量引用方式,"1i\${outText}"是无效的。'
  • -i 是指编辑已有文件${targetDIR}/${logFileName},如果没有此参数,只是显示处理后结果,文件不会被修改。
  • '1i\'"${outText}"是指在第一行的前面,插入内容${outText}
    • 1代表行数,需要在文件中存在,否则会处理失败,且不会有报错提示。
    • i\代表行的前面插入内容,而a\代表行的后面插入内容。
Bash
sed -i "s|${targetDIR}||" ${targetDIR}/${logFileName} # 替换targetDIR为空
  • "s|${targetDIR}||"是查找变量值+替换为空,也就是删除。一般格式是's/查找内容/替换内容/',由于${targetDIR}是路径,包含了[/],所以使用[|]做为分隔符。也可使用其他特殊字符,做为分隔的定界符,比如[:]

更多:

  • 删除空白行:sed '/^$/d' file
  • 删除文件的第2行:sed '2d' file
  • 删除文件的第2行到末尾所有行:sed '2,$d' file
  • 删除文件最后一行:sed '$d' file
  • 删除文件中所有开头是test的行:sed '/^test/'d file

正则表达式替换

Bash
echo this is digit 7 in a number | sed 's/digit ([0-9])/\1/'
this is 7 in a number

awk

数值计算见:Shell中的运算符

awk [options] 'script' var=value file(s)
awk [options] -f scriptfile var=value file(s)

常用命令选项

  • -F fs fs指定输入分隔符,fs可以是字符串或正则表达式,如-F:
    • 默认使用空格与制表符作为字段分隔符,有多少个空格就分开几列,所以要避免有连续空格,或行开头就是空格。
    • -F'[ ]+'表示一个或多个空格作为分隔符。
  • -v var=value 赋值一个用户定义变量,将外部变量传递给awk
  • -f scripfile 从脚本文件中读取awk命令
  • -m[fr] val 对val值设置内在限制,-mf选项限制分配给val的最大块数目;-mr选项限制记录的最大数目。这两个功能是Bell实验室版awk的扩展功能,在标准awk中不适用。

内容提取

场景:将指定目录的文件大小与文件名,生成到一个文件里。

ll命令的输出:

total 140M
drwxrwxr-- 2 pubFiles pubFiles 4.0K Nov 28 09:30 ./
drwxr-xr-x 5 root     root     4.0K Nov 26 20:20 ../
-rw-r--r-- 1 lat      ubuntu   110M Jul 26 06:44 AxureRP-Setup-3754.exe
-rw-r--r-- 1 root     root      428 Nov 28 11:31 list.txt
-rw-r--r-- 1 lat      ubuntu    25M Oct 20 20:28 rclone-v1.71.2-linux-amd64.deb
-rw-r--r-- 1 lat      ubuntu   1.4M Jan 25  2025 rustdesk-server-hbbr_1.1.14_amd64.deb
-rw-r--r-- 1 lat      ubuntu   4.1M Jan 25  2025 rustdesk-server-hbbs_1.1.14_amd64.deb
Bash
echo `date` > /data/downloads/list.txt
# tee file.txt 命令可以同时输出到屏幕+文件里,来代替 >>
ls -lhF /data/downloads | awk '{print $5,"\t",$9}' >> /data/downloads/list.txt
cat /data/downloads/list.txt
-rw-r--r-- 1 lat      ubuntu   110M Jul 26 06:44 AxureRP-Setup-3754.exe

中间是空格分隔,连续多个视为一个。

  • $0是整行内容
  • $1是-rw-r–r–
  • $2是1
  • $3是lat
  • $4是ubuntu
  • $5是110M
  • $6是Jul
  • $7是26
  • $8是06:44
  • $9是AxureRP-Setup-3754.exe,可以看出上面命令awk '{print $5,"\t",$9}'在文件名有空格时,会不符合预期输出。
110M    AxureRP-Setup-3754.exe
0       list.txt
25M     rclone-v1.71.2-linux-amd64.deb
1.4M    rustdesk-server-hbbr_1.1.14_amd64.deb
4.1M    rustdesk-server-hbbs_1.1.14_amd64.deb

awk脚本基本结构

awk 'BEGIN{ print "start" } pattern{ commands } END{ print "end" }' file
awk "BEGIN{ i=0 } { i++ } END{ print i }" filename

BEGIN语句块、能够使用模式匹配的通用语句块、END语句块3部分组成,这三个部分是可选的。任意一个部分都可以不出现在脚本中,脚本通常是被 单引号 或 双引号 中。

awk的工作原理

awk 'BEGIN{ commands } pattern{ commands } END{ commands }'
  • 第一步执行BEGIN{ commands }语句块中的语句;
  • 第二步从文件或标准输入(stdin)读取一行,然后执行pattern{ commands }语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
  • 第三步当读至输入流末尾时,执行END{ commands }语句块。

示例

$ echo -e "A line 1\nA line 2" | awk 'BEGIN{ print "Start" } { print } END{ print "End" }'
Start
A line 1
A line 2
End

使用不带参数的print时,它就打印当前行,当print的参数是以逗号进行分隔时,打印时则以空格作为定界符。

内置参数

说明:[A][N][P][G]表示第一个支持变量的工具,[A]=awk、[N]=nawk、[P]=POSIXawk、[G]=gawk

     **$n**  当前记录的第n个字段,比如n为1表示第一个字段,n为2表示第二个字段。 
     **$0**  这个变量包含执行过程中当前行的文本内容。
[N]  **ARGC**    命令行参数的数目。
[G]  **ARGIND**  命令行中当前文件的位置(从0开始算)。
[N]  **ARGV**    包含命令行参数的数组。
[G]  **CONVFMT**  数字转换格式(默认值为%.6g)。
[P]  **ENVIRON**  环境变量关联数组。
[N]  **ERRNO**    最后一个系统错误的描述。
[G]  **FIELDWIDTHS**  字段宽度列表(用空格键分隔)。
[A]  **FILENAME**     当前输入文件的名。
[P]  **FNR**          同NR,但相对于当前文件。
[A]  **FS**           字段分隔符(默认是任何空格)。
[G]  **IGNORECASE**   如果为真,则进行忽略大小写的匹配。
[A]  **NF**    表示字段数,在执行过程中对应于当前的字段数。
[A]  **NR**    表示记录数,在执行过程中对应于当前的行号。
[A]  **OFMT**  数字的输出格式(默认值是%.6g)。
[A]  **OFS**   输出字段分隔符(默认值是一个空格)。
[A]  **ORS**   输出记录分隔符(默认值是一个换行符)。
[A]  **RS**    记录分隔符(默认是一个换行符)。
[N]  **RSTART**   由match函数所匹配的字符串的第一个位置。
[N]  **RLENGTH**  由match函数所匹配的字符串的长度。
[N]  **SUBSEP**   数组下标分隔符(默认值是34)。

由于NF为行的字段数,所以$NF表示为最后一个字段的内容,而$(NF-1)则是倒数第二个字段的内容;和$n一样,只是为最后一个。

示例:

$ echo -e "line1 f2 f3\nline2 f4 f5\nline3 f6 f7" | awk '{print "Line No:"NR", No of fields:"NF, "$0="$0, "$1="$1, "$2="$2, "$3="$3,"\t",$(NF-1),$NF}'
Line No:1, No of fields:3 $0=line1 f2 f3 $1=line1 $2=f2 $3=f3    f2 f3
Line No:2, No of fields:3 $0=line2 f4 f5 $1=line2 $2=f4 $3=f5    f4 f5
Line No:3, No of fields:3 $0=line3 f6 f7 $1=line3 $2=f6 $3=f7    f6 f7

示例解释

awk '{++S[\$NF]} END {for(a in S) print a, S[a]}'

{++S[$NF]}

  • $NF:代表当前行的最后一列(NF = Number of Fields,字段数量)
  • S[$NF]:使用最后一列的值作为关联数组 S 的键
  • ++S[$NF]:每次遇到这个值时,对应的计数器加1

END {for(a in S) print a, S[a]}

  • END:处理完所有行后执行
  • for(a in S):遍历数组 S 的所有键
  • print a, S[a]:打印键(最后一列的值)和对应的计数

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注