使用|使用 Shell 进行数据整理(学习笔记)

写在前面:本篇内容来自于 MIT 推出的课程:计算机教育中缺失的一课,这门课程介绍了命令行、强大的文本编辑器的使用、使用版本控制系统提供的多种特性等等。中文课程主页:https://missing-semester-cn.github.io/
本篇为课程的第4节,主题为数据整理。
大多数情况下,数据整理需要您能够明确哪些工具可以被用来达成特定数据整理的目的,并且明白如何组合使用这些工具。

  • 获取服务器日志: ssh myserver journalct > journal
  • sed:使用正则表达式进行替换,用法, sed 's/.*Disconnected from //', 将这一部分替换为空。 s/REGEX/SUBSTITUTION/
    • 捕获组,如果想要保留表达式中的一部分匹配信息,可以在SUBSTITUTION中使用 \1、\2 来表示其中的捕获到的信息
  • 正则表达式的一些用法
    • . 除空格之外的”任意单个字符”(这里对吗,没有空格吗?)
    • * 匹配前面字符零次或多次
    • + 匹配前面字符一次或多次
    • [abc] 匹配 a, bc 中的任意一个
    • (RX1|RX2) 任何能够匹配RX1RX2的结果
    • ^ 行首
    • $ 行尾
  • 正则表达式默认是贪婪匹配的,可以在 *+ 后添加 ? 来变为非贪婪的模式
  • 测试正则表达是否正确:debug
  • 匹配任意一个单词( [^ ]+ 会匹配任意非空且不包含空格的序列)
    • 其中 ^ 如果放在表达式最开始,代表从行首开始匹配;否则含义为“不包含”
  • 对输入数据排序: sort
    • sort -n 会按照数字顺序对输入进行排序(默认情况下是按照字典序排序 -k1,1 则表示“仅基于以空格分割的第一列进行排序”。 ,n 部分表示“仅排序到第 n 个部分”,默认情况是到行尾。
    • sort -r 可以倒序
  • 把连续出现的行折叠为一行并使用出现次数作为前缀: uniq -c
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, # 合并行,使用,分隔

  • awk:是一种编程语言,非常善于处理文本
    • $0 表示整行的内容, $1$n 为一行中的 n 个区域,区域的分割基于 awk 的域分隔符(默认是空格,可以通过 -F来修改)。
    • 所有以c 开头,以 e 结尾,并且仅尝试过一次登陆的用户。 | awk '$1 == 1 && $2 ~ /^c[^ ]*e$/ { print $2 }' | wc -l
      • 该匹配要求文本的第一部分需要等于1(这部分刚好是uniq -c得到的计数值)
      • 第二部分必须满足给定的一个正则表达式
BEGIN { rows = 0 } $1 == 1 && $2 ~ /^c[^ ]*e$/ { rows += $1 } END { print rows }

  • 命令行计算器, bc
    • bc -l 使用计算库,包括正弦余弦
课后练习
  1. 学习一下这篇简短的 交互式正则表达式教程.
    - \d 匹配数字,\D 匹配非数字的字符 - . 可以匹配任意字符,\. 匹配句号 - 匹配特定的字符,[abc],可以匹配 a或b或c - 去除特定字符,[^abc],可以匹配除了 a或b或c 的任意字符 - 表示字符,\w 效果 = [A-Za-z0-9_],\W ,非这些字符 - 捕获出现一定次数的字符 - a{3},出现3次 - a{1,3},出现不少于1次,不多于3次 - * 匹配出现任意次,+ 匹配出现至少1次 - ? 匹配是可选的,可以不出现或出现1次 - 各种空格的处理:\s 可以匹配包括 space空格,tab(\t),newline (\n),和回车(\r),非常有用。\S 匹配非空格的字符 - ^ 匹配句首 - $ 匹配句尾 - 捕获组 - 正则表达式可以用于提取信息,用于进一步的处理 - 使用 () 括起来,可以对得到的结果分组 - 嵌套组:多个括号嵌套起来 - 条件表达式,结合 () 和 | - 引用(back reference) - \0 完整匹配 到的文本 - \1 第一个组 - \2 第二个组

  2. 统计words文件 (/usr/share/dict/words) 中包含至少三个a 且不以's 结尾的单词个数。这些单词中,出现频率前三的末尾两个字母是什么? sedy命令,或者 tr 程序也许可以帮你解决大小写的问题。共存在多少种词尾两字母组合?还有一个很有挑战性的问题:哪个组合从未出现过?(这里使用捕获组是个更好的解法: .*(..),其中 \1 就可以捕获到最后两个字母)
    # 统计词数 cat /usr/share/dict/words | rg "\w*a\w*a\w*a\w*[^'s]" | tr "[:upper:]" "[:lower:]" | uniq -c | wc -l # 5345#!/bin/bash words="./words.txt" cat /usr/share/dict/words | rg "\w*a\w*a\w*a\w*[^'s]" | tr "[:upper:]" "[:lower:]" >words.txtrun() { for i in {a..z}; do for j in {a..z}; do echo -n "$i$j " rg ".*$i$j$" $words | wc -l | awk '{ print $1 }' done done }run >occurance.txtecho "the most frenquent 3 combinations" cat occurance.txt | sort -nk2,2 -r | head -n3echo -n "there are total " cat occurance.txt | awk ' BEGIN { num = 0 } $2 ~ "0" { num += 1 } END { printf num } ' echo " combinations"echo "never appeared combinations" cat occurance.txt | awk ' {if ($2 == "0") print $1} ' >nevershowed.txt paste -s -d , nevershowed.txt

  3. 进行原地替换听上去很有诱惑力,例如: sed s/REGEX/SUBSTITUTION/ input.txt > input.txt。但是这并不是一个明智的做法,为什么呢?还是说只有 sed是这样的? 查看 man sed 来完成这个问题
  4. 找出您最近十次开机的开机时间平均数、中位数和最长时间。在Linux上需要用到 journalctl ,而在 macOS 上使用 log show。找到每次起到开始和结束时的时间戳。在Linux上类似这样操作:
    【使用|使用 Shell 进行数据整理(学习笔记)】Logs begin at ...

    systemd[577]: Startup finished in ...
    在 macOS 上, 查找:
    === system boot:

    Previous shutdown cause: 5
  5. 查看之前三次重启启动信息中不同的部分(参见 journalctlb 选项)。将这一任务分为几个步骤,首先获取之前三次启动的启动日志,也许获取启动日志的命令就有合适的选项可以帮助您提取前三次启动的日志,亦或者您可以使用sed '0,/STRING/d' 来删除STRING匹配到的字符串前面的全部内容。然后,过滤掉每次都不相同的部分,例如时间戳。下一步,重复记录输入行并对其计数(可以使用uniq )。最后,删除所有出现过3次的内容(因为这些内容上三次启动日志中的重复部分)。
  6. 在网上找一个类似 这个 或者这个的数据集。或者从这里找一些。使用 curl 获取数据集并提取其中两列数据,如果您想要获取的是HTML数据,那么[pup](https://github.com/EricChiang/pup)可能会更有帮助。对于JSON类型的数据,可以试试[jq](https://stedolan.github.io/jq/)。请使用一条指令来找出其中一列的最大值和最小值,用另外一条指令计算两列之间差的总和。

    推荐阅读