假設你已經 用 mbsync 之類的把郵件下載到自己的 linux 機器, 以 maildir 的形式儲存。 今天要介紹 debian 的 notmuch 套件, 它可以跟 neomutt 等等 MUA 搭配使用; 不過今天先只介紹在命令列上獨立使用 notmuch。
一、 簡介
剛安裝好, 先執行 notmuch setup
回答完問題之後, 會產生一個設定檔 ~/.notmuch-config 。
(因為我已設定好,又重跑一次 setup, 所以右圖顯示 notmuch 提供一些預設值。)
每次執行過 mbsync 下載新郵件, 就可以用 notmuch new
叫它掃描新郵件、 更新資料庫。 它不會去動你的郵件,
而是會在 ~/Maildir/ (或是當初 setup 所指定的目錄底下)
底下建立自己的資料庫 ~/Maildir/.notmuch/ 。
它採用 xapian 作為內文搜尋引擎; xapian 預設並不支援中文。
還好找到 gugod 大大的文章 :
要先執行 export XAPIAN_CJK_NGRAM=1
然後才執行 notmuch new
。
如果已有舊的 (不支援中文的) 資料庫,
那就先用 notmuch dump ...
把自己建立的 tags 備份起來 (如果有的話)、
刪掉舊的資料庫 rm -rf ~/Maildir/.notmuch/
、
重新 notmuch new
、
最後再 notmuch restore ... 還原自己的 tags (如果有的話)。
以下是一些搜尋範例:
notmuch search '助教' # 搜尋提到 「助教」 的所有 threads notmuch search from:ckhung@cyut.edu.tw # 搜尋 「從 ckhung@cyut.edu.tw 寄出」 的所有 threads notmuch search date:12/1/24..12/31/24 # 搜尋 2024 年 12 月份收發的所有 threads notmuch search date:12/1/24..12/31/24 from:nchu.edu.tw # 搜尋 2024 年 12 月份來自 nchu 的所有 threads notmuch search 'from:nchu.edu.tw and not iLearning' # 來自 nchu 但不含 'iLearning' 的所有 threads notmuch count '*' # 總共有多少封信件 (而不是多少 threads!)
注意: notmuch search 的結果是每個 thread 一列
(每一組來來回回的對話), 如果要把每一封信獨立出來放在一列,
就要用 --output=messages 。
所以 notmuch search --output=messages '*' | wc -l
的輸出就會跟 notmuch count '*'
一樣。
其他心得:
- 搜尋中文時, 若使用三個中文字 (或更多) 的詞, 會失敗。
- 日期的三種寫法: 12/31/24 等於 12/31/2024 等於 2024-12-31
- 可以使用 and (可省略)、 or、 not、 小括弧組成複雜邏輯條件, 並用引號括起來,
例如:
notmuch search 'from:cyut.edu.tw not (from:ckhung or 隔離)'
- 完整的搜尋條件語法請見:
man notmuch-search-terms
notmuch search ...
的輸出資訊可以是郵件檔案的路徑 (--output=files)、 輸出格式可以是 json (--format=json) 等等, 詳見man notmuch-search
。notmuch show ...
可以顯示郵件完整內容, 詳見man notmuch-show
。
二、 應用實例: 弄懂 threads vs messages vs files
在我的機器上的某個時間點, 以下指令所產生的數字分別是:
notmuch search '*' | wc -l # => 499 threads notmuch count '*' # => 542 messages notmuch count --output=files '*' # => 593 files
一個 thread 可能包含好幾封 messages, 前面解釋過了;
而每封郵件可能會在不同的目錄裡重複存放, 所以檔案數又多於郵件數。
notmuch search ...
的輸出當中,
在日期與寄件人之間, 有一組數字, 例如以下:
thread:00000000000000b2 December 27 [4/4(5)] Greg Chao-Kuei Hung 洪朝貴, ...
這表示有一組 thread 內含 4 封郵件, 在檔案系統裡共儲存在 5 個檔案裡。 我所看到的 [A/B(C)], A 跟 B 都是相同的, 我不知道它們的差異; 而 C 都大於前兩者。 絕大多數的 threads 都是獨立單一的信件 (已讀不回), 呈現 [1/1]。
我用這個指令把所有 threads 按照 「包含幾封郵件、使用了幾個檔案」 分類,
並數一下每一類的 thread 個數:
notmuch search '*' | perl -ne 'm#\[\d+/(\d+)(\((\d+)\))?\]#; $nf=($3 or $1); print("$1,$nf\n")' | sort | uniq -c | perl -pe 's/(\d+) (\d+),(\d+)/($1,$2,$3),/'
得到:
(435,1,1), (38,1,2), (1,1,3), (13,2,2), (3,2,3), (1,2,4), (1,3,3), (2,3,5), (1,4,4), (1,4,5), (1,5,5), (1,5,6), (1,7,7),
這意謂著: 有 435 組對話 「已讀不回」, 各只使用了一個檔案; 有 38 組對話 「一個回覆」 就完結了, 每個 thread 佔用 2 個檔案; ... 有一組對話來來回回共有7封信, 總共存在系統的7個檔案裡。 把上面的資訊稍微編輯一下, 存成 python3 的陣列, 叫它 count_nm_nf。 在 python3 裡面, 以下三式的輸出都是 0:
sum(N for N, _, _ in count_nm_nf) - 499 sum(N*nm for N, nm, _ in count_nm_nf) - 542 sum(N*nf for N, _, nf, in count_nm_nf) - 593
在 ChatGPT 的協助之下, 我終於成功驗證三者之間的關係了。
三、 應用實例: 兩部機器的計數為何不一致?
我曾在兩部電腦 (稱為 A 與 B 好了)
用 notmuch count '*'
數郵件數,
兩者的 mbsync 等等設定都相同 (都去相同的來源抓信),
但數出來的結果卻不一樣。
於是我在其中一部電腦下:
notmuch show --format=json '*' | jq 'map(.[0][0] | [.headers.Date, .id, .headers.Subject])' > date-id-subject.json
取得 「日期-郵件代號-主旨」 清單, 存成 json 格式,
再用小程式 nmj2csv.pl
轉成 csv 格式: nmj2csv.pl date-id-subject.json | sort > date-id-subject.csv
。
(jq 教學)
若有必要可再砍掉主旨欄 (不知為何, 同一封信件, 在不同機器, 主旨字串有長版跟短版):
cut -d, -f -2 date-id-subject.csv > list-A.csv
。
在另一部機器也如法泡製。 如此得到的兩個檔案, 就可以這樣比較:
comm -3 list-A.csv list-B.csv
查出相異的部分, 再查出相異訊息的 thread id
(notmuch search ...
所顯示的第一個欄位)
最後用 notmuch show thread:000xxx
查看訊息內容,
發現原來是 B 電腦在 mbsync -a
的過程當中被我用 ^C 中斷過,
結果微軟的 outlook server 出現了通知錯誤的
"幽靈信件", 無法直接刪除。
於是我用: notmuch search --output=files "subject:'Retrieval using the IMAP4 protocol failed'" > to-delete.txt
列出所有 "幽靈信件" 的路徑 (都在 ~/Maildir/ 底下)、
一口氣刪除所有 "幽靈信件" 的檔案:
[危險指令,請確認已有備份才做!]
rm $(cat to-delete.txt)
、
最後再 mbsync -a
把改變回傳。
因為我的 ~/.mbsyncrc 裡面有一句 Expunge Both
,
所以微軟伺服器那邊的也終於一起刪掉了。
四、 tagging
本來想直接用 「搜尋」 的方式來管理郵件; 後來發現中文搜尋有字數限制, 所以覺得可能還是學用 tag 來管理比較方便。
這個指令: notmuch tag +plurk -- from:notifications@plurk.com
撈出所有來自 notifications@plurk.com 的郵件, 幫它們加上 "plurk" 這個標籤。
"--" 之前可以有好幾個 +標籤 (新增標籤) 或 -標籤 (刪除標籤);
"--" 之後是搜尋條件。
我想把 「INBOX 裡面帶有 plurk 標籤的郵件」 都搬到 plurk/ 目錄底下。
按照 Maildir 的結構, 實際上應該要放到 .../plurk/cur/ 目錄。
搬完之後要執行 notmuch new
讓它重整資料庫!
notmuch search --output=files tag:plurk | grep '/gmail/INBOX/cur/' > to-move.txt mv $(cat to-move.txt) ~/Maildir/gmail/plurk/cur/ notmuch new
以後有需要、學到別招, 再補在這裡。
五、 補充
regex 跟 jq 太好用了!
Notmuch 比較欠缺有系統的教學文。 還好 ChatGPT 現在變得很強了, 回答我很多問題。
今天先介紹獨立在命令列上的使用方式; 與 neomutt 的整合, 改天再試。
沒有留言:
張貼留言
因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。