読者です 読者をやめる 読者になる 読者になる

opamp_sando's blog

クソザコが割りと適当なことを書くためにある備忘録です。あとたまに普通の日記も書きます

RaspberryPi2を買って遊んでる

お久しぶりです。Raspberry Pi2をついに購入しました。今日は購入後どういうことをしてるかだけメモしておきます。
ある程度やりたいことができて一段落したら何か備忘録を書くかもしれません。

f:id:opamp_sando:20151019224546j:plain

見えにくいですが、机の横に金属製のカゴを取り付けてそこにおいてます。ラズパイケースは買ってないので、机に直接置くのはいろんな意味で嫌だったので、手元に合ったものと近くの店に売っていたもので適当に仕上げました。
GPIOを活用していろいろやっていきたいので、卓上のブレッドボードに配線が届く位置にしたかったのもあってこの位置です。

OSはもちろんArchLinux ARMを使っています。現在のところ不便はありません。インストールも、公式WebサイトのInstallationに従えばそのままできました。ただし、rootのファイルシステムext4ではなくf2fsを使ってみることにしました。以下の記事を参考にしてます。

http://morinezumiiii.hatenablog.com/entry/2014/08/14/090223

こちらのサイトでは/boot/cmdline.txtを書き換えることになっていますが、不要になったようで特に変更せずにインストールできました。また、起動後にディスプレイやキーボードを繋がなくても自動でDHCPでネットワークに接続してsshdも起動していたのでそのままログインして設定できました。

とりあえず、現在やっていることとしては外部からのアクセスができるように部分的に家のポートを開放してsshを外から(もちろん公開鍵認証のみ)受け付けるようにしたのと、httpを外からうけつけるようにしてます。写真にも写ってるようにSDRを使って1090MHz(つまりADS-Bとか)を受信してるので、dump1090を外部から参照できるようにhttpのポートを開けています。

とりあえず今後の課題

  • アナログ温度センサーをAVRでAD変換してSPIなどを使ってラズパイに送信し、室温をブラウザなどから確認可能にする
  • アンテナの位置が室内で電波の入りがとても悪いのでなんとかしたい(なんとかできるとは言ってない)
  • ある程度落ち着いたらfr24にもフィードしたい

こんなところ。

現在SPI通信に苦戦中。ちなみにちゃんとできた暁にはAVRと温度センサーは小さいユニバーサル基板上に実装してラズパイと同じかごに入れて常時動かす予定。

ArchLinux Installation

今更ですが、前に書いたインストール方法の記事を誤って削除したので布教も兼ねて再度書きたいと思います。

ということで今回はArchLinuxのインストール方法の記事です。

https://www.archlinux.org/

インストール環境

インストールは仮想マシンに対して行うことにします。今回はBIOSから起動することを想定します。 UEFIから起動する場合は過去の記事も参照してください。

また、仮想マシンのインストール・セットアップなどについては省略します。

isoファイルのダウンロード

isoファイルをミラーサーバーからダウンロードしておきます。

http://ftp.tsukuba.wide.ad.jp/Linux/archlinux/iso/

http://ftp.jaist.ac.jp/pub/Linux/ArchLinux/iso/

すでにローカルにisoをダウンロードしている場合は多少古くてもそれを利用することができます。この記事は2015.06.01のisoファイルでインストールを行ったものです。

起動

isoファイルを利用してマシンを起動します。物理マシンの場合はUSB,CD,DVDなど物理媒体に書き込んでから起動します。仮想マシンの場合はisoファイルを直接指定して起動します。

起動すると以下のような画面になります。64bitの場合はx86_64を、32bitの場合はi686を選択して起動してください。

f:id:opamp_sando:20150618235806p:plain

起動したらキーマップを設定します。USキーボードを利用している場合は設定の必要はありません。それ以外のキーボードを使用している場合はloadkeysコマンドを使ってキーマップを設定します。

$ loadkeys jp106

パーティション分割

パーティション分割にはcfdiskを利用することができます。最近知らない間にcfdiskがGPTにも対応していたのでGPTでパーティション分割を行いたい場合でも利用できます。

$ cfdisk

基本的に上のコマンドで自動的にデバイスを選択して起動しますが、明示的に指定することもできます。

$ cfdisk /dev/sda

起動したら以下のような画面になります。今回はMBR形式で分割します。

f:id:opamp_sando:20150619000649p:plain

MBRの場合はdosを選択します。GPTの場合はgptを選択します。すでにパーティションが分割されている場合は上記の画面は表示されません。初期化してからインストールする場合はcfdiskにzオプションをつけて起動してください。

起動後に実際にパーティションを分割します。今回は簡単に次のように分割しました。
以後この記事では下図のように分割したことを前提にコマンドを記載しますので、必要に応じて読み替えてください。

f:id:opamp_sando:20150619001028p:plain

分け方は任意ですので、好きなように分けることができます。またMBRの場合はBootフラグを忘れずに立てます

分割ができたらWriteしてQuitします。

パーティションのフォーマット

先ほど分割したパーティションをフォーマットします。安定と安心のExt4やXFSを使っても良いですし、最近流行りのbtrfsを使うこともできます。
/bootパーティションにはXFSは使えないので無難にExt4を使うことをおすすめします。

パーティションのフォーマットはmkfs系のコマンドを使って以下のように作成します。

$ mkfs.ext4 /dev/sda1
$ mkfs.xfs /dev/sda6

また、スワップ用のパーティションを切った場合はmfswapコマンドを利用します。また、その後スワップを有効化します。

$ mkswap /dev/sda5
$ swapon /dev/sda5

パーティションのマウント

パーティションのマウントはmountコマンドで/mnt以下に行います。

$ mount /dev/sda6 /mnt
$ mkdir /mnt/boot
$ mount /dev/sda1 /mnt/boot

このように階層が浅いものから順番にマウントし、mkdirして次のパーティションをマウントします。

基本パッケージのインストール

ミラーの変更

ミラー設定は後回しでも良いですが、ここで設定しておくと次のパッケージインストールが高速化できます。

$ vi /etc/pacman.d/mirrorlist

mirrorlistファイルを開いて、任意のミラーをリスト上部に持ってきます。上にあるものから優先して利用されます。
以下は例です。

f:id:opamp_sando:20150619002224p:plain

base、base-devel、grubのインストール

インストールする準備が整いましたので、実際に基本パッケージをインストールしていきます。それにはpacstrapコマンドを使うことができます。

$ pacstrap /mnt base base-devel grub openssh

とりあえず、このようにbaseとbase-develは入れておきましょう。opensshは任意です。baseやbase-develに含まれていそうで含まれてなくて後で困ったことがあるので念の為ここで指定しておきます。
ブートローダーは今回grubを使うのでgrubを入れておきます。他のものを使う場合はそれでも問題ありません。
その他事前にインストールしておきたいパッケージがあればここで指定することができます(シェルやエディタなど)

しばらく待ってインストールが終了したら次に進みます。

fstabの生成

インストール環境のfstabを生成します。genfstabコマンドを使うと自動生成できます。

$ genfstab -p /mnt > /mnt/etc/fstab

chrootする

chrootしてインストール環境に入ります。この時実際にはarch-chrootコマンドを使います。

$ arch-chroot /mnt

インストール環境の設定

chrootしたらホスト名などインストール環境の設定を行います。

ホスト名設定

ホスト名は/etc/hostnameファイルに書き込みます。

$ echo "madokawaii" > /etc/hostname

キーマップの設定

US以外のキーボードを利用している場合は、/etc/vconsole.confにキーマップの設定を書き込んでおきます。

$ echo "KEYMAP=jp106" > /etc/vconsole.conf

タイムゾーン設定

タイムゾーンを日本時間に合わせておきます。

$ ln -s /usr/share/zoneinfo/Japan /etc/localtime

ロケール設定

/etc/locale.genを編集してロケール設定を行います。

$ vi /etc/locale.gen

必要なロケールコメントアウトしておきます。

f:id:opamp_sando:20150619003420p:plain

よくわからなければja_JP.UTF-8とen_US.UTF-8あたりをコメントアウトしておくと良いと思います。

編集したら保存して、locale-genコマンドを実行します。

$ locale-gen  

rootパスワード設定

rootユーザーのパスワードを決めておきます。

$ passwd

grubインストール

gurb-installコマンドでgrubをインストールします。

$ grub-install /dev/sda

続いてgrub-mkconfigで設定ファイルを生成します。

$ grub-mkconfig -o /boot/grub/grub.cfg

再起動

これでインストール作業は終了なのでchroot環境からexitコマンドなどで脱出して、umountでマウントしているパーティションをアンマウントしたらrebootします。

$ umount /mnt/boot
$ umount /mnt
$ reboot

rebootしたらインストールされたArchLinuxが起動します。*1

f:id:opamp_sando:20150619004228p:plain

うまく行かなかったら

reboot後再度インストーラが立ち上がる

まず、再起動しても再度インストーラが起動する場合は仮想マシンがインストールされたArchLinuxよりインストーラのディスクを優先して読んでいる可能性があります。仮想マシンの設定を確認して仮想HDD優先で起動するようにしてください。主にVirtualBoxのことです。

boot失敗する

ブート失敗して起動しない場合は、ブートローダのインストール・設定またはパーティション分割時にbootフラグを立て忘れている可能性があります。再度インストーラを起動して設定を確認してください。

ネットワークへの接続設定について

起動後インターネットにはつながってない状態です。したがって設定が必要になります。

有線接続や仮想マシンの場合

有線接続で1つのネットワークに繋ぎっぱなしにする場合や、仮想マシンで簡単に使う場合はdhcpcdを利用して簡単に接続できます。

$ ip link show

とコマンドを入力すると有効なNIC一覧が表示されるので、ネットワーク接続に利用するNIC名を確認します。その後、systemctlコマンドで次のようにします。例としてNIC名がens3だった場合を示します。

$ (sudo) systemctl enable dhcpcd@ens3
$ (sudo) systemctl start dhcpcd@ens3

enableにすることで、次回起動以降自動で起動します。誤って登録した場合はdisableにして実行することで無効化できます。

Staticな設定やWi-Fiを利用したい場合

staticなネットワーク設定やWi-Fiの設定を行いたい場合はnetctlを利用して設定することをおすすめします。

https://archlinuxjp.kusakata.com/wiki/Netctl

netctlは/etc/netctl以下に設定ファイルをおいて設定することができます。設定ファイルはたいていの場合exampleに入っているものを改良するかwifi-menuコマンドを使って設定できます。
それでも動かない場合は手動で書き換える必要があります。

*1:ロケール設定を何か誤ったようでロケールがおかしいね

quicklispでプライベートなリポジトリを作る試み

とりあえずタイトル通り、quicklispでユーザープライベートなリポジトリ(dists)を作る。ずっと作れないと思ってたが作れるようなのでメモ。

※一応できたが一部よくわからない場所をゴリ押しているので注意
 後日何か分かり次第修正・変更します。

環境の準備

今回はサーバードメインexample.comとする。また、httpで通信できるWebサーバーを動かしておく必要がある(今回はlighttpdを使った)

また、これ以降今回作成するプライベートなリポジトリ(dist)のルートディレクトリを$DROOTと表すことにする。また、$DROOTは必ずしもWebサーバーのドキュメントルートである必要はなく、UserDir以下とかでも大丈夫なようだ。
今回の例では$DROOTはドキュメントルートと同じとする。

テスト用のプロジェクトの用意

とりあえず実際にquicklispでインストールしてみるプロジェクトをvtestという名前で用意した。

(cl-project:make-project #p"vtest")

で、quicklisp本家のdistを参考にしたところgitベースで管理していることもあってか日付毎にディレクトリやファイル名を分けて管理しているようなのでそれを真似して、vtestプロジェクトを配布用にvtest-20150617-gitという風にrenameして圧縮した。

$ cp -r vtest vtest-20150617-git
$ tar cfvz vtest-20150617-git.tgz

で、このtarballを$DROOT/archive/vtest/2015-06-17/以下に配置した。

releases.txtを作成

$DROOT/dist/examplecom/2015-06-17/releases.txtを作成する。

# project url size file-md5 content-sha1 prefix [system-file1..system-fileN]
vtest http://example.com/archive/vtest/2015-06-17/vtest-20150617-git.tgz 10818 1c0827c819b2ab8ec95ae74c5b5a8bb6 3aa2f73886cac23db4bce179166cc4f03e3f8e78 vtest-20150617-git vtest.asd

この1行目のシャープで始まる行はHeaderというらしく無いとエラーになったのでコピペしてつけるようにしよう。

URLは名前の通りhttpアクセスした場合の先ほどのファイルのURLを記述する。sizeの大きさはlsのlオプションで確認できるファイルサイズでいいようだ。ちなみにこの値を間違うと失敗する。

$ ls -l

で、file-md5は名前の通りtarballのmd5sumで良いようだ。

$ md5sum vtest-20150617.tgz

問題は次のcontent-sha1で、これがよくわからない。上記の値はとりあえず適当に書いた値だが、それでもちゃんと動いた。
本家distsのreleases.txtの一番はじめにある1amというプロジェクトで試したところ、どうやら名前通り本当にプロジェクト中のファイルをすべて連結したもののsha1sumの値のようだが、どのような順番で連結したものなのかだとか、ディレクトリが含まれる場合はどうすればよいのかは不明。

prefixにはアーカイブ化したプロジェクトのディレクトリ名を記述すれば良いはず。 そして、最後にasdファイルを指定する。

これでreleases.txtは終了。

systems.txtを記述

systems.txtを記述する。同様に$DROOT/dist/examplecom/2015-06-17/systems.txtとして配置する。
今回のvtestは依存関係がないので単純な中身になる。

# project system-file system-name [dependency1..dependencyN]
vtest vtest vtest

依存関係がある場合、この後に列挙していけば良いようだ。

distinfoの記述

このあたりもよくわからないことが多いのだが、とりあえず本家の真似をして作ってみる。
$DROOT/dist/examplecom/2015-06-17/distinfo.txtをまずは作成。

name: examplecom
version: 2015-06-17
system-index-url: http://example.com/dist/examplecom/2015-06-17/systems.txt
release-index-url: http://example.com/dist/examplecom/2015-06-17/releases.txt
archive-base-url: http://example.com/
canonical-distinfo-url: http://example.com/dist/examplecom/2015-06-17/distinfo.txt
distinfo-subscription-url: http://example.com/dist/examplecom.txt

で、作ったら$DROOT/dist/examplecom.txtとしてシンボリックリンクを作成

$ ln -s $DROOT/dist/examplecom/2015-06-17/distinfo.txt $DROOT/dist/examplecom.txt

注意として、どうやらこのファイルのパースはスペースを区切りとして1行をsplitしてパースしているようなので余計なスペースを入れると後々失敗する。

quicklisp-clientの設定

ql-dist:install-distを使う方法

ql-dist:install-distを使うことで先ほどサーバー上に配置したdistinfo.txtをダウンロードして自動で設定できるようだ。

* (ql-dist:install-dist "http://example.com/dist/examplecom/2015-06-17/distinfo.txt")  

とする。

直接設置する方法

ql-dist:install-distを使わずに手動で配置する場合は次のようにする。
ローカルのquicklispのdistsディレクトリの中に、今回作る自作のexamplecomという名前のディレクトリを作成する。

$ mkdir ~/quicklisp/dists/examplecom

人によってはquicklispのprefixを変えているかもしれないが、今回はデフォルトの場合のコマンド。

で、このディレクトリ以下にdistinfo.txtファイルを作って先ほどサーバー側に作ったファイルと同じものを作成する。
つまり以下のやつ。

name: examplecom
version: 2015-06-17
system-index-url: http://example.com/dist/examplecom/2015-06-17/systems.txt
release-index-url: http://example.com/dist/examplecom/2015-06-17/releases.txt
archive-base-url: http://example.com/
canonical-distinfo-url: http://example.com/dist/examplecom/2015-06-17/distinfo.txt
distinfo-subscription-url: http://example.com/dist/examplecom.txt

で、これが配置できたらREPLに入って以下を実行する。

(ql-dist:enable (ql-dist:dist "examplecom"))

これでTが返ってきたらおk。

vtestをインストール

これで準備ができたのでvtestをインストールしてみる。

(ql:quickload 'vtest)

これでvtestがロードされれば成功。

まとめ

quicklispの内部の仕組みが少しだけわかった気がするが、依然として不明な場所が多い。今回の場合distinfo.txtをローカルでも手動で作成したが、サーバーでも作成しているのだからサーバーだけ指定して取ってくるquicklispの関数か何かがあるかもしれない?
quicklisp-clientのコードを読んでいたらql-dist:install-dist関数があったのでこれを利用できた。

あと、cannonical-distinfo-urlとdistinfo-subscription-urlの2つを指定する意味がよくわからないのもある。 これはソースを読んだわけじゃないからまだはっきりとしないが、おそらくdist本体のアップデートの為のものじゃないかと思う。はっきりしたらまた追記・修正する。

後はcontent-sha1は本当にコードを読まないとどうやって生成するのかも何に使われてるかもイマイチわからない。 content-sha1は名前通りパッケージ内部のファイルをすべて連結したもののsha1sumだった。これにはドットで始まるドットファイルも含まれる。結合させる順番は単純にlsで表示されるアルファベット順で、ディレクトリなら再帰的に内部のファイルを連結させる。ただ、適当な値を使ってもやっぱりパッケージはquickloadできるのは謎。

だがとりあえず、スクリプトか何かでasdf準拠のプロジェクトをパッケージ化してarchive以下に適当に配置、releases.txtとsystems.txtを生成して新しいdistを生成、examplecom.txtのリンクを更新という作業を自動化することができればそれなりに運用していけそうな気がする。まだまだ謎は多いから他人に使わせるのはちょっとまだ無理な気がするが。

あと、Common LispにはRubygemsclojarsのようなユーザーがPONと自作のパッケージを簡単に公開できるサービスがない(知らないだけ?)けど、quicklispはプライベートなリポジトリを作れるのならquicklispを利用する形で作れなくはなさそう?

buildappを使ってみた

LispGUI使って何か作ろうかと考えていたが、その前に流石にGUIだと実行可能ファイルとして実行できたほうが嬉しいのでCommon Lispで実行可能ファイルを生成する方法を調べた。

この辺の方法は処理系依存で大体どの処理系でもCore Imageをファイルに書き出す的な方法で実現することができるらしい?のだが、いつものCLikiさんによるとbuildappという補助ツールがあるようなので今回はこれを使ってみた。

buildappのインストール方法は省略する。

簡単なGUIを表示するコードを書く

まずは、buildappにかけるコードを書いてみる。LTKが簡単そうだったのでとりあえずこれを使ってみた。

;;; guitest.lisp
(use-package :ltk)
(defun hello-gui (args)
  (with-ltk ()
    (let ((b (make-instance 'button
                            :master nil
                            :text "ボタン"
                            :command (lambda ()
                                       (format t "Clicked!!~%")))))
      (pack b))))

ほとんどチュートリアルの写し。

buildappで実行可能ファイルを作る

ltkはquicklispで入れてるので、buildappのload-systemだけでは読み込めなかった。
ということで次のようにすればよいらしい。

$ sbcl --no-userinit --no-sysinit --non-interactive --load ~/quicklisp/setup.lisp --eval '(ql:quickload :ltk)' --eval '(ql:write-asdf-manifest-file "ql-manifest.txt")' 

$ buildapp --manifest-file ql-manifest.txt --load-system ltk --load guitest.lisp --output guihello --entry hello-gui

これでguihelloという実行可能ファイルができた。

f:id:opamp_sando:20150613223455p:plain

動かしてみたらこんな感じ。

参考資料

http://www.cliki.net/creating%20executables

http://stackoverflow.com/questions/18917067/how-to-use-buildapp-in-combination-with-quicklisp

http://www.peter-herth.de/ltk/ltkdoc/

http://www.xach.com/lisp/buildapp/

emacsでOSで分岐するマクロを書いてみた

emacsでOSごとに分岐するマクロを書いてみた。

(defmacro cond-system (&rest slit)
  `(cond
    ,@(mapcar
       (lambda (elem)
         (let ((os-type (car elem))
               (body (cdr elem)))
           (cond
            ((listp os-type) `((or ,@(mapcar (lambda (os) `(string-match ,(symbol-name os) system-configuration)) os-type)) ,@body))
            ((eq os-type 'other) `(t ,@body))
            (t `((string-match ,(symbol-name os-type) system-configuration) ,@body))))) slit)))

多分ちゃんと動く・・・かな?

(cond-system
      (win (format "win"))
      (darwin (format "darwin"))
      (linux (format "linux"))
      ((freebsd netbsd) (format "BSD"))
      (other (format "Others")))

こんな感じで使える。

(cond
 ((string-match "win" system-configuration) (format "win"))
 ((string-match "darwin" system-configuration) (format "darwin"))
 ((string-match "linux" system-configuration) (format "linux"))
 ((or
   (string-match "freebsd" system-configuration)
   (string-match "netbsd" system-configuration))
  (format "BSD"))
 (t (format "Others")))

こんな感じに展開される。

Common Lispみたいにパッケージがあるとシンボルを使ってこんなことするのもなかなか面倒になるので、この点はemacsの良い所?な気がする。まあ、パッケージなくて困ることのほうが多い気はするけど。 本当はキーワード使おうかと思ってたけど、Emacs Lispでキーワードを文字列にする方法がわからなかったorz

maximaで軸にラベルをつける

x軸やy軸にラベルを付けたい時に、gnuplotのxlabelとかを指定してもつけれなかったが、普通に以下のようにすれば良いらしい。

plot2d([[discrete,data[1],data[2]],[discrete,data2[1],data2[2]]],[style,points],[xlabel,"x軸ラベル"],[ylabel,"y軸ラベル"],[gnuplot_preamble, "set xtics 1; set title 'グラフタイトル'"]);

要は

[xlabel, "x軸のラベル"]
[ylabel, "y軸のラベル"]

を渡してあげるとよい。

ついでにグラフタイトルについてはgnuplotのオプションで指定できた。

参考資料

http://www.k3.dion.ne.jp/~kawada/Uinx/maxima-plot.html

PostgreSQLに画像を放り込んで遊んだ日記

半分日記のような内容です。

やったこと

  • Common LispPostgreSQLに接続してディレクトリ内の画像を放り込む簡単な関数を書いた
  • psqlコマンドで放り込んだ画像データのHEXテキストを取り出した
  • xxdコマンドで元に戻した
  • ちゃんと取り出せた(やったー)

みたいな内容です。

Common Lispでデータを放り込むプログラム

ブログに貼るとは思ってなかったのでほとんど抽象化もクソもないほげぴよコードですが...
とりあえずcl-fadとcl-dbiを使わせて頂いています。

picturesデータベースのtestテーブルを予め作成しておいたのでそのへんを作成してるコードはないです。

CREATE TABLE test (filename varchar(256) primary key,body bytea);
(defun to-bytea-str (sarray)
  (let ((bstr (loop for i from 0 to (1- (array-total-size sarray)) by 1 collect (format nil "~2,'0X" (aref sarray i)))))
    (format nil "~{~A~}" bstr)))

(defun read-bindata (filename)
  (with-open-file (in filename :element-type '(unsigned-byte 8))
    (let ((binary (make-array (file-length in) :element-type '(unsigned-byte 8))))
      (read-sequence binary in)
      binary)))

(defun insert-dir (path)
  (if (cl-fad:directory-exists-p path)
   (let ((db (dbi:connect :postgres
                          :host "192.168.11.10"
                          :database-name "pictures"
                          :username "opamp"
                          :password "opamp"))
         (files (remove-if #'(lambda (x)
                               (cl-fad:directory-pathname-p x))
                           (cl-fad:list-directory path :follow-symlinks nil))))
     (dbi:with-transaction db
       (dolist (f files)
         (let ((bin (read-bindata f)))
           (let ((q (concatenate 'string "INSERT INTO test (filename,body) VALUES ('"
                                 (namestring f) "',"
                                 "E'\x" (to-bytea-str bin) "'"
                                 ")")))
               (dbi:do-sql db q)
               (format t "~A Finished." f))))) 
     (dbi:disconnect db))
   nil))

当初はsxqlを使って見てたんですが、bytea型のデータを格納する場合にinsert-intoのset=をどうすればよいかわからなかった(いろいろ試したけどどれもエラーだった)ので諦めてSQLを直書きしています。

あとは、format関数が結構便利でしたというくらいですかね。リストになった文字列の連結や、1Byteを表す2文字の16進数文字列を作るときとか便利でした。
はじめはwrite-to-stringでやろうとしてたんですが、例えば"0"のような1桁の数値はそのまま1桁になってしまうので、SQLに記述する文字列に直した時に4bitずれるんですよね。
具体的には

ffd8 ffe0 0010

のようなデータが

ffd8 ffe0 0104

のようになってしまいます。本来"00"となってINSERTのVALUESにかかれてほしいところが"0"になったためですね。他にも"01"なども"1"となるのでこうなります。
formatで以下のように書くと解決できました。

(format nil "~2,'0X" 0)  

確認までに

* (loop for i from 0 to 15 do (format t "~2,'0X~%" i))
00
01
02
03
04
05
06
07
08
09
0A
0B
0C
0D
0E
0F
NIL

という感じです。

psqlコマンドで取り出した

取り出すときはpsqlを使いました。psqlでpicturesデータベースに入って以下のようにしてみます。

\copy (SELECT encode(body, 'escape') FROM test LIMIT 1) TO '/home/xyzzy/dbout.hex'

これで"/home/xyzzy/dbout.hex"にテーブルの要素1つのbodyが16進数ダンプされたような形式で出力されます。
これはただののASCIIテキストなので画像ビュアーなどでは開けません。

xxdで逆変換する

xxdで逆変換します。

$ cat ~/dbout.hex | xxd -r -p > image.jpg

xxdコマンドつよい。

終わり

如何せんPostgresもCommon Lispも初心者なのでもっと良い方法があるかもしれないが、まあ今日は別に特に目的なく遊んでみただけなので...

それにしてもxxdはvim付属コマンドなのでvimを入れないと使えないのがアレですね。同じような機能をもってる似たようなソフトはないものか。今や黒歴史と化した昔作った某アレもありますが、あれは決まったフォーマット以外は読み込めないのでクソです。

DBはLAN内の勉強用に立ててるPostgreSQL9.4.1を使ったんですが、勉強用だし使うのは私一人でたかが知れてるからとlog_statementをmodにしてたんですが、100ファイルくらいある画像フォルダをinsert-dir関数でうpしたらログファイルの容量が瞬間的に900MBくらいになってまともにログ見れなくなって困りました。でかいログファイルはどうやってみるのが普通なんですかね。思いつく方法といえばheadやtailやgrepで行数削ってみるかsplit当たりで分割して見るくらいしか思いつきませんが。

参考資料

http://qiita.com/tamurashingo@github/items/f4ec10dc87720243c8e5

http://stackoverflow.com/questions/1521509/common-lisp-integer-to-hex-conversion

http://stackoverflow.com/questions/6730729/how-to-download-postgres-bytea-column-as-file

あとは利用したライブラリのドキュメントとHyperSpecですね。

Firefox ブラウザ無料ダウンロード