ドキュメントを読めば
ldap[s]://<host>[:port]
と書いてあるので当然である…。気をつけましょう。
特に使える予定もなさそう。
ドキュメントを読めば
ldap[s]://<host>[:port]
と書いてあるので当然である…。気をつけましょう。
特に使える予定もなさそう。
slapd のセットアップで、ドキュメントに基づいて
# cat ~/admin.ldif dn: dc=example,dc=com objectClass: organization objectClass: dcObject dc: example o: example dn: cn=admin,dc=example,dc=com objectClass: organizationalRole cn: admin
を ldapadd
しようとしたら
# ldapadd -Y EXTERNAL -H ldapi:// -f ~/admin.ldif SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 adding new entry "dc=example,dc=com" ldap_add: Invalid syntax (21) additional info: objectClass: value #0 invalid per syntax
というエラーが。
どうやらこれは objectClass
が定義されていない時に出るエラーらしいが、 organization
も dcObject
も core.schema にあるし普通に定義されている。
おかしいな〜と思ってググってると同じような症状の人が。
https://www.openldap.org/lists/openldap-software/200110/msg00255.html
なるほど最後のスペース…と思って自分のものを見なおしてみたら含まれていた!!!!
どうやら openldap のドキュメントからそのままコピーしたらスペースが含まれてしまうらしい。気をつけましょう…。
↑の人はこれで2日潰れたらしい。2日は潰しませんでしたが2時間は潰しました…。
最近京大の五十嵐先生が公開しているテキストを使って OCaml に入門していた。
このテキストは object まで行かないので入門したとは言えそうにないが、一区切り付いたので初心を記録する意味で雑に感想を書いておく。
主に Haskell を考えながら書いています、が Haskell もそんなに知らないです。
Haskell のほうが記号が多くて便利だし、楽しさはあると思う 。ラムダ式などはシュッと書きたい。
でもシンプルな構文により初見で分かりやすいのも長所である。どっちもどっちだ(まだ途中なので本当にシンプルなのか分からないけど)。
構文はあまり好きではない(嫌いでもない)けど、諸概念は良いし納得できるものも多いので難しいところだなあという感じ。
とりあえず Haskell の入門を再開したくなった。
まだ途中だからね!
最近とにかく強い静的型付け言語を使いたい気持ちが高まっていて、python のような軽い文法で無いかなあと探していたら nim があった。また使いたい。
他の現代的な言語として Kotlin や Swift は最高だと思っているけど、エディタでサクッと書くには向いてない気がするんですよね。。
Python における bundler といえば (超ざっくり言えば) virtualenv になるのですが*1、イマイチ使いにくいなと思っていたら pipenv
というものが登場していた。ちょっと使ってみたのでメモ。
Pipfile(.lock)
を元に良い感じに virtualenv 作成 + pip install
してくれるやつ
Pipfile
が使える便利 virtualenv wrapper という感じ$ pip3 install pipenv
--three
と指定すると Python3 だけの環境が作られる。--two
も同様。
$ pipenv --three Creating a Pipfile for this project... Creating a virtualenv for this project... ⠋Already using interpreter /usr/bin/python3 Using base prefix '/usr' New python executable in /home/nonylene/.local/share/virtualenvs/pipenv-test-Iy4gNUM5/bin/python3 Also creating executable in /home/nonylene/.local/share/virtualenvs/pipenv-test-Iy4gNUM5/bin/python Installing setuptools, pip, wheel...done. Virtualenv location: /home/nonylene/.local/share/virtualenvs/pipenv-test-Iy4gNUM5
home 以下に virtualenv を作成されたくない場合は環境変数 PIPENV_VENV_IN_PROJECT
を 1 にすれば .venv/
に作成される。この辺 --path
で指定したいですね。
Pipfile
は Gemfile
と同じ要領で使えるもの。toml で記述する。
[[source]] verify_ssl = true url = "https://pypi.python.org/simple"
pip 2.0 に向けて Pipfile
の仮実装があり、これを使っているらしい。
Pipfile
を主に実装した人は pipenv
作った人と同じ人だった。実装は落ち着いているけど pip に取り入れられるのはいつになるのか…。
pipenv install [package]
でインストールするとパッケージが virtualenv にインストールされ、 Pipfile
も置き換えてくれる。
$ pipenv install requests Installing requests... Collecting requests Downloading requests-2.13.0-py2.py3-none-any.whl (584kB) Installing collected packages: requests Successfully installed requests-2.13.0 Adding requests to Pipfile's [packages]... P.S. You have excellent taste! ✨ 🍰 ✨
最後の行は作者のライブラリを入れた時に表示される。良い話。
$ cat Pipfile [[source]] verify_ssl = true url = "https://pypi.python.org/simple" [packages] requests = "*"
Pipfile.lock
の生成。
$ pipenv lock Locking [dev-packages] dependencies... ⠹Locking [packages] dependencies... ⠹Updated Pipfile.lock!
Pipfile.lock
が生成される。 json らしい。人間が触るものではない。
$ cat Pipfile.lock { "_meta": { "hash": { "sha256": "da2810af0c3b5333e0de2fce9bea2a228812e2014e5f5fe3b1c533badc6c24e4" }, "requires": {}, "sources": [ { "url": "https://pypi.python.org/simple", "verify_ssl": true } ] }, "default": { "requests": { "hash": "sha256:1a720e8862a41aa22e339373b526f508ef0c8988baf48b84d3fc891a8e237efb", "version": "==2.13.0" } }, "develop": {} }%
$ pipenv shell
を用いることで、virtualenv の activate された shell が起動する。
$ pipenv shell Launching subshell in virtual environment. Type 'exit' or 'Ctrl+D' to return. $ python3 Python 3.5.2 (default, Nov 17 2016, 17:05:23) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import requests >>> requests.get("http://example.com") <Response [200]>
$ pipenv run
を使えば bundle exec
っぽいことができる。これが便利。
$ pipenv run python3 -c "import requests; print(requests.get('http://example.com').text)" <! doctype html> ...
ただ spawn
していて新しいプロセス立ててるので pipe が上手くいってなく、パイプやリダイレクションができない。*2
追記: マージされたので今後は大丈夫になるでしょう。
pipenv install
をすると Pipfile.lock
|| Pipfile
にもとづいてインストール・ virtualenv 構築してくれる。README.md
にだいたい書いてある )Remember where the virtual environment was created · Issue #252 · kennethreitz/pipenv · GitHub
Pipfile をお先に扱える + virtualenv の便利 wrapper として bundler 感覚で使えるのが良いと思いました。積極的に使っていこうと思います。
Vim のキーマップを簡単に切り替えられるプラグイン、 vim-keymaps を作りました。
下のように、複数のキーマップを設定することができ、簡単に切り替えることが出来ます。下では <C-k>
でキーマップを切り替えています。
今すぐダウンロード!
各キーマップを示すディクショナリを入れた配列を g:keymaps
に設定し、後はキーマップを変更するためのキーを適当に割り当てる or コマンドを打つと使えます。詳しくは README.md
で。
初めは接続されているキーボードでなんとか判別できないかなと考えましたが、 ssh 先でキーボードの配列を読むのは不可能なので手動で変更するようにしました。
私は普段、コーディングするアプリケーション上では数字と記号を入れ替えて使っています。例えば 1
は !
になり、 !
は 1
になります。
瞬時に括弧などが打てるので非常に便利に使っていたのですが、キーボードによって数字と記号の関係は異なるので、JIS 配列に合わせたマッピングを書いていると US キーボードでは崩壊してしまいます。記号だけではなく数字も入力できなくなるので非常に困る。
しかし、いろいろあって先月から US 配列の mac を使うことになりました。
mac のキーボードのみ使うなら .vimrc
を書き直して US にマッピングし直せば良いのですが、普段接続して使っている HHKB はもちろん JIS 配列。本体キーボードで使う時だけ paste モードで使う運用をしていました。
しかし paste モードのままでは tab で(空白ではなく) tab が入力されたり、数字記号以外の各種マッピングが効かないなど困ります。
これはもうキーマップをキー配列ごとに変更するしかない。 せっかくの機会なのでプラグインを自分で作りました。
vim のプラグインを書くことは初めてでした。まともに vimscript も書いたこと無かったし。
書くときは下のサイトを参考にしたり、有名なプラグインのコードを見たりしながら書きました。
vim のドキュメントも結構読みました。読みやすい英語だったし、充実していて良かった。
vimscript, はじめは変数のスコープ定義が特殊だったりしてとっつきにくいですが、書けてくるとなかなか楽しいです*1。
設定ファイルをどのように記述しようか迷って、結局 vimscript の配列を使うことにしました。ただ、 vimscript で配列や辞書型を複数行に記述しようとすると割りと面倒で、行の最初に \
を書くことで行の継続としなければなりません。あくまで行の継続なので、途中にコメントを書くことは出来ません…。( vim は一行ごとのコメントしかできない ) README を書いてる時にその事実に気づいて修正しました…。
マッピングの数は結構多いので、.vimrc
に長大な配列を記述することになります。以下は自分の設定 を半分ぐらい省略したもの です。
let g:keymaps = [ \ { \ 'name': 'JP', \ 'keymap': { \ 'noremap!': { \ '1': '!', \ '3': '#', \ '4': '$', \ '5': '%', \ '6': '&', \ '!': '1', \ '"': '2', \ '#': '3', \ '$': '4', \ '%': '5', \ '&': '6', \ "'": '7', \ '(': '8', \ ')': '9', \ }, \ 'imap': { \ '2': '<Plug>delimitMate"', \ '7': "<Plug>delimitMate'", \ '8': '<Plug>delimitMate(', \ '9': '<Plug>delimitMate)', \ }, \ 'cnoremap': { \ '2': '"', \ '7': "'", \ '8': '(', \ '9': ')', \ }, \ }, \ }, \ { \ 'name': 'PASTE', \ 'paste': 1 \ }, \]
dein のように toml や json に分けれればなあと思ったのですが、 vimscript では標準ライブラリでシュッとパースすることはできません*2。Python バインドでも使えば良いのですが、標準では入っていないことが多いので諦めました*3。
この g:keymap
からマッピングするのは非常に素朴で、コマンド、左辺、右辺をスペースでつなげて放り込んでいます。なのでオプションを適当に入れても動くし、他のコマンドも打てちゃいます(キーマップ削除する時にエラー出ると思いますが)。
execute(l:cmd . ' ' . pair[0] . ' ' . pair[1])
特に立派な API は無かったのでこうしました。スペース等も自分で書くのと同じようにエスケープすれば動きます。
デフォルト設定ではキーマップ変更時に以前のマッピングを削除しています。よって、マッピングと対になるアンマッピングコマンドを生成する必要があります。
これも素朴に1文字目と2文字目をパースしています。
function! s:create_unmap(origin) let l:first = a:origin[0] if l:first is 'n' if a:origin[1] is 'o' " noremap let l:cmd = 'unmap' else let l:cmd = 'nunmap' endif elseif l:first is 'm' " unmap let l:cmd = 'unmap' else let l:cmd = l:first . 'unmap' endif if a:origin =~ '!' let l:cmd .= '!' endif return l:cmd endfunction
このプラグインは paste モードをサポートしているのですが、paste モードに入ってしまうとマッピングが全て外れるのでキーマップの切り替えができません。
set pastetoggle=<同じキー>
とすることで vim の機能によって paste モードを抜けることができるのですが、vim の機能で抜けているので次のキーマップには移動されず paste 用キーマップのままになってしまいます。
結局、autocmd
を使うことによって paste モードの変更を検知して、フック上でキーマップを変更することにしました。
augroup keymaps autocmd! autocmd OptionSet paste call s:on_change_paste() augroup END function! s:on_change_paste() " sometimes v:option_new not working if g:keymaps_paste_auto_rotate && !&paste \ && get(keymaps#get_current_keymap(), 'paste', 0) " exit paste mode -> rotate call keymaps#rotate_keymap() endif endfunction
keymaps#get_current_keymap_name
で現在のキーマップ名を取れるようにしたので、これを lightline に表示すれば現在のキーマップを簡単に見れます。最初に示した gif もそうしています。
paste モード変更時にもうまくするには lightline も paste を検知できるように autocmd
を新しく設定する必要があります。
augroup local autocmd! autocmd OptionSet paste call lightline#update() augroup END
<SID>
をキーマップに使っているとその <SID>
は実行時のスクリプトに依存するのでプラグイン用のスコープになってしまう。
<expr>
をキーマップに使って関数を実行したい場合、呼び出す関数はグローバルでなければならなかったりする。autoload/
に多くを分割したのだけど、最初のキーマップを設定するために起動時に読み込まれるので逆効果になっているかもしれない。
endfor
は end
にできるが endfunction
は end
に省略できなかったりして面白い。
l:
をちゃんと書いてたけど実は暗黙的に設定されるっぽい。.vimrc
サクサク書けそう。'
が一瞬で打てるのが良いですね。でも数字と記号を入れ替えていると 0
に記号が割り当てられてしまって不便だったりする。これで今まで通り記号が簡単に入力できるようになりました。 「paste モードではないが記号のマップは削除したキーマップ」なども設定できて快適です。
書いておかないとあとで分からなくなるのでメモ。
zsh 便利ですよね。vcs_info
も便利ですよね。
vcs_info
は名前の通り VCS の情報を出してくれる君です。
zshのターミナルにリポジトリの情報を表示してみる · けんごのお屋敷
最近 zsh のプロンプトを以下のように変更しました。
旧
新
ポイントは左にあった vcs_info
が右に移動していることです。
vcs_info
を右に移動するには vcs_info
を単純に RPROMPT
に移動させれば良いのでは?と思いますが、 RPROMPT
はコマンド入力と同じ場所に表示されるため、パスやコマンドが長いと消えてしまいます。
なら上に表示すれば良いのではと思って PROMPT
と RPOMPT
を二行にしてみると、今度は表示されなくなってしまいます。
RPOMPT
は PROMPT
が複数行に渡っても最終行と同じ行に表示され、改行ができません。
一行上の右端に表示するには、 precmd
内でプロンプト復帰前に printf
コマンドを用いて表示する必要がありました。
色がない場合、 printf
で右端に表示するには以下のように行なえば良いです。
# プロンプト復帰前に呼ばれる precmd() { # $prompt_right -> 右端に表示する文字列 # $prompt_left -> 左端に表示する文字列 printf "%s%*s" "$prompt_left" "$((${COLUMNS} - ${#prompt_left}))" "$prompt_right" }
%*s
( *
で幅を指定する) で長さに "$((${COLUMNS}-${#prompt_left}))"
(全体の横の長さから $prompt_left
の長さを引いたもの) を指定することによって、右寄せを実現しています。
しかし、これを色付きで行うと崩れてしまいます。
なぜなら、ANSI エスケープシーケンスを含めた時、 $prompt_right
等にエスケープシーケンスが入って文字の長さが長くなり、 printf
の幅空けや ${#prompt_left}
による文字長の取得が狂うためです…!!!!😇
この時、ちゃんとシーケンスの長さを考慮してあげるとうまく表示できるようになります。
# 長さは適当に 10 にしている printf "%s%*s" "$prompt_left" "$((${COLUMNS} - ${#prompt_left} + 10" "$prompt_right"
vcs_info
の action 問題vcs_info
には actionformats
という機能があり、コンフリクト解決やリベース時などにその action
を表示することが出来ます。
自分は、acrion
の中身は目立つように赤色にしています。なので action
があると、シーケンスの文字数が増えて再び描画が崩れてしまいます。
action
があるかどうかは precmd
内部では分かりません。どうしようかなと調べていると、 vcs_info
にはフック関数を設定できることが分かりました。
これを使って、フック関数の中で action
があるかどうかを見れば、あとは変数にセットすることで precmd
内でも分かります。
最終的に以下の様になりました。
# tput でエスケープシーケンスを出力する tput_normal=$(tput sgr0) tput_yellow=$(tput setaf 3) tput_red=$(tput setaf 1) tput_cyan=$(tput setaf 6) # vcs_info の設定 zstyle ':vcs_info:*' enable git hg zstyle ':vcs_info:*:*' formats "[${tput_yellow}%r: %b${tput_normal}]" zstyle ':vcs_info:*:*' actionformats "[${tput_yellow}%r: %b${tput_normal} (${tput_red}%a${tput_normal})]" # hook 関数の設定 # メッセージが設定される直前 zstyle ':vcs_info:*+set-message:*' hooks vcs_info_hook # VCS が存在しないとき zstyle ':vcs_info:*+no-vcs:*' hooks no_vcs_hook # VCS がないときのエスケープシーケンス長 novcs_color_len=$((${#tput_cyan} + ${#tput_normal})) # VCS があるが action がないとき noaction_color_len=$((${novcs_color_len} + ${#tput_yellow} + ${#tput_normal})) # VCS があり、action もあるとき action_color_len=$((${noaction_color_len} + ${#tput_red} + ${#tput_normal})) # $prompt_color_len にシーケンス長を設定する # VCS があるときの hook function +vi-vcs_info_hook() { # action があるかどうかは hook_com[action] を見れば良い if [ -z ${hook_com[action]} ]; then prompt_color_len=$noaction_color_len else prompt_color_len=$action_color_len fi } # VCS がないときの hook function +vi-no_vcs_hook() { prompt_color_len=$novcs_color_len } precmd() { vcs_info # 左寄せする文字列 prompt_left="${tput_cyan}${USER}@${HOST}${tput_normal}" # 各情報を表示 printf "\n%s%*s\n" "$prompt_left" \ "$((${COLUMNS}-${#prompt_left}+${prompt_color_len}-1))" \ "${vcs_info_msg_0_}" } # このプロンプトは printf 後に表示される (パスの部分) PROMPT='%~ %(!. !root! #.>) '
途中で -1 しているのは、右寄せの最後に一文字空けるためです (wrap っぽく判別されるのを防ぐ)。
これで大体いい感じになりました。
ウィンドウのサイズを変更した時、 RPROMPT
では良い感じに右寄せ文字が移動してくれるのですが precmd
で printf
した文字列は再描画されないので残念ながらこのままではズレてしまいます。
ここで、 TRAPWINCH
を用います。
TRAPWINCH
はサイズが変更された時に呼ばれる関数です。しかし、単純にここで printf
すると、プロンプトの下に追加されてしまうので思った通りになりません。
そこで、 printf
していたのを PROMPT
に変数として組み込み、TRAPWINCH
内で zle reset-prompt
することによって PROMPT
ごと再描画するようにします。
# $prompt_header を生成する function generate_promopt_header() { prompt_header=$(printf "\n%s%*s" "$prompt_left" \ "$((${COLUMNS}-${#prompt_left}+${prompt_color_len}-1))" \ "${vcs_info_msg_0_}") } precmd() { vcs_info prompt_left="${tput_cyan}${USER}@${HOST}${tput_normal}" # printf をやめて $prompt_header を生成するのみとする generate_promopt_header } TRAPWINCH() { # 長さが変わったので $prompt_header を再生成 generate_promopt_header # プロンプト再描画 zle reset-prompt } # $prompt_header を改行前に含める PROMPT='${prompt_header} %~ %(!. !root! #.>) '
こうして、幅が変わっても無事に再描画されるようになりました。
実はまだ問題が残っていて、ブランチ名等が日本語等のときにカラム幅が合わなくなり、はみ出してしまいます。
wc -c
等を使ってもバイト数だから上手くいかないし、どうしたものか…。
まあ日本語で git ブランチや git 用のディレクトリ作ることはそうそう無さそうなので今回は放置。
全体的な zshrc
はこちらです。(大したことやってない)
新年なので Shell の pipe と redirection を実装した。
前回の記事はこちら
前回実装してみて、さすがに pipe と redirection ぐらいは実装しておいてもいいのでは?と思って実装してみた。
パイプについての説明は
が分かりやすくて、ここを見ると大体いい感じに実装できた。
一つ困ったのは、試しに
$ ls | ls
とした時に
ls: write error: Broken pipe
が確率的に発生していた。試しに C で同じような実装をすると特に発生せず、速度の問題かと思って C で sleep
しても再現しなくて困っていた(まあこんなコマンド打つことないけど…)。
結局原因は Python がデフォルトでは SIGPIPE
を無視することで、これによって後段の ls
が終了しても前段が正しく終了せずに ls
が実行されてしまっていた。確率的に起きていたのは SIGPIPE
が execvp
が実行される前か後かの違いによるものだと思う。
Python で SIGPIPE
を無視しないようにすると直ったので良かった。
signal.signal(signal.SIGPIPE,signal.SIG_DFL)
リダイレクションも pipe と原理は同じで、ファイルディスクリプタを dup
するだけでシュッと実装できた。
段々気合でパースしている部分が大変になってきている…。
面白かったのは
$ ls > a | cat
とした時に zsh では出力がなされたけど bash / dash では出力されなかった。
これは、 zsh の multios
という機能によって zsh が redirection と pipe どちらにも出力しているからだった。
unsetopt multios
すると、bash / dash と同じ挙動になった。なるほど〜