計算機教育中缺失的一課 - MIT - L4 - 數據整理

https://missing.csail.mit.edu/
https://missing-semester-cn.g...
https://www.bilibili.com/vide...

筆記

REGEX

  1. 入門交互式教程
  2. 進階文字教程

regex debuggergit

A taste of data wrangling

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

sort -n 會按照數字順序對輸入進行排序(默認狀況下是按照字典序排序 -k1,1 則表示「僅基於以空格分割的第一列進行排序」。,n 部分表示「僅排序到第n個部分」,默認狀況是到行尾。就本例來講,針對整個行進行排序也沒有任何問題,咱們這裏主要是爲了學習這一用法!github

若是咱們但願獲得登錄次數最少的用戶,咱們可使用 head 來代替tail。或者使用sort -r來進行倒序排序。編程

咱們能夠利用 paste命令來合併行(-s),並指定一個分隔符進行分割 (-d)。bash

AWK

awk 實際上是一種編程語言,只不過它碰巧很是善於處理文本。服務器

awk 程序接受一個模式串(可選),以及一個代碼塊,指定當模式匹配時應該作何種操做。默認當模式串即匹配全部行(上面命令中當用法)。 在代碼塊中,$0 表示整行的內容,$1$n 爲一行中的 n 個區域,區域的分割基於 awk 的域分隔符(默認是空格,能夠經過-F來修改)。在這個例子中,咱們的代碼意思是:對於每一行文本,打印其第二個部分,也就是用戶名。ssh

再舉個例子,讓咱們統計一下全部以c 開頭,以 e 結尾,而且僅嘗試過一次登錄的用戶。編程語言

| awk '$1 == 1 && $2 ~ /^c[^ ]*e$/ { print $2 }' | wc -l

其中 wc -l 統計輸出結果的行數。ide

既然 awk 是一種編程語言,那麼則能夠這樣:學習

BEGIN { rows = 0 }
$1 == 1 && $2 ~ /^c[^ ]*e$/ { rows += $1 }
END { print rows }

BEGIN 也是一種模式,它會匹配輸入的開頭( END 則匹配結尾)。而後,對每一行第一個部分進行累加,最後將結果輸出。spa

bc

bc (Berkeley Calculator) 是一個命令行計算器。例如這樣,能夠將每行的數字加起來:

| paste -sd+ | bc -l

下面這種更加複雜的表達式也能夠:

echo "2*($(data | paste -sd+))" | bc -l

Shell 命令中的 -

雖然到目前爲止咱們的討論都是基於文本數據,但對於二進制文件其實一樣有用。例如咱們能夠用 ffmpeg 從相機中捕獲一張圖片,將其轉換成灰度圖後經過SSH將壓縮後的文件發送到遠端服務器,並在那裏解壓、存檔並顯示。

ffmpeg -loglevel panic -i /dev/video0 -frames 1 -f image2 -
 | convert - -colorspace gray -
 | gzip
 | ssh mymachine 'gzip -d | tee copy.jpg | env DISPLAY=:0 feh -'

其中 -frames 1 爲第一幀畫面,-f image2 將結果保存爲圖片而不是視頻格式。

命令中 - 表明標準輸入輸出流,例如 convert - -colorspace gray - 的意思是把標準輸入流的內容做爲程序的輸入,灰度處理後的結果再放到標準輸出流中。

課後練習

習題 2

words 文件能夠在這裏下載:/usr/share/dict/words

$ grep -E "^.*[aA].*[aA].*[aA].*$" /usr/share/dict/words \
| grep -vE "'s$" \
| sed -E "s/^.*(\w{2})$/\1/" \
| sort \
| uniq -ic \
| sort -r \
| head -n3

    101 an
     63 ns
     51 ia

共存在多少種詞尾兩字母組合?顯然

$ echo "26*26" | bc -l
676

咱們把剛纔的詞尾保存下來,把全部的字母組合也保存爲文件。

$ grep -E "^.*[aA].*[aA].*[aA].*$" /usr/share/dict/words \
| grep -vE "'s$" \
| sed -E "s/^.*(\w{2})$/\1/" \
| sort \
| uniq -i > words.txt 2> words.txt

$ cat words.txt | head -n5
aa
ac
ad
ae
ag

$ echo {a..z}{a..z} | sed -E 's/ /\n/g' > full_words.txt

$ cat full_words.txt | head -n5
aa
ab
ac
ad
ae

分別統計統計一下組合數:

$ wc -w full_words.txt
676 full_words.txt
$ wc -w words.txt
110 words.txt

而後咱們找沒有出現過的組合,具體作法是把 words.txt 中的每一行做爲查找串,在 full_words.txt 中不匹配的行。

$ grep -F -v -f words.txt full_words.txt | head -n 5
ab
af
ai
aj
ao

結果應該共有 676 - 110 = 566 個,驗證一下:

$ grep -F -v -f words.txt full_words.txt | wc -w
566

習題 3

用輸出重定向進行原地替換隻會獲得空文件。man sed 中能夠看到 sed 有 -i 選項,能夠進行原地替換。

-i[SUFFIX], --in-place[=SUFFIX]

              edit files in place (makes backup if SUFFIX supplied)
相關文章
相關標籤/搜索