深酒とお昼寝で忘れる

深酒とお昼寝で忘れる

素面でも意図したことを忘れがちなしらふいとさんは、忘れる前に何かしら書き残せたらとても満足のようです

複数のスペースを一つに置換できますか(ただし引用符の中は除く)

以前 zsh で、失敗したコマンドはヒストリファイルに保存しない設定をしました(付随して、スペースで始まるコマンドは履歴に残さない、コマンド内の複数のスペースは一つに置換する、といった設定も自前で行ないました)が、使っていくうちにいくつか改善できる点が分かってきました。今回はそのうち、複数のスペースを一つに置換する設定について紹介します。

以前の設定では、複数のスペースを一つに置換する zsh のオプションである hist_reduce_blanks 相当の機能を実現するためにコマンドの文字列に対して tr -s ' ' を実行していました。大抵はこれで問題ないのですが、うまくいかない場合もあります。例えば、

grep    '    something    '

というコマンドは、

grep '    something    '

のように履歴に保存されてほしいのですが、

grep ' something '

として保存されます。grep' の間の複数のスペースは一つに置換したいけれども引用符('")の中についてはそのままにしたいわけです。

ということで、設定を変更します。なるべくシンプルなワンライナーで実現したいと思ったのですが、自分の正規表現力では無理なのでちょこちょこと検索すること数日間……。最終的に、該当個所は下記のようになりました。

local cmd_reduce_blanks
if [[ ${_LASTCMD} =~ \" ]]; then
  cmd_reduce_blanks=$(echo ${_LASTCMD} \
    | perl -pe 's/ +(?=(([^"]*"){2})*[^"]*$)/ /g')
elif [[ ${_LASTCMD} =~ \' ]]; then
  cmd_reduce_blanks=$(echo ${_LASTCMD} \
    | perl -pe "s/ +(?=(([^']*'){2})*[^']*$)/ /g")
else
  cmd_reduce_blanks=$(echo ${_LASTCMD} | tr -s ' ')
fi

この手の処理で活躍する sedawk ではなく、Stack Overflow の回答 を参考にして Perl に頼ることになりました。" の中以外の複数のスペースを一つに置換するコマンドは

perl -pe 's/ +(?=(([^"]*"){2})*[^"]*$)/ /g'

となります(' の中以外を対象とする場合は "' を入れ替えてください)。この正規表現の具体的な説明は こちらの正規表現テスタ をご覧ください。コマンドに "' の両方を使うことはまれだと思うので、どちらか一方の引用符の中かどうかを判定するロジックになっています。

上記のコードでは _LASTCMD という変数にコマンドの文字列が保存されており(参照:以前の記事)、この文字列内の複数のスペースを一つに置換して cmd_reduce_blanks という変数に格納しています。ちなみに "' の両方を使うことはまれと書きましたが、' を引用符としてではなくアポストロフィとして " の引用符の中で使うことはあり得るかもしれません。例えば下記のようなコマンドです。

git    commit    -m    "Thank    God    it's    Friday"

このような場合でも正しく対応して

git commit -m "Thank    God    it's    Friday"

と履歴に保存するため、if 文はコマンドが " を含む場合を ' を含む場合よりも優先しています。いかがでしょうか。いやーしかし、正規表現、覚えられませんねー。

Mac (OS X El Capitan) で /home/$USER を /Users/$USER へのシンボリックリンクにする

あまり需要はない気がしますが記録しておきます。普通にシンボリックリンクを作ろうとすると、

$ cd /home
$ sudo ln -s /Users/$USER
ln: ./$USER: Operation not supported

上記のように怒られてしまいますが、/etc/auto_master ファイルの

/home    auto_home    -nobrowse,hidefromfinder

という行をコメントアウトしてから sudo automount -cv すると /home がアンマウントされるので、無事シンボリックリンクが作成できるようになります。

こちら でみつけました。

(Neo)vim で同じ列内のウィンドウのみ高さを等しくする

Vim のウィンドウを縦に分割し、さらにそれぞれの列を横に分割します。それぞれの列内のウィンドウは高さが等しいとは限らず、最大化されているウィンドウがあったりします。 f:id:someneat:20170816033006p:plain

沢山ファイルを開いて作業をしていると、こんな風にウィンドウの配置が最適化されていくことがあります。さて、そんなときに一つの列内のウィンドウ「のみ」高さをそろえたくなったときには、どうすれば良いのでしょうか。

CTRL-W = とすると、他の列のウィンドウも含む「全ての」ウィンドウの高さ(と幅)がそろってしまいます。 f:id:someneat:20170816033027p:plain

気になったので 聞いてみたところ、速攻で解決しました。:new | q としましょう(:new の代わりに :split でも可)。

マニュアルによると、Vimequalalways というオプションが有効の場合(デフォルトで有効です)、ウィンドウを分割したり(:new)、閉じたり(:q)する際に全てのウィンドウの大きさをそろえようとします。と書いてあるのですが、その挙動はもう少し複雑で全てのウィンドウではなく動作に関連する特定のウィンドウ(?)の大きさがそろうようです。:new するとウィンドウは横に分割され、その列内のウィンドウのみ、高さがそろいます。その後続いて :q すると新しく開いたウィンドウは閉じられ、その列内のウィンドウの高さがそろいます。このようにして所望の動作になるわけです。 f:id:someneat:20170816033046p:plain

既存のウィンドウ操作のショートカットに似たマッピングにしてみます。

nnoremap <C-w>e :new \| q<CR>
nnoremap <C-w><C-e> :new \| q<CR>

地味に便利。