アジャイルサムライ読書会渋谷道場に参加してました。

7/27に行われたアジャイルサムライ読書会渋谷道場に参加していました。

この読書会では事前に本を読み、先に疑問点や感想などをwikiにまとめ、それをもとに話し合うという形式で行われました。その内容は参加した人たちの手によりgithubのページに記述されています。

実体験に基づいた話や、他のアジャイルの資料に関する話などさまざまなことを聞くことができ、自分の意見を口に出すことで内容をそしゃくできたように感じました。
また懇親会に参加し、楽しいひとときを過ごすことができました。

今回の読書会での対象は1−3章でしたが、ここはまだまだ導入部分であり、これからがもっともっと楽しくなりそうなので、引き続き学習を続けていきたいと思います。

全体のKPTや感想などは上記のwikiに記述したため、こちらでは個人のKPTを記述したいと思います。

  • Keep
    • 自分の意見を発言した
    • 懇親会に楽しく参加できた
  • Problem
    • ファシリテーターのほうだけみて発言してしまった
    • 会場下調べが乏しく、予定していた復習ができなかった
    • 始まる前にwikiを更新しなかった
  • Try
    • 周りの様子を把握し、誰に向かって話しているのか意識して発言する
    • 予習してwikiに疑問点を書く

第二回も参加予定です。参加者の皆様よろしくお願いします。

github製のwiki'gollum'で日本語が使えないときの対処法

この記事はgritの2.4.2がgem installできるようになれば用済みではあるが、本日大変困ったのでメモ。

githubwiki部分を切り出したwiki engineのgollumは、日本語を使おうとすると次のようなエラーが表示されてしまう。

Grit::GitRuby::Internal::LooseObjectError at /
size mismatch

    file: loose.rb
    location: get_raw_object
    line: 59

これは、現時点でgem install gritで入るversionが2.4.1であることによって起きている。gollumが内部で使用しているGit repositoryを扱うライブラリであるgritがbytesizeを使うべきところでlengthを使い文字列を調べているのが原因らしい。
この件に関する修正はgithubのmasterにはcommitされているものの、その修正が含まれるgemはまだgem installで入れられる状態ではない。そのため、自分でgithubからmasterをcloneし、packageを作成する必要がある。

今回は以下のようなコマンドで上記の作業を行った。

$ git clone git://github.com/mojombo/grit.git
$ cd grit
$ rake build
$ gem install pkg/grit-2.4.1.gem --local

gritを入れなおしたらgollumを再起動する。このとき、既にエラーが出てしまったリポジトリで再起動しても同じエラーが表示されてしまった。その場合は素直にgollumのデータを記録するリポジトリを再作成すれば、日本語の文章が記録・表示できるようになっている。

CoffeeScriptの文法メモ

ソースコードにある'#=>'は説明用のもので、その値を評価した値を載せている。CoffeeScriptの機能ではない。
確認したversionは1.1.0

基本

ブロックはインデントで表現

hello = (name) ->
  console.log "hello #{name}"

hello 'kei_q'

変数の宣言に'var'は必要無い

n = 1

comment

コメントは'#'で書く

n = 1 # comment

'###'で範囲コメント
コメント内で'*/'は使えない

###
comment
###

string

文字列の途中で改行を入れても無視される

s = "hoge
fuga"
s #=> "hogefuga"

文字列の中に'#{}'使うことで、式を評価した値を埋め込むことができる。シングルクォートでは展開されない。

s = 'hoge'
"#{s}" #=> "hoge"
'#{s}' #=> '#{s}'

クォート、もしくはダブルクォート3つでheredocumentとなる
このとき、先頭の空白は行頭の空白が最も少ない行の分だけ省かれる

s = '''
  first line
    second line
'''
s #=> 'first line\n  second line'

s2 = '''
  first line
 second line
'''
s2 #=> ' first line\nsecond line'

bool

trueとfalseにはそれぞれ別名が用意されている。

bool 別名
true yes, on
false no, off

regex

複数行にわたるregexが書ける
このregexでは空白が無視される
また、#{}による式の展開もできる

r = /// (
  hoge    # comment
  | fuga  # comment
  | #{s}  # comment
)
///

array

数値の範囲による配列の生成ができる。最後の値を含む場合は'..', 含まない場合は'...'を使用する。

[1..3]  #=> [1, 2, 3]
[1...3] #=> [1, 2]
[3..1]  #=> [3, 2, 1]

配列に対して範囲を指定すると、該当する部分を抜き出すことができる。範囲は0から数える。
後ろから数える場合、負の値で指定する。こちらは-1から数える。
始点か終点の片方を省略することができる。その場合、範囲は始点なら0、終点なら-1を指定したものと同じになる。

arr = [0..9]
arr[4..5] #=> [4, 5]
arr[4..]  #=> [4, 5, 6, 7, 8, 9]
arr[..5]  #=> [0, 1, 2, 3, 4, 5]
arr[..-2] #=> [0, 1, 2, 3, 4, 5, 6, 7, 8]

sliceは左辺にも使用することができる。
詳しくは後述のパターンマッチを参照

arr = [0..9]
arr[4..5] = [-1, -1]
arr #=> [0, 1, 2, 3, -1, -1, 6, 7, 8, 9]

'in'演算子を使うことで、配列に値が含まれているか調べることができる。

3 in [1, 2, 3] #=> true

object

オブジェクトリテラルでは、'{'を省略したyamlのような書き方ができる。
以下の二つのobjは同じ。

obj =
  a : 1
  b :
    c : 3
    d : 4

obj = {
  a:1,
  b:{
    c:3,
    d:4
  }
}

classなどの予約語をオブジェクトのプロパティとして使用する場合、明示的に文字列にしなくてもそのまま使える。

obj =
  class : 'hoge'
  var : 'fuga'

obj.class #=> 'hoge'

'of'でkeyが存在するか調べることができる

obj = { a : 1, b: 2}
'a' of obj #=> true

control expression

全般

javascriptの制御構文は使えない
値を返す

if, unless

条件式の括弧は必要無い

if str == 'hoge'
  f()
else if str == 'fuga'
  g()
else
  h()

unless(= if not)が使用できる

unless str == 'hoge'
  f()

後置if, unlessが使用できる

f() if str == 'hoge'
g() unless str == 'hoge'

三項演算子はない。if-then-elseを使用する

if true then f() else g()

値を返すので、変数に代入できる

s = if false
  'a'
else if true
  'b'
else
  'c'

s #=> 'b'
switch

breakは自動挿入される
whenの項目には複数記述できる
whenに続けて処理を書く場合は'then'の後に書く

switch str
  when 'foo' then h()
  when 'hoge, 'fuga'
    f()
  else
    g()

switchの条件省略をすると!!hogeでtrueになるものが選択される

s = switch
  when undefined
    'hoge'
  when true
    'fuga'

s #=> 'fuga'
while, until, loop

条件式において括弧は必要無い

i = 0
while i < 9
  i += 1

until(=while not), loop(while true)が用意されている

i = 0
until i > 10
  i += 1

loop
  if bool
    break
  f()

後置while, untilが使用できる

f() while bool
g() until bool

whenで条件が記述できる

while i++ when arr[i]
  f(arr[i])
for-in

配列のループにはfor-inを使用する

for val in array
  hoge(val)

引数二つで配列のindexも合わせてbindされる

for val, index in array
  f(index, val)
for-of

オブジェクトのkeyを走査するにはfor-ofを使用する

for key of obj
  key

引数二つでkeyとその値を返す

for key, val of obj
  f()

forの後に'own'を付けると、hasOwnPropertyでチェックした値だけになる

for own key, val of obj
  f()
for-in, for-of共通の記述

'when'で条件を記述し、trueになったもののみblockを評価

evenlist = for n in [1..9] when even(n)
  n

evenlist #=> [2, 4, 6, 8]

'by'でn個毎に評価。最初の値は評価される

ns = for n in [1..9] by 3
  n

ns #=> [1, 4, 7]

後置for

f(val) for val in array

comprehensions
括弧を付けないと異なる解釈をされるので注意

arr = (n*n for n in [1..3]) #=> [1, 4, 9]

括弧を省略すると、以下のような意味になる

v[i] = val[i] for val, i in array
(v[i] = val[i]) for val, i in array

forの次にdoが付いた関数を書くと、forの中に直接処理が書かれるのではなく、別に関数を定義して呼び出したのと同じになる
#788を参照

for n in [1..9]
  do (n) ->
    console.log

_fn = (n) ->
  console.log
_fn(n) for n in [1..9]
try catch
try
  1 / 0
catch err
  "error is #{err}."
finally
  f()

関数

定義

宣言
関数の定義は、引数のリスト、'->'、関数本体の順に書く
関数の返り値は関数の最後の式

add = (x, y) -> x + y
add(1, 2) #=> 3

無引数なら引数の括弧は省略できる

hello = -> console.log 'hello'

関数の引数に値を書いておくことでdefault引数が使用できる
該当する引数が無い場合、指定した値が入る

add = (x, y = 1) -> x + y
add(1) #=> 2
add(1, 2) #=> 3

可変長引数
splatは引数の中で一つのみ
splatの前後に引数がある場合、それを考慮して、残りの値をsplatに入れる

print = (y...) -> y.join(', ')
print = (x, y..., z) -> "#{x}, #{y}, #{z}"

引数にはECMAScript harmonyのdestructuringが使用できる

f = ([a, b], c) -> a+b+c
f [1, 2], 3

関数の前に'do'を付けると、即時関数となる

do ->
  hoge
呼び出し

splat
呼び出しのとき、'...'を配列の後ろに付けることで、展開して関数を呼び出す

f = (a, b, c) -> a+b+c
arr = [1, 2, 3]
f arr... #=> 6

関数呼び出しでは括弧を省略できる
ただし、引数が無い場合は関数そのものと区別が付かないため、呼び出したいときは明示的に括弧を付ける必要がある

add 1, 2
add(1, 2)
add()

複数呼び出しでの括弧補間ルールは、単に式の右から関数の括弧を補間する

add add 1, 2, add 3, 4
add (add 1, 2, (add 3, 4))

上記ルールにより、以下の例は多くの人が期待したとおりにはならない。method chainでは素直に括弧を付けよう

obj
  .hoge 1
  .fuga 2

obj.hoge(1).fuga(2)   # 期待してたもの
obj.hoge((1).fuga(2)) # 現実

class

基本形
コンストラクタは'constructor'という名前で定義する
extendsには一つのみ指定可能
メソッドはオブジェクトリテラルと同様に':'で記述する
インスタンス変数は'@'で書くことができる。これは'this.'と書いたのと同じ意味になる

class A
  constructor: (v) ->

class B extends A
  constructor: () ->
    @x = 1

  getX: -> @x
  setX: (@x) ->

引数に@でthisに代入
以下は同じ

class A
  constructor: (@v) ->

class A
  constructor: (v) ->
    @v = v

'super'で親クラスのメソッドを呼べる

class A
  f: (n) -> n * 2

class B extends A
  f: (n) ->
    super(n) + 3

(new B).f(1) #=> 5

=>を使用すると、関数を定義した場所のthisを関数の内側でbindする

class A
  constructor: (@x) ->
  f: -> setTimeout (=> console.log @x), 2000

methodに@はthisではなくてクラス名に変換
以下は同じ

class Math
  @PI: 3.1415

Math.PI = 3.1415

extendsは演算子としても定義されており、単独で書くこともできる
右結合

A extends B extends C
A extends (B extends C)

classの名前は省略可能。classの返り値を代入することで、以下のように定義することもできる

exports.Hoge = class
  method: ->

blockも省略可能なので、以下もコンパイル可能

class

演算子

以下の演算子は意味が変更されている
javascriptでの'==', '!='はCoffeeScriptには存在しない

CoffeeScript 意味
== ===
!= !==
1 == '1' #=> false

いくつかの演算子には別名が用意されている
|*alias|*意味|
| is | != |
| isnt | == |
| not | ! |
| and | && |
| or | \|\| |

以下の演算子は繋げて記述できる

  • >
  • <=
  • =>
  • is(==)
  • isnt(!=)
x = 3
1 < x < 10 #=> true

演算子と優先順位をgrammer.coffeeから転載
上ほど優先順位が高い

operators = [
  ['left',      '.', '?.', '::']
  ['left',      'CALL_START', 'CALL_END']
  ['nonassoc',  '++', '--']
  ['left',      '?']
  ['right',     'UNARY']
  ['left',      'MATH']
  ['left',      '+', '-']
  ['left',      'SHIFT']
  ['left',      'RELATION']
  ['left',      'COMPARE']
  ['left',      'LOGIC']
  ['nonassoc',  'INDENT', 'OUTDENT']
  ['right',     '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS']
  ['right',     'FORIN', 'FOROF', 'BY', 'WHEN']
  ['right',     'IF', 'ELSE', 'FOR', 'DO', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS']
  ['right',     'POST_IF']
]

予約語

以下のwordが予約語として定義されている

case, default, function, var, void, with
const, let, enum, export, import, native
__hasProp, __extends, __slice, __bind, __indexOf

雑多

prototypeの省略

'.prototype.'と記述するかわりに'::'と記述することができる

class A
A::f = ->
javascriptの埋め込み

'`'で囲うことで、javascriptをそのまま埋め込むことができる。

f = `function() {
  return "hoge"
}`
f() #=> "hoge"
soak existential operator

'?'を使用すると、対象がnull, もしくはundefinedではないことを確認することができる
変数の後ろに付けると存在を確認し、結果をboolとして返す

if var? then true

二項演算子としても使用でき、その場合左辺が存在していなければ右辺が返る

var ? "hoge" #=> 'hoge'
var = 1
var ? "hoge" #=> 1

代入との組合せで'?='も使用できる
以下は変数が定義さていなかったら空のオブジェクトを代入する例

var = var ? {}
var ?= {}
?. 変数の存在を確認
?:: 変数の存在を確認
?[] 変数の存在を確認
?() 関数の存在を確認
hoge?.fuga
hoge?::fuga
hoge?[1]
hoge?()
{var} = hoge

以下は同じ意味

{x} = obj
x = obj.x
代入文でのdestructuring

配列やオブジェクトの構造を書くと、対応する場所の変数に代入される。再帰的に書くこともできる
詳しくはECMAScript harmonyのdestructuringを参照

[swapA, swapB] = [swapB, swapA]
[{width:w, height:h}, hoge] = [{width:1, height:2}, 3]
w #=> 1
h #=> 2
hoge #=> 3
terminator

';'で一行に続けて書くことができる

1 + 2; 3 + 4

不完全な文法で無理矢理改行したい場合は'\'が使用できる
以下の例は問題無くparseできる

n = 1 \
  + 2
未調査

OptCommaの説明
{}や()の補完ルール
左辺に関数呼び出しが含まれる場合のルール
undefinedとvoid 0
代入文のchain
class methodでの=>
mixin (wiki FAQ)
制御構文が値を返す件の具体例
returnの補完ルール
replで変数を含むrangeが正常に解釈されない問題

ブラウザでttyrecのファイルを再生する方法

ttyrecという端末の出力を録画できるソフトウェアがあり、それをブラウザで再生・共有できるttyshareが500を返すようになって久しい昨今、いかがお過しでしょうか。

web上でttyを再生するサービスがあるといろいろ便利だと思うので作りたいと思い、調べていたところjavascriptで実装されたescape sequenceのparserを知りました。試しにそれを使用してttyrecをブラウザで再生するものを作ってみました。 サンプルとして簡単にコマンドの入力とその実行結果を録画したものを用意しています。

肝心のparseと表示の部分はfiresshに含まれるcli.jsを使用しています。もともとpythonで実装されていたVT100parserをjsに移植したもののようで、こちらを使用しました。あとはこれに食わせるためにttyrecの記録形式を少し変更しただけです。

実際に動作させてみたところ、一部sequenceが解釈されていなくて正常に再生されないものがありますが、簡単なvimの動画などはそこそこまともに表示されました。 しかし、実装が一文字一文字spanで囲うなど強引な作りであるため、他のcanvasflashで実装された同種のサービスと比べて動作が重いです。
画面全体を頻繁に書き換えるようなものはchromeでも重たく、当初サンプルとして用意ていたzawの動画は表示が崩れてしまいました。

現状これを使うのは厳しいと感じます。flashと違ってiphoneで見ることもできるのはメリットかもしれませんが、工夫を凝らさないとあまり実用的にならなそうな感じがしました。

全体のソースコードは以下に公開しています。
https://github.com/keqh/replayTerm

プログラミングHaskell 9章 練習問題6の他人の解答を自分なりに変更してみた

以下の記事を見たので、自分なりに書き直してみた。
http://d.hatena.ne.jp/morning_reading/20110415/p1

変更した部分については以下に説明を載せて、最後に全文を載せます。

解説

cls :: IO ()
cls = putStr "\ESC[2J"

type Pos = (Int, Int)
type Board = [Int]

このあたりは変更なし

goto :: Pos -> IO ()
goto (x, y) = putStr $ "\ESC[" ++ show y ++ ";" ++ show x ++ "H"

ここは()ではなくて$で区切るように変更。$は右側の式を評価してから左の関数に適用するもの。

seqn :: [IO a] -> IO ()
seqn []     = return ()
seqn (a:as) = do a
                 seqn as

seqnは、sequence_という名前で同じ機能のアクションが用意されているので、こちらを使います。
このようなよくありがちなパターンはすでに用意されていることが多いので、hoogleで確認すると良いと思います。
今回の場合、IOアクションのリストを順番に実行するアクションが欲しいので、'[IO a] -> IO ()'というクエリでhoogleで検索すれば、該当するものが見つかるはずです。
http://www.haskell.org/hoogle/

showboard :: Board -> IO ()
showboard b = goto (1,1) >> mapM_ showLine (zip [1..] b)
  where
    showLine (id,num) = putStrLn $ show id ++ ": " ++ replicate num '*'

showboardはガッツリ変更。もとはseqnが使われてましたが、意味的にはmapM_のほうが素直に思えたので変更。
また、行の出力は直接書くと見づらいのでwhereで別途定義しました。
takeとrepeatを使用しているところは、同様の関数であるreplicateがあるので、こちらを使用します。
showLineについては、括弧の数を減らしました。関数適用が一番優先順位高いことと、$を使えば括弧がなくてもそれなりにわかりやすいかなと思います。

checkLimit :: Int -> Int -> Maybe Int
checkLimit limit n
  | 1 <= n && n <= limit = Just n
  | otherwise = Nothing

getNat :: Int -> MaybeT IO Int
getNat limit = liftIO readLn >>= MaybeT . return . checkLimit limit

もとはgetNatというひとつのアクションとして定義されていたものを、checkLimitとして一部分離しました。
これは、そもそもIOに依存していないところを分けておくと、副作用に影響されない範囲が明確になりますし、テストもしやすくなります。
getNatはのちに出てくるnextgenアクションのために'MaybeT Io Int'という型になってます。
getLineとreadの代わりに、Preludeで定義されてるこれと同じ機能をもつreadLnに変更しています。MaybeT . returnというのが「pureな値をMaybeT IOにするもの」というふうに見ると、getNatの処理は、
readLnで値を読み込んで、
その値をcheckLimitで検査して
その結果をMaybeT IOとして返す
ものになります。

nextboard :: Board -> Int -> Int -> Board
nextboard b s n = let (xs, (y:ys)) = splitAt s b in xs ++ [y-n] ++ ys

nextboardは、ユーザが選択した行のコマを指定の数だけ取る処理です。getNatのところで引数チェックをしているので、ここでは意図的にvalidationは省かれています。
ここではもとのコードでは再帰処理で実装してますが、splitAt関数を使って分割したあと、リストを再構成する方がわかり易い気がしたのでそうしています。

nextgen :: Board -> IO Board
nextgen b = do
  r <- runMaybeT $ nextgenM b
  case r of
    Nothing -> nextgen b
    Just r' -> return r'

nextgenM :: Board -> MaybeT IO Board
nextgenM b = do
  liftIO $ putStr "Enter slot number: "
  s <- getNat $ length b
  liftIO $ putStr "Enter take number: "
  x <- getNat $ b !! (s-1)
  return $ nextboard b (s-1) x

nextgenは大きく変更しています。プログラミングHaskellでここまでに登場していないMaybeTというモナド変換子を使っています。
本にはない範囲ですが、以下の記述があったので、なんとかできないものかと思って変更して見ました。

ただ2回入力を受け取るのに Maybe 型のパターンマッチをそれぞれ書かないといけなくて、これはまとめて書けるようにできないかと試行錯誤したのですが思いつきませんでした。

nextgen :: Board -> IO Board
nextgen b = do
  r <- runMaybeT $ nextgenM b
  case r of
    Nothing -> nextgen b
    Just r' -> return r'

nextgenは実装をMaybeTを使用したものに分離して、ここでは単純なものになっています。
runMaybeTは'MaybeT IO a'な処理を実行して、IO (Maybe a)を返します。
つまり、ここの処理は、Nothingが返ってくるかもしれないIO処理、つまり入力値の検証機能つきのユーザ入力処理を実行して、 正しくなければ再度nextgen, そうでなければその値を返しています。

nextgenM :: Board -> MaybeT IO Board
nextgenM b = do
  liftIO $ putStr "Enter slot number: "
  s <- getNat $ length b
  liftIO $ putStr "Enter take number: "
  x <- getNat $ b !! (s-1)
  return $ nextboard b (s-1) x

今回一番変更したのはこの部分です。おこなっていることは、
slotの入力を促すメッセージを出力して、
ユーザからの入力を受け取って
コマをいくつ取るか入力を促すメッセージを出力して、
コマの数を受け取って、
最後に受け取った値を反映したboardの値を返す
処理です。コードそのままですね。

'MaybeT IO Board' という型は、IOとMaybeの両方の特徴をもったものになっています。 maybeはモナドでもあるので、モナド変換子によってIOと組み合わせることができます。
それぞれの処理において、どこか途中でNothingが返ってきた場合、nextgenM全体がNothingになります。
そのため、最初のslotの値を入力する時点でNothingが返された場合、次のgetNatに行かず、nextgenM全体がNothingとなります。
全ての処理でJustが返された場合、無事最後のreturnまで到達し、Justが返ります。

Maybeモナドについては、以下にある羊さんの例が参考になると思います。
http://www.sampou.org/haskell/a-a-monads/html/meet.html

nimmt :: Board -> IO ()
nimmt b = do
  cls
  showboard b
  b <- nextgen b
  when (any (/=0) b) (nimmt b)

nimmtアクションでは、もとはifを使用していたところをControl.Monadで定義されているwhenに変更しています。
また、条件部分はセクションを使い、余計な変数を省いています。
allをanyに変更したのは、’全てが0であるか’を調べるよりも、’ひとつでも0でないものが含まれている’ことを調べるほうが走査回数がすくなりますし、whenを使用した関係上、ゲーム続行の条件として条件が示されているほうが素直だと思ったからです。

run_nimmt :: IO ()
run_nimmt = nimmt [5,4,3,2,1]

全文


参考:一から自分で書いたもの

若干の仕様変更はありますが、概ね元ネタと同じような動作をします。

zshでコマンドが空の状態でenter押したときに任意のコマンドを実行する方法

背景

.zsh_historyを見たらlsコマンド打ちすぎなことに気がついた

解決案

コマンドライン空でEnter押したらlsしてほしい

解決するコード

alls() {
  zle accept-line
  if [[ -z "$BUFFER" ]]; then
    echo ''
    ls
  fi
}
zle -N alls
bindkey "\C-m" alls

解説

もともとのEnterは、zleのwidgetであるaccept-lineが割り当てられている。
なので、bindkeyで割り当て直して、accept-lineしつつコマンドラインのBUFFERを調べて、空ならlsを実行するようにしている。

その他改善案

  • chpwd hookで、cdのたびにlsされるようにする
  • filerなどを有効活用する
  • alias l=lsとかで満足する

keyremap4macbookのremap設定を記録しておいて、再設定を簡単にする方法

keyremap4macbookはキーボードの配置をいろいろよきにはからってくれる素晴しいアプリケーション。日本語配列のキーボードを英語配列にしたり、ctrl単体でescにしたり、セミコロンをenterにしたり、spaceがshiftに変更できたりする。 ただ、毎回GUIからポチポチcheckboxをクリックして設定するのはdotfile脳になりつつある自分には辛かったので、なんとかする方法を考えてみた。

keyremap4macbookには一部機能をconsoleから使うことができる。cliのコマンドを引数無しで実行すると、以下のようにusageとexampleが表示される。

$ CMD=/Library/org.pqrs/KeyRemap4MacBook/app/KeyRemap4MacBook_cli.app/Contents/MacOS/KeyRemap4MacBook_cli
$ CMD
Usage:  KeyRemap4MacBook_cli list
  KeyRemap4MacBook_cli selected
  KeyRemap4MacBook_cli changed
  KeyRemap4MacBook_cli select INDEX
  KeyRemap4MacBook_cli enable IDENTIFIER
  KeyRemap4MacBook_cli disable IDENTIFIER

Example:
  KeyRemap4MacBook_cli list
  KeyRemap4MacBook_cli selected
  KeyRemap4MacBook_cli changed
  KeyRemap4MacBook_cli select 1
  KeyRemap4MacBook_cli enable remap.shiftL2commandL
  KeyRemap4MacBook_cli disable remap.shiftL2commandL

上記のコマンドで、enableを使えばremapの設定ができそうなことがわかる。引数として指定するコマンド名は サブコマンドのchangedを使うと、現在有効になっている設定を確認することができるので、それを加工する。

$ $CMD changed | sort
pointing.relative2scroll_rate=500
remap.controlL2controlL_escape=1
remap.jis_command2eisuukana_prefer_command=1
remap.jis_eisuu2commandL_eisuu=1
remap.jis_eisuu2commandL_eisuuremap.jis_escape2eisuuAndEscape=1
remap.jis_escape2eisuuAndEscape=1
remap.jis_jansi=1
remap.jis_kana2commandR_kana=1
remap.jis_kana2commandR_kanaremap.semicolon2return_controlsemicolon2semicolon=1
remap.semicolon2return_controlsemicolon2semicolon=1
remap.space2shiftL_space_keyrepeat=1
repeat.initial_wait=200
repeat.wait=20

enableコマンドはremapのみで、それ以外のrepeatなどはうまく設定できなかった。とりあえずremapの設定を実行するスクリプトを書いてみた。
https://github.com/keqh/_config/blob/master/mac/keyremap4macbook.sh

これを実行すると、remap4macbookの設定が行なわれる。これでmacの再インストールのときにGUIからマウスを使って設定する必要もないし、自分がどんな設定をしたか記録しておくことができる。