平衡点
2014/10/07
_ マルチバイト文字列の幅を 2 としてカウントする
ログインシェルは zsh ですが, あまりカスタマイズしていない佐々木ですこんばんは. 業務で Windows で作成されたらしき日本語の暗号化 zip ファイルを受け取ってじたばたすることが, 最近になって(ようやく?)無視できないくらい増えてきて嫌気がさしています. 外部にファイルを添付して送信する場合に暗号化した zip ファイルにして送信するって文化は誰が勧めたんでしょうね. 心底無駄なんでやめて欲しいです.
本題
zsh の RPROMT で今いるディレクトリを表示させています. 右端に表示させたいので
# source: sh
# 変数の文字列計算用関数
function count_prompt_chars (){
print -n -P -- "$1" | sed -e $'s/\e\[[0-9;]*m//g' | wc -m | sed -e 's/ //g'
}
# precmd のプロンプト更新用関数
function update_prompt (){
## プロンプト: 1段目左
local ps_user="%(!,%B%F{magenta}%n%b,%n)"
local ps_host="%m"
[[ -n ${SSH_CONNECTION} ]] && ps_host="%F{yellow}%m%f"
local prompt_1st_left="[$ps_user@$ps_host$chroot_info]"
## プロンプト: 1段目右
local prompt_1st_right="[%F{white}%(5~,%-2~/.../%1~,%~)%f]"
## 1段目行の残り文字列の計算
local left_length=$(count_prompt_chars $prompt_1st_left)
local right_length=$(count_prompt_chars $prompt_1st_right)
local bar_rest_length=$[ COLUMNS - left_length - right_length -1 ]
## 1段目に水平線を引く
local prompt_1st_hr=${(l:${bar_rest_length}::-:)}
## PROMPT の設定
# @see Zshをかわいくする.zshrcの設定
# URL: http://qiita.com/kubosho_/items/c200680c26e509a4f41c
# 横幅等を調整.
local ps_status="[%j]%(?.%B%F{green}.%B%F{blue})%(?!(*'-')%b!(*;-;%)%b)%f "
local ps_mark="%(!,%B%F{magenta}#%f%b,%%)"
PROMPT="$prompt_1st_left$prompt_1st_hr$prompt_1st_right-"$'\n'"$ps_status$ps_mark "
PROMPT2='|%j]> '
SPROMPT="[%j]%B%F{red}%{$suggest%}(*'~'%)?<%b %U%r%u is correct? [n,y,a,e]:%f "
# 右プロンプト
RPROMPT="$ps_vcs_info"
}
precmd_functions+=update_prompt
なんて.

ですが, zip を展開して日本語のディレクトリなんかができてしまうと...

原因は wc -c なのですが, これを例えば wc -m なんてすると, 文字数のカウントは正しくなりますが, 表示自体はズレます. なので, ASCII では 1, それ以外では 2 を返すようにしたくなりました.
sed なんかでじたばたしていた訳ですが...
wc -m で文字数カウントできるのはありがたいが、マルチバイトの時には2、それ以外は1を返すようにできないだろうか…
— Youhei SASAKI (@uwabami) 2014, 10月 7
@uwabami sedは拡張正規表現ではないので ? はエスケープ不要だった。
sed 's/?/./g' input.txt |iconv -f UTF-8 -t US-ASCII//TRANSLIT |sed 's/?/../g' |wc -m
— シェルまおう(電子書籍セール沼出身) (@satoh_fumiyasu) 2014, 10月 7
@uwabami sed 's/[^\x01-\x7e]/../g' input.txt |wc -m で充分そうな気もしました。ご参考まで。
— シェルまおう(電子書籍セール沼出身) (@satoh_fumiyasu) 2014, 10月 7
なるほど. 勉強になります.
というわけで
# source: sh
function count_prompt_chars (){
# @see https://twitter.com/satoh_fumiyasu/status/519386124020482049
print -n -P -- "$1" | sed -e $'s/\e\[[0-9;]*m//g' | sed -e 's/[^\x01-\x7e]/aa/g' | wc -m | sed -e 's/ //g'
}
としてみました. これで幸せ.

という現実逃避ネタでした.
(追記) 早速ツッコミ頂きました.
@uwabami おつです。好みの問題ですが、sed は
sed -e $'s/\e\[[0-9;]*m//g' -e 's/[^\x01-\x7e]/aa/g'
のように一回でよさげ。
— シェルまおう(電子書籍セール沼出身) (@satoh_fumiyasu) 2014, 10月 7
おっしゃる通りです..._| ̄|○