プログラミング

WSL BashからWindows 10のクリップボード履歴をSQLで正規表現検索

WindowsのWSL Linux Bashコマンドラインから、windows 10本体のクリップボード履歴をSQLデータベース言語のselect文などを使用し、正規表現検索して出力するようにします。がんばりました。


[yuipro “Windows 10 dittoを使ってクリップボード履歴を正規表現検索(SQL)”]という記事を書きました。書いてる途中でwslから取得したいなーとふと思い、いつもの悪い癖でやってしまいました。

結果から言うといちおうは、いい感じにできたことはできたんですが、かなりツギハギです。1時間以上くらい頑張りました。すごいコードになりました。
ここ数ヶ月で一番考えたかもしれない。

https://yuis.xsrv.jp/images/ss/ShareX_ScreenShot_ab2abf29-c834-46f4-bd0e-bbc64a97e037.png

取り直した。

はい。

clipboard-search(){

    : e.g. clipboard-search [regex] [limit] [offset]

rsub /mnt/c/Users/${WINUSER}/AppData/Roaming/Ditto/Ditto.db

eval "$(cat - <<'EOSS' | sed -Ee "s/@ARG1/${1:-.}/g; s/@ARG2/${2:-1}/g; s/@ARG3/${3:-0}/g"
ssh -q yuis@192.168.0.167 "$(cat <<'EOS'
sqlite3 /home/yuis/rsync/Ditto.db "$(cat <<'EOT'
SELECT mText from Main
WHERE mText REGEXP "@ARG1"
order by lDate desc
LIMIT @ARG2 OFFSET @ARG3
EOT
)"
EOS
)"
EOSS
)"

}



かなり複雑ですが、sqliteデータベースをbashから検索するっていう需要にはこれひとつでいけるので、例えばchromeのlocal storageを正規表現検索したいとか検索履歴を正規表現検索したいとかでも応用効きますし、sshで違う環境のubuntuからデータ取ってきてるので、wslじゃなければオーバーキルなんですけど、このやり方は他のことにも応用効くと思います。

たかがこれだけのことをするだけですが、前準備は長いので、やりたい方は少し覚悟してください。

必要準備

dittoをインストール

この関数ではdittoというクリップボードマネージャーから生成されたsqliteデータベースファイルを使用しています。なのでインスコしてください。

Ditto download | SourceForge.net

WSLの他に、ubuntuのインストールされたpcを用意する。(たぶんvirtualboxでも可)

sqlite3を実行するためのubuntuを用意します。wslではだめな理由は、wslではsqlite3のバージョンが最新のものにできかねるからです。そもそもwslというのはいろんなアプリ(e.g. sqlite)を入れて安定するように作られていないので(少なくとも現段階では)、僕みたいにアップグレードできなかったり、(アップグレードできないとregexが使えない、かもしれない)うまくことが進んでいるように見えてもどっかでバグったりします。

また、このpcのSSH接続において、パスワードを省略できるように設定しておくとストレスフリーかと。

sshでのプロンプトのパスワードを省略する

Sqlite3で正規表現を使えるように設定する

sqlite3はregexが使えます。が、何も設定しないと、Error: no such function: REGEXPとなります。

REGEXP calls a user defined function which must first be defined and loaded into the the database

sqlite3の実行環境で以下。

cat << EOT >> ~/.sqliterc
.load /usr/lib/sqlite3/pcre.so
EOT

How do I use regex in a SQLite query? – Stack Overflow

その他

rsubは以下aliasです。


rsyncubuntupc(){
  rsync -e 'ssh' -r "$1" yuis@192.168.0.167:"${HOME}/rsync/"
}

alias rsub="rsyncubuntupc"

解説

まずrsubですが、これはrsyncしてるだけです。クリップボードが更新されるとdittoのデータベースも更新されます。この関数を呼び出すたびに、dittoの”更新されているかも知れないデータベース”をrsyncしています。
まぁなので、これを省けば早くなると思います。データベースのサイズにもよるでしょうが。しかしこれ省くと整合性がというかなんというかすっきりしませんね。

で…メインスクリプトですが、

ssh -q yuis@192.168.0.167 "$(cat <<'EOS'
sqlite3 /home/yuis/rsync/Ditto.db "$(cat <<'EOT'
SELECT mText from Main
WHERE mText REGEXP "@ARG1"
order by lDate desc
LIMIT @ARG2 OFFSET @ARG3
EOT
)"
EOS
)"

で、まずはrsyncで飛んできたsqliteファイルをsqlite3でselectするコードを、こちらからsshで飛ばしています。

eval "$(cat - <<'EOSS' | sed -Ee "s/@ARG1/${1:-.}/g; s/@ARG2/${2:-1}/g; s/@ARG3/${3:-0}/g"
...
EOSS
)"

ですが、悩んだ末最終奥義を使ってしまいました。まぁ、動くので、大丈夫です。
bashのヒアドキュメントは'EOT'だと式展開しない、EOTだとするということになっています。では、上記スクリプトの'EOS'EOSに変えたらどうなるか。一見SQLを含めたインサイドのスクリプトには、式展開されるようなやつ(e.g. $)はないですね。
じゃあ、今@arg1となっているところを${1}にして試してみましょうか。 …… ファ!?定番のnearエラーが出てしまいました。全くわかりません。どこで変な展開がされてしまったのかわかれば、それにエスケープを入れてやることが出来ますが、全くわかりません。ということで、こういうときは無理に間違い探しをしようとせず、テキストにしてしまって、それをbash以外の言語で置換して、再度evalでテキストをスクリプトとして実行、という流れが王道(僕の中では)です。

まぁこのコード自体、読みやすさより書きやすさ、短さを意識しているので、ヒアドキュメントで縛らなければこういう問題はそもそも起きないはずなんですけどもね。

使い方

yuis@192.168.0.167などを置き換えてください。

: e.g. clipboard-search [regex] [limit] [offset]と書いてるとおりですが、例。

clipboard-search "ruby.on.rails" 1 0 ... 正規表現で検索して、一番更新日の最新のもの1つのクリップボード履歴テキストを出力
clipboard-search "ruby.on.rails" 2 0 ... 正規表現で検索して、一番更新日の最新のもの2つのクリップボード履歴テキストを出力 (複数行の場合はどこで区切られているのかわからなくなるので使い勝手はよくありません。そのための(CSVではなく)データベースですからね。)
clipboard-search "ruby.on.rails" 1 1 ... 正規表現で検索して、二番目に更新日の最新のもの1つのクリップボード履歴テキストを出力
clipboard-search "ruby.on.rails" 1 0 | wc ... バイプすることももちろんできます。
clipboard-search "ruby.on.rails" 1 0 | clip.exe ... クリップボードにコピー。

我ながら天才的な発明をしてしまった。気がしなくもない。

追記

lDateではなく、clipOrderを使ったほうが良さそうです。lDateはおそらく作成されたタイムスタンプ。テキストデータであるmTextは一意の値であると思われます。ので、例えば同一のテキストをコピーした場合、そのテキストは上記のselectで一番最初に来るべきですが、否。そうしたい場合は、clipOrderにします。ctrl+num+をしたときに表示されるものと同じ並び順で出力されることになります。普通の使い方ならこっちでしょう。

clipboard-search(){

    : e.g. clipboard-search [regex] [limit] [offset]

rsub /mnt/c/Users/${WINUSER}/AppData/Roaming/Ditto/Ditto.db

eval "$(cat - <<'EOSS' | sed -Ee "s/@ARG1/${1:-.}/g; s/@ARG2/${2:-1}/g; s/@ARG3/${3:-0}/g"
ssh -q yuis@192.168.0.167 "$(cat <<'EOS'
sqlite3 /home/yuis/rsync/Ditto.db "$(cat <<'EOT'
SELECT mText from Main
WHERE mText REGEXP "@ARG1"
order by clipOrder desc
LIMIT @ARG2 OFFSET @ARG3
EOT
)"
EOS
)"
EOSS
)"

}


ちなみに、以下で履歴を取得できます。clipboard-historyで一番最新のそれ、clipboard-history 1で二番目に最新のそれ、です。


clipboard-history(){
    : e.g. clipboard-history
    : e.g. clipboard-history 1
    clipboard-search . 1 "${1:-0}"
}

正直言って使うタイミングが全くわからない。(なんで書いたんだよ。)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です