【Linux】数据处理命令

内容检索

内容查看

less 创建一个文件分页器,使我们可以通过翻页的方式浏览较长的文本

1
2
less filename
grep 'pattern' filename | less

vim 可对查看的数据格式进行修剪

1
2
3
4
5
6
7
8
# 临时查看输出文件
strace -f gcc hello.c |& vim -
:set nowrap
:%!grep -e execve  # 过滤
:%s/, /  \r/g      # 方便查看调用参数

# 退出
:!q

搜索

常用 grep (Global regular expression print) 命令搜索内容。

注意:mac 原生 grep 与 Linux 不一致,可以安装 ggrep (GNU)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 查找目录下所有文件内容
grep -r DURATION *

# 与 或 非 -E 使用扩展正则表达式
grep -E 'pattern1.*pattern2' filename
grep -E 'pattern1|pattern2' filename
grep -v 'pattern' filename

grep --ignore-case # 忽略大小写
grep -C 5 # 上下文 前后5行
grep --context|before-context|after-context=3 "search_pattern" filename

# 去除空行 '^$' 表示空行
cat filename | grep -v '^$'

# 逐行显示(grep 默认 buffer 全部结果)
tail -f file | grep --line-buffered 'pattern'

例 0:如果文件 test.log 有100w 行,查找含有“error”的行并显示对应的行号

1
grep -n "error" test.log

例 1:使用 grep 查找日志中的 IPv4 address

1
2
3
4
5
6
7
# 不完全与 ip 地址对应(0~255),可以初步过滤
# -o 只显示匹配的内容,不显示完整的一行
dig www.baidu.com | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}"

# 日志规整,直接使用分割符分割,或利用特殊边界
awk '{print $2}'
grep -oE "[IP].*[" | ...

例 2LeetCode-193. 有效电话号码

1
2
3
4
5
6
7
# 有效的电话号码必须满足以下两种格式
# (xxx) xxx-xxxx
# xxx-xxx-xxxx

# -P 使用 Perl 拓展正则,支持 \d = [0-9]
# grep -P '^(\(\d{3}\) |\d{3}-)\d{3}-\d{4}$' file.txt
grep -E '^(\([0-9]{3}\) |[0-9]{3}-)[0-9]{3}-[0-9]{4}$' file.txt

新命令 rg (ripgrep) 命令速度快,用法非常符合直觉,可以替代 grep

1
2
3
4
5
6
7
8
# 查找所有使用了 requests 库的文件
rg -t py 'import requests'
# 查找所有没有写 shebang 的文件(包含隐藏文件)
rg -u --files-without-match "^#!"
# 查找所有的foo字符串,并打印其之后的5行
rg foo -A 5
# 打印匹配的统计信息(匹配的行和文件的数量)
rg --stats PATTERN

比较差异

diff 命令

1
2
3
4
5
6
7
8
# 查看两个文件的不同
diff a b -y
# 比较文件夹 foo 和 bar 中包含文件的不同
diff <(ls foo) <(ls bar) # <( CMD ) 会执行 CMD 并将结果输出到一个临时文件中
# 将两个文件中相同的内容设置为空字符串,剩下的内容就是差异
diff --unchanged-group-format='' <(cat occurance.txt) <(cat all.txt) | wc -l
# 只显示不同行
diff a b -y | grep -E '<|>'

grep 命令

1
2
3
4
5
6
7
8
9
# -f --file <规则文件> : 指定规则文件,其内容含有一个或多个规则样式,让grep查找符合规则条件的文件内容,格式为每行一个规则样式。
# -F --fixed-regexp : 将样式视为固定字符串的列表
# -w --word-regexp : 只显示全字符合的列
# -v --invert-match : 显示不包含匹配文本的所有行
grep -f a.txt b.txt
grep -wf a.txt b.txt   # 取出相同的行
grep -wvf a.txt b.txt  # 取出不相同的行
# 如果要计算取出的行一共多少行
grep -wf a.txt b.txt | wc -l  # 相同的行一共有多少行

如果 a.txt 有重复行数据,使用下面命令过滤产生新文件 a1. txt,再用上述方法比较

1
2
3
4
5
6
7
# 排序 -> 计数 -> 按重复数量倒序 -> 截取 -> 导出
sort a.txt | uniq -c | sort -rn | cut -c 9- > a1.txt

# 注意:当重复的行并不相邻时,uniq 命令不起作用
sort -u a.txt > c.txt       # 去重 => sort a.txt | uniq > c.txt
sort -u b.txt > d.txt       #     => sort b.txt | uniq > d.txt
sort c.txt d.txt | uniq -d  # 仅显示重复出现的行列

comm 命令用于一列列的比较两个已排序文件的差异

1
comm c.txt d.txt

数据处理

awk

mac 原生是 nawk,与 Linux 不一致,可以统一安装 gawk (GNU)

awk 是用于文本处理的强大工具,对于指定模式串(可选) 完成对应代码块的操作,更多细节参考 AWK程序设计语言

例 0:表格取出特定的列

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# -F '[***]' 多个分隔符 同时作用,没有优先顺序
echo "123#456-78#9" | awk -F '[-#]' '{print $1"-" $2 "-" $3 "-"  $4}'
123-456-78-9

# 中文字符\t[0.123, 0.456] -> 中文字符 0.456
#
# awk 多个分隔符(注意转义字符\\)
echo '中文字符        [0.123, 0.456]' | awk -F '["\t","\\]"]' '{print $1 $3}'
# awk + gsub
echo '中文字符        [0.123, 0.456]' | awk  '{gsub(/\[|\]|,/,"",$0); gsub(/\t/," ",$0); split($0,a," "); print a[1]" "a[3]}'
# awk + match
echo '中文字符        [0.123, 0.456]' | awk -F "\t" '{match($2, /\[([^,]+), (.+)\]/, a); print $1, a[1], a[2]}'

例 1:自己最常用的十条命令

1
history | awk '{$1="";print substr($0,2)}' | sort | uniq -c | sort -n | tail -n 10

例 2:所有以 c 开头,以 e 结尾,并且仅尝试过一次登录的用户

1
2
3
4
5
6
7
8
9
# 5 jack
# 1 tom
# 3 jerry
awk '$1 == 1 && $2 ~ /^c[^ ]*e$/ { print $2 }' filename

# 打印满足要求的行号
awk 'BEGIN { rows = 0 }
		$1 == 1 && $2 ~ /^c[^ ]*e$/ { rows += $1 }
	 END { print rows }' filename

例 3LeetCode 194.转置文件 | Code

NF: 当前行的字段数量; NR: 当前处理的行号; FS: 字段分隔符,默认为空格

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
awk '
{
    for (i = 1; i <= NF; i++) {
        s[i] = s[i] ? s[i] FS $i : $i
    }

} END {
    for (i in s) {
        print s[i]
    }
}' file.txt

例 4LeetCode 192. 统计词频 | Code

1
2
3
4
5
6
# tr 用于转换或删除文件中的字符
# -s:缩减连续重复的字符成指定的单个字符
cat words.txt | tr -s ' ' '\n' | sort | uniq -c | sort -r | awk '{print $2, $1}'

# sed 替换空格为换行 g表示替换所有匹配项
cat words.txt | xargs | sed 's/\s/\n/g' | sort | uniq -c | sort -k1nr,2r | awk '{print $2, $1}'

例 5LeetCode 195. 打印第十行 | Code

1
2
3
4
5
6
# head -10 file.txt | tail -1 # 不足10行会返回最后一行(不合题意)
tail +10 file.txt | head -1

sed -n "10p" file.txt

awk 'NR==10 {print $0}' file.txt

sed

mac 原生 sed 与 Linux 不一致,可以统一安装 gsed (GNU)

sed 是一个基于文本编辑器 ed 构建的流编辑器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# s/REGEX/SUBSTITUTION/

# -i 原地修改
current_time=$(date +"%Y-%m-%dT%H:%M:%S+08:00")
sed -i "1,/^lastmod:.*$/s/^lastmod:.*$/lastmod: $current_time/" filename

ssh myserver journalctl
 | grep sshd
 | grep "Disconnected from"
 | sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/'
 | sort | uniq -c
 | sort -nk1,1 | tail -n10
 | awk '{print $2}' | paste -sd,

# paste 命令将多个文件的内容按列合并 -s 合并行 -d 指定分隔符
paste -s file1.txt file2.txt

数据分析函数

1
2
3
4
5
6
7
8
# 求和
| paste -sd+ | bc -l

# 统计
| R --slave -e 'x <- scan(file="stdin", quiet=TRUE); summary(x)'

# 图表
| gnuplot -p -e 'set boxwidth 0.5; plot "-" using 1:xtic(2) with boxes'

Reference

0%