Linux 文本内容处理小记

这篇笔记用来整理目前用过或者见过的一些文本处理软件,意在提升命令行和脚本对文本处理效率,发现一些可能没关注的实用技巧

简单

先上点简单易用的软件,可以完成基本的文件处理、统计和排序 

wc 统计文本信息

print newline, word, and byte counts for each file  打印文件的行数,单词数,字符数

可以统计单个文件或多个文件的行数、单词数和字符数,一般用来统计行数用的比较多

wc /etc/passwd /etc/shadow
  46   93 2576 /etc/passwd
  46   46 1897 /etc/shadow
  92  139 4473 total

行数 单词数 字节数 文件名

参数 默认(lwc):
    -c, --bytes
    -m, --chars
    -l, --lines
    -w, --words

ls -l /etc | wc -l

注意:默认的第三个参数,是字节不是字符,写中文就能测试出来默认是-c 不是 -m,但是中文的字节计算不太对

中文在不同编码下占用的字节数不同,在GB2312编码下,一个中文字符占2个字节;在GBK编码下,一个中文字符占2个或4个字节;在UTF-8编码下,一个中文字符占3个或4个字节

echo 打印显示

echo - display a line of text 显示文本中的一行

这个是shell脚本中会经常看到的文本处理命令,也不需要什么复杂的用法,完全可不用特别参数,查看man 也没有太多的参数,不过支持12个转义字符,我取了几个看着有点用的出来

man echo
echo 参数
    -E     disable interpretation of backslash escapes (default) 关闭反斜杠转义支持
    -e     enable interpretation of backslash escapes 开启反斜杠转义支持
    -n     do not output the trailing newline  不打印末尾的换行

If -e is in effect, the following sequences are recognized:
    \\     backslash            打印\
    \n     new line             打印换行
    \r     carriage return      打印回车
    \t     horizontal tab       打印水平制表符
    \v     vertical tab         打印垂直制表符

 echo 参数后面的所有东西都会打印,可以拼接,可以支持取变量,但是注意单引号内原样输出

echo hello world         打印字符
ehco $PATH               打印变量
echo $(hostname)         打印执行结果
echo `hostname`          打印执行结果
echo PATH IS "$PATH"     "" 号内可以取出变量
echo '$PATH'             '' 号内原样输出
echo "hello" -n          打印结果是 hello -n ,参数记得放前面

watch 观察变化

watch - execute a program periodically, showing output fullscreen 定期执行程序,全屏显示输出

这个命令适合用来观察变化,比如文件清单、大小,内容相对固定的文件等等,变化内容可以高亮显示,Ctrl+c 退出

watch 参数:
    -d, --differences        高亮显示变化内容
    -n, --interval seconds   刷新间隔,默认2秒
    -t, --no-title           不显示title
    -e, --errexit            命令执行报错时停止更新,可以按键退出
    -g, --chgexit            命令输出发生变化时退出
    -x, --exec               默认将命令交给sh -c ,需要处理引号,通过exec 可以避免额外引号

man 里面给了几个案例,觉得有点意思

watch -d ls -l
watch -d 'ls -l | fgrep joe'
watch -n 5 -d cat /proc/meminfo

tee 输出重定向

tee - read from standard input and write to standard output and files 获取标准输入写入到标准输出和文件

工作中会发现,有时候需要把执行结果重定向到日志文件,但是也需要观察执行情况,开俩窗口?nohub 其实就可以指定输出到文件和启动时输出到窗口,不过 tee 可能是更好的选择。

tee 参数:
    -a, --append                 追加写入
    -i, --ignore-interrupts      忽略中断信号
    --output-error               如果写入报错,做什么操作
        'warn'            诊断任何写入报错
        'warn-nopipe'     诊断任何写入报错,除了管道输出
        'exit'            写入报错时退出
        'exit-nopipe'     写入报错时退出,除了管道输出

放个自己常用的方式,边看边记录

pidstat -d 5 5 |tee -a pidstat$(date "+%Y%m%d").log

diff 文件及内容对比

diff - compare files line by line 逐行对比文件内容

有时候想看看文件和备份文件有哪些改动,diff 就能排上用处,不过感觉不如git diff 显示明显

diff 参数:
    -q, --brief    在文件不同时才打印,打印内容是 Files file1 and file2 differ 
    -s, --report-identical-files    和 q 相反,这个是文件相同时打印

    -y, --side-by-side   打印两列对比,比较直观
    --left-column        配合-y 使用,只打印左边列以及变化的右边列

    -i, --ignore-case    忽略大小写
    -w, --ignore-all-space    忽略空格
    -B, --ignore-blank-lines  忽略空行    
diff /etc/rc0.d/ /etc/rc5.d/    目录下文件对比
diff file1 file2 -y             文件内容对比,显示两列

还行

这里记录一些稍微需要花一点点时间的命令,功能相对较复杂一些

cut 内容分割

cut - remove sections from each line of files   用指定的字符方式分割文本

有明显且简单的分隔规律时,cut就能满足文本处理需求

cut -d 字符 -f 第几段 filename

date "+%D %H:%M:%S"|cut -d ' ' -f 1
01/11/24

date "+%D %H:%M:%S"|cut -d ' ' -f 1-2
01/11/24 11:13:23
关于参数 -f 取分段
    N        取第N段,计数从1开始,超过最大值就是空
    N-       取第N到最后
    N-M      取N到M段
    -M       取第一段到M段

注意:

1)分隔仅支持一个字符,否则报错 cut: the delimiter must be a single character

2)如果用空格分割,空格不会被合并

3)支持文件和重定向的标准输出

iconv 文本编码转换

iconv - convert text from one character encoding to another 文本编码转换

这个命令的参数很少,帮助文档的案例可以直接拿来用

iconv 参数:
    -l, --list        列出所有已知的字符集编码
    -o outputfile, --output=outputfile   指定输出到文件

    -c         遇到无法解析的字符,直接丢弃而不是终止,也会在 -t 的处理选项生效

    -f from-encoding, --from-code=from-encoding

    -t to-encoding, --to-code=to-encoding
        -t 的编码支持指定遇到无法编码的字符的处理
            //IGNORE        遇到无法编码的字符,丢弃,转换后打印报错
            //TRANSLIT      无法编码会用近似的字符,没有近似字符用?替换

文档案例测试以及发散: 

echo abc ß α € àḃç | iconv -f UTF-8 -t ASCII//TRANSLIT
abc ss ? EUR abc

echo abc ß α € àḃç | iconv -f UTF-8 -t ASCII//IGNORE
abc    
iconv: illegal input sequence at position 22

echo abc ß α € àḃç | iconv -f UTF-8 -t ASCII
abc iconv: illegal input sequence at position 4

echo abc ß α € àḃç | iconv -f UTF-8 -t ASCII -c
abc
$ file input.txt
input.txt: ISO-8859 text, with CRLF line terminators
$ iconv -f ISO-8859-1 -t ASCII//IGNORE < input.txt -o output.txt
iconv: illegal input sequence at position 112
$ file *.txt
input.txt:  ISO-8859 text, with CRLF line terminators
output.txt: ASCII text, with CRLF line terminators

 ISO-8859 转 GBK 还是 ISO-8859,UTF-8 转其他不行,因为少了libc.mo 未解决,所以还是得了解一下字符集编码才行呐  man charsets 查看字符集编码的介绍

iconv 报错文件不存在排查:

$ file export.log 
export.log: UTF-8 Unicode text
$ iconv -f UTF-8 -t ISO-8859 < export.log
iconv: failed to start conversion processing: No such file or directory
$ strace iconv -f UTF-8 -t ISO-8859 < export.log
...
openat(AT_FDCWD, "/usr/share/locale/en_US.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en_US.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en_US/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
write(2, "iconv: ", 7iconv: )                  = 7
write(2, "failed to start conversion proce"..., 37failed to start conversion processing) = 37
write(2, ": No such file or directory", 27: No such file or directory) = 27
write(2, "\n", 1
)                       = 1
exit_group(1)                           = ?
+++ exited with 1 +++
$ locale -a |grep en_US.utf8
en_US.utf8

从strace 追踪结果看得出来,这个文件不存在不是输入文件不存在,是编码的文件不存在,报错的文件确实不存在,但是locale 能查询到UTF-8的支持,yum 里面也找不到 libc.mo 的提供软件。没解决,说是要重装glibc ,算了。。。

libc.mo 是一个包含编译好的本地化字符串的文件,它是与 C 库(libc)一起使用的。本地化字符串是特定于语言环境的文本,例如消息、错误消息和用户界面元素的标签。

tr 字符转换与删除

tr - translate or delete characters 转化或删除字符

看帮助文档,tr 支持用集合来处理字符,用处还是不少滴,还有更复杂的用法,建议看看man 

tr 参数:
    -d, --delete            删除集合1的字符,不转化
    -s, --squeeze-repeats   字符替换 貌似是默认值

例一 把文本小写转大写
$ w | tr -s '[a-z]' '[A-Z]'

例二 打印结果去掉斜杠
$ w | tr -d '/'

例三 去转义字符
$ echo -e 'a \t b'|tr -d '\t'

sort 排序

 sort - sort lines of text files  对文件的文本按行排序

这个一般搭配nuiq 计数使用,可以在排序后分类和计数

sort 参数
    -b, --ignore-leading-blanks     忽略前面空格
    -d, --dictionary-order          只考虑空格和字母数字字符
    -f, --ignore-case               忽略大小写
    -g, --general-numeric-sort      根据一般数值比较
    -h, --human-numeric-sort        支持人类可读的比较 (例如2K 1G)
    -n, --numeric-sort              依据字符串数值比较
    -r, --reverse                   取反
    -M, --month-sort                月份比较
sort | uniq -c | sort -rn

nuiq 计数 

uniq - report or omit repeated lines 报告或省略重复的行

一般搭配sort ,实现分类和统计

uniq 参数:
    -c, --count            按出现次数为行添加前缀
    -d, --repeated         只打印有重复的行,每组一行
    -D                     打印所有重复行
    -u, --unique           打印无重复的行
    -f, --skip-fields=N    跳过前N个字段比较
    -s, --skip-chars=N     跳过前N个字符比较
    -i, --ignore-case      忽略大小写
    -w, --check-chars=N    每行中用于比较字符不超过N个
sort | uniq -c | sort -rn

有点意思

这部分就记录三剑客,grep  awk sed ,平时工作能用的程度还是不难滴,由于过于强大,这里就简单记录一些平时会用到的,后续发现有意思的方式也会持续补充

grep 文本匹配

grep, egrep, fgrep - print lines matching a pattern 打印匹配模式的行

egrep 就是 grep -E,主要是支持正则表达式的意思

fgrep 就是 grep -F ,主要是用通过字符串列表匹配,由换行符分隔,任何换行符都要匹配

grep 平时主要用来过滤文本,能直接用在文件上面,也能通过管道来过滤,日常工作中使用频率很高;支持正则表达式,让 grep 在文本匹配和过滤处理上自由度极高。正则表达式,grep命令的正则有些不同,随后有记录。

grep 的参数很多,能处理文本,文件/目录,能过滤二进制文件,还能读取块设备和套接字,这里只记录一些可能会用到的,更详细的建议看看man grep

grep 参数:

    匹配选项:
        -E, --extended-regexp      正则匹配,扩展规则匹配
        -F, --fixed-strings        字符串(文本段)匹配
        -G, --basic-regexp         基础规则匹配 默认

    匹配控制:
        -i, --ignore-case                   忽略大小写
        -v, --invert-match                  反选
        -e PATTERN, --regexp=PATTERN        使用正则表达式
        -w, --word-regexp                   单词匹配,完全匹配
        -x, --line-regexp                   整行匹配,完全匹配

    输出控制:
        -c, --count                匹配的数量
        -m NUM, --max-count=NUM    读取到文本的第NUM行就停止匹配
        -o, --only-matching        只打印匹配到的内容,而不是过滤到的行
        
        -n, --line-number              打印行号

        -C NUM, -NUM, --context=NUM    打印匹配到的行的前后NUM行
        -A NUM, --after-context=NUM    打印匹配到的行的后NUM行
        -B NUM, --before-context=NUM   打印匹配到的行的前NUM行

    文件和目录:
        -r, --recursive     可以递归的读取目录下的所有文件,没指定目录就是工作目录

        -R, --dereference-recursive  在-r 基础上,还将搜索符号链接指向的目录中的文件
grep -v '#'|grep -v '^$'         过滤注释和空白行
grep -i 'error' messages         查找错误日志,忽略大小写
grep -ri '192.168' /etc/         递归读取目录文件内容,并匹配
grep 'hello' *.log               从多个文件中匹配合适的内容
grep "$HOSTNAME" /etc/hosts      从文件中获取匹配用户名的行

获取 Java 进程,并取出最大堆内存大小,并累加(注意这里以 M 为单位)
ps -ef|grep java|grep "Xmx[0-9]*M" -o|grep -o "[0-9]*" | awk '{s+=$1} END {print s}'

注意:'' 原样输出,"" 可以取值

注意

1)*.log 是以.log 结尾的内容,.*log 是以log结尾的内容

2)使用管道时,要注意grep 的参数,管道传过来的参数会被认为是文本,也可能是文件

echo 'spring.log'|strace grep sp*.log
有springboot.log:execve("/bin/grep", ["grep", "springboot.log"], [/* 28 vars */]) = 0
没有springboot.log:execve("/bin/grep", ["grep", "sp*.log"], [/* 28 vars */]) = 0

sed 文本替换

sed - stream editor for filtering and transforming text  用于过滤和转换文本的流编辑器

sed 工作中比较常用来替换文本,可以处理管道的结果也能用来直接替换文件内容,非常强大

Hold space 

Hold space是 sed 命令的一个临时缓冲区,用于保存模式匹配行的副本

Hold space:
    h 命令:当前模式匹配行将被复制到 hold space
    H 命令:当前模式匹配行将被追加到 hold space
    g 命令:hold space 中的文本将替换模式匹配行
    G 命令:hold space 中的文本将追加到模式匹配行

Pattern space

Pattern space 是 sed 命令的主缓冲区,用于存储当前正在处理的文本行

Pattern space:
    p 命令:pattern space 中的文本将被打印到标准输出
    d 命令:pattern space 中的文本将被删除
    s 命令:pattern space 中的文本将被替换
    a 命令:文本将追加到 pattern space 的末尾
    i 命令:文本将插入到 pattern space 的开头

平时常用的不用太复杂,如下几种常用的替换已经写好了,自取。

文本替换打印:
    sed 's/"hello" /"hello world"/g' 将hello 替换成hello world
    sed 's/\:/ /g'   将':'替换成空格,冒号需要转义

文件内容替换操作:
    sed -i s/"PermitRootLogin yes"/"PermitRootLogin no"/g /etc/ssh/sshd_config

文本内容截取:
    nl /etc/passwd | sed '2,5d'    打印结果,去除2-5行
    nl /etc/passwd | sed '3,$d'    打印结果,去除3到最后行

nl 等价于 cat -n 显示行数

注意

1)sed 后面如果要接超过两个以上的动作时,每个动作前面得加 -e 才行

2)替换匹配规则尽量的需要写完整,替换前可以打印出来看看,避免意料之外的替换

3)直接操作文件,记得先备份

awk 文本分割

gawk - pattern scanning and processing language  模式扫描与处理语言

awk 还是不一样啊,其他的就是应用,它的描述是语言,目前工作中常用来分割文本,比cut 的分割更多样,目前awk 帮助文档已经直接显示gawk 了,gawk 更强大,功能更完善和更快

功能太过复杂,这里就简单的提供几个平时会用到的案例:

last -n 5 | awk '{print $1 "\t" $3}'     默认空格或tab分段,该命令取第1和3段
awk -F"--server.port=" '{print $2}'      -F 后面紧跟分段条件,然后打印第2段

变量:
    NF 每一行 ($0) 拥有的字段总数
    NR 目前 awk 所处理的是第几行数据
    FS 目前的分隔字符,默认是空格键

注意:
    awk 内的 NR, NF 等变量要用大写,且不需要有 $ 字符

文本正则表达式

Linux 命令行对于正则表达式的支持以及文本处理对于正则表达式的支持的区别:

Linux 命令行支持基本正则表达式 (BRE)、扩展正则表达式 (ERE) 和 Perl 兼容正则表达式 (PCRE)。文本处理仅支持基本正则表达式 (BRE)。

Linux 命令行中的正则表达式通常比文本处理中的正则表达式更快。这是因为 Linux 命令行使用更优化的正则表达式引擎。

Linux 命令行中的正则表达式比文本处理中的正则表达式更广泛可用。这是因为 Linux 命令行是一个更普遍的工具,而文本处理是一个更专业的工具。

一些常见的文本正则表达式:
    ^word   以word开头的子串
    word$   以word结尾的子串
    .       任意字符
    \       转义字符
    *       重复前面的字符0到无穷次,不能单独出现,搭配其他的字符使用,.* 表示任意字符
    [list]  匹配括号中的任意一个字符
    [n1-n2] 匹配括号中的任意一个字符,n1-n2是连续的意思,排序方式与系统编码有关
    [^list] 不能匹配括号中的任意一个字符
    \{n,m\} 连续 n 到 m 个的『前一个 RE 字符』
    \{n\}   连续 n 个的前一个 RE 字符
    \{n,\}  连续 n 个以上的前一个 RE 字符
  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Protobuf是一种高效的序列化协议,可以用于数据交换和数据存储。它的主要优势是大小小,速度快,可扩展性强。下面是使用Protobuf的一些小记: 1. 定义消息格式 首先,需要定义消息格式,以便Protobuf可以将数据序列化和反序列化。消息格式定义在.proto文件中,使用protobuf语言编写。例如,下面是一个简单的消息格式定义: ``` syntax = "proto3"; message Person { string name = 1; int32 age = 2; } ``` 这个消息格式定义了一个名为Person的消息,包含两个字段:name和age。 2. 生成代码 一旦消息格式定义好,就可以使用Protobuf编译器生成代码。编译器将根据消息格式定义生成相应的代码,包括消息类、序列化和反序列化方法等。可以使用以下命令生成代码: ``` protoc --java_out=. message.proto ``` 这将生成一个名为message.pb.java的Java类,该类包含Person消息的定义以及相关方法。 3. 序列化和反序列化 一旦生成了代码,就可以使用Protobuf序列化和反序列化数据。例如,下面是一个示例代码,将一个Person对象序列化为字节数组,并将其反序列化为另一个Person对象: ``` Person person = Person.newBuilder() .setName("Alice") .setAge(25) .build(); byte[] bytes = person.toByteArray(); Person deserializedPerson = Person.parseFrom(bytes); ``` 这个示例代码创建了一个Person对象,将其序列化为字节数组,然后将其反序列化为另一个Person对象。在这个过程中,Protobuf使用生成的代码执行序列化和反序列化操作。 以上是使用Protobuf的一些基本步骤和注意事项,希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值