ShellScriptでドヤりたいGWアドベントカレンダー1日目いくで。
ブラウザの履歴ってその人がその日何をやっていたのか示すものとして結構な情報詰まってると思うのよね。
「俺今日何やってたっけ・・・」っていう時の思い出し作業や、「さっき見てたページもう一回開きたい」ときとか
ターミナルからさくっと開けたら便利じゃねってことで作った。
Requirement
- Mac
- fzf
Chrome履歴のDBの場所
Chromeに関するファイルは~/Library/Application\ Support/Google/Chrome/
にある。
履歴に関するものはSQLiteのDBで保存されている。
~/Library/Application\ Support/Google/Chrome/Default/History
ChromeHistoryにアクセスする
SQLiteなのでsqlite3
コマンドで扱える。
$ sqlite3 ~/Library/Application\ Support/Google/Chrome/Default/History
でテーブル等を見ることができるが、Chromeを開いているときはChromeがLockしているので
Error: database is locked
とアクセスが弾かれてしまう。 Chromeを一旦閉じて接続するか、一旦DBのコピーをしてそれに接続するといった形を取る。
# Lockされているのでコピーを取る $ cp ~/Library/Application\ Support/Google/Chrome/Default/History ~/ # コピーしたDBに接続する $ sqlite3 ~/History
こうすればChromeを開いたままChromeHistoryにアクセスできる。
ChromeHistoryからアクセス履歴を取得する
Historyにはいくつかテーブルがあるが、アクセス履歴を保存しているのはurls
テーブル。
sqlite3のコマンドは他で調べたらいっぱい出てくるので適当にググってね。
$ sqlite3 ~/Library/Application\ Support/Google/Chrome/Default/History SQLite version 3.19.3 2017-06-27 16:48:08 Enter ".help" for usage hints. sqlite> .headers on sqlite> select * from urls; id|url|title|visit_count|typed_count|last_visit_time|hidden 57647|https://www.google.co.jp/save?authuser=0|コレクション|2|0|13200489910808958|0 57648|https://number333.org/|iPhone・Mac・ガジェットブログ "monograph(モノグラフ)"|1|0|13200489927232416|0 57649|https://qiita.com/luccafort/items/e36faff2d7e4320f2bf3|tigがLibrary not loadedとエラーになってしまう現象の対応 - Qiita|1|0|13200491048935113|0 57650|https://orebibou.com/2019/01/macos%E3%81%A7%E3%80%8Cdyld-library-not-loaded-usr-local-opt-readline-lib-libreadline-7-dylib%E3%80%8D%E3%81%AA%E3%82%8B%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E5%87%BA%E3%82%8B%E3%82%88%E3%81%86/|MacOSで「dyld: Library not loaded: /usr/local/opt/readline/lib/libreadline.7.dylib」なるエラーが出るようになる | 俺的備忘録 〜なんかいろいろ〜|1|0|13200491115865059|0
アクセス時刻を表すlast_visit_time
カラムの値が通常のunixtimeとは異なるから注意。
どうやら基本時刻が1601-01-01 00:00:00
に設定されているらしいので、変換が必要になる。
時刻の変換、ソート等をしたら最終的には下のようなクエリになった。
SELECT url, title, DATETIME(last_visit_time / 1000000 + (strftime('%s', '1601-01-01') ), 'unixepoch', '+9 hours') AS date FROM urls GROUP BY title ORDER BY date DESC LIMIT 10000 ;
実行結果
url|title|date https://orebibou.com/2019/01/macos%E3%81%A7%E3%80%8Cdyld-library-not-loaded-usr-local-opt-readline-lib-libreadline-7-dylib%E3%80%8D%E3%81%AA%E3%82%8B%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E5%87%BA%E3%82%8B%E3%82%88%E3%81%86/|MacOSで「dyld: Library not loaded: /usr/local/opt/readline/lib/libreadline.7.dylib」なるエラーが出るようになる | 俺的備忘録 〜なんかいろいろ〜|2019-04-23 20:05:15 https://www.google.co.jp/search?q=dyld:%20Library%20not%20loaded:%20/usr/local/opt/readline/lib/libreadline.7.dylib|dyld: Library not loaded: /usr/local/opt/readline/lib/libreadline.7.dylib - Google 検索|2019-04-23 20:05:12 https://qiita.com/luccafort/items/e36faff2d7e4320f2bf3|tigがLibrary not loadedとエラーになってしまう現象の対応 - Qiita|2019-04-23 20:04:08 https://qiita.com/nwtgck/items/f5427c0d0f7827658bd5|Macでawkを実行するとエラー: "dyld: Library not loaded: /usr/local/opt/readline/lib/libreadline.7.dylib" となる問題の解決法 - Qiita|2019-04-23 20:02:08
あとはこれをShellで包んであげればOKだ。
ShellScriptでイジイジする
基本的な流れは
- ChromeHistoryをコピーする
- コピーしたChromeHistoryにexpectでクエリ投げる
- クエリの結果をawkで整形してfzfに食わせる
でfin。イージーですね。
chromeHistory.sh
# ChromeのDBの内容をcsvで一時保存するパス PATH_CHROME_HISTORY=/Users/`whoami`/chrome_history.csv function export_chrome_history() { local USER=`whoami` # Chromeを開いているとdbがロックされるのでコピーしたものを参照する cp ~/Library/Application\ Support/Google/Chrome/Default/History ~/ local SQL=" SELECT url, title, DATETIME(last_visit_time / 1000000 + (strftime('%s', '1601-01-01') ), 'unixepoch', '+9 hours') AS date FROM urls GROUP BY title ORDER BY date DESC LIMIT 10000 ; " # そのままSQLを流すとエラーが出るので改行を消す local SQL=$(echo "${SQL}" | tr '\n' ' ') # コマンドで参照できるようにDBの内容をcsvに書き出す expect -c " spawn sqlite3 /Users/$USER/History expect \">\" send \".mode csv\r\" expect \">\" send \".output $PATH_CHROME_HISTORY\r\" expect \">\" send \"$SQL\r\" expect \">\" " >/dev/null } function show_chrome_history() { local filter=${1:-""} local chrome_history=$(cat $PATH_CHROME_HISTORY | tr -d '"') # 見栄えを良くするためURLは表示しない local select_history=$( echo "$chrome_history" \ | grep "$filter" \ | awk -F ',' '!a[$2]++' \ | awk -F ',' '{print $3"\t"$2}' \ | tr -d "\r" \ | fzf \ | tr -d "\n" ) # URL取得処理 if [ -n "$select_history" ]; then # URLを取得する際タイトルでgrepするため local title=`echo "$select_history" | awk -F '\t' '{print $1}'` local url=`echo "$chrome_history" | grep "$title" | head -n 1 |awk -F ',' '{print $1}'` open $url fi } export_chrome_history show_chrome_history $1
実行
$ sh chromehistory.sh
sh chromehistory.sh PATTERN
でgrepできるようにしている。予めある程度絞り込んだ結果を出したいときやURLで絞り込みたい時に使う。
fzfの選択画面では見栄えのため、URLを表示させていないから用意した。ぶっちゃけ使った試しはない。
日付ごとに出す
直近のアクセスしたページを出力するのならば上記で事足りるが、「昨日みたあのページなんだっけ・・・」という時にfzfの画面で一々下に行くのはなんとも愚かだと思うので、日付ごとに出力するようにする。
function show_by_date() { local chrome_history=$(cat $PATH_CHROME_HISTORY | tr -d '"') # 表示したい日付を選択する local select_date=$( echo "$chrome_history" \ | awk -F ',' '{print $3}' \ | awk -F ' ' '{print $1}' \ | grep -P '^[0-9]{4}-.*' \ | sort -ur \ | tr -d "\r" \ | xargs -I {} gdate '+%Y-%m-%d (%a)' -d {} \ | fzf \ | awk -F '(' '{print $1}' ) show_chrome_history $select_date }
GNU系のdate
コマンドで曜日込みの日付を出力して、日付を第一引数に渡してgrepさせているだけですね。
sh chromehistory.sh -d
みたいな形で実行したかったので最終的には以下。
chromehistory.sh
PATH_CHROME_HISTORY=/Users/`whoami`/chrome_history.csv function export_chrome_history() { ... } function show_chrome_history() { ... } function show_by_date() { ... } function main() { export_chrome_history if [ "$1" = '-d' ]; then show_by_date else show_chrome_history $1 fi } main $1
終わり
fzfに食わせるだけでいろんなものがだいぶ便利になるよね。
大体面倒くさい作業あったらどうにかしてfzfに食わせられないか考えちゃうんだけどそれだけで午前の業務が終わることが多々ある。ごめんなさいね。