这篇笔记用来整理目前用过或者见过的一些文本处理软件,意在提升命令行和脚本对文本处理效率,发现一些可能没关注的实用技巧
先上点简单易用的软件,可以完成基本的文件处理、统计和排序
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 - 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 - 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 - 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
这里记录一些稍微需要花一点点时间的命令,功能相对较复杂一些
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 - 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 - 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 lines of text files 对文件的文本按行排序
这个一般搭配nuiq 计数使用,可以在排序后分类和计数
uniq - report or omit repeated lines 报告或省略重复的行
一般搭配sort ,实现分类和统计
这部分就记录三剑客,grep awk sed ,平时工作能用的程度还是不难滴,由于过于强大,这里就简单记录一些平时会用到的,后续发现有意思的方式也会持续补充
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 从文件中获取匹配用户名的行
注意:'' 原样输出,"" 可以取值
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
未完待续