2017年6月28日水曜日

AA表示用フォントはFirefoxとの相性に注意

Ubuntuで久々にFirefoxを起動してかなり驚いた事がありました。 …大手サイトさえまともに表示できない。文字が何も表示されない。 なんでだろうと思って色々確認してみるとフォント指定に影響を受けていました。

例えばYahoo!ではbodyに"MS PGothic","Hiragino Kaku Gothic Pro","ヒラギノ角ゴ Pro W3",Helvetica,Arial,sans-serif を指定しているのですが、実際の表示ではArial, IPAMonaPGothic, TakaoPGothic, Ubuntuを使っていました。

どうやらAAの表示確認に入れたIPAモナーフォントが悪さをしていたようです。 IPAモナーフォントはMS PGothicの代替として使われますが、 フォントが不足しているのでまともに表示できないという状況に陥っていたようです。 IPAモナーフォントを削除してフォントキャッシュを作成し直すと無事元通りに。

Chromeではこのような問題は起きなかったので、何らかの処理が異なるのだと思いますが、 MS PGothic互換のAA表示用フォントのインストールは注意深く行う必要がありそうです。

2017年6月27日火曜日

Linuxコマンドを補完するRubyスクリプト

Rubyによる補完スクリプトを作っていて気付きましたが、 rb-readlineはファイル補完はしてくれますが、Linuxコマンドの補完はしてくれないようです。 仕方ないので自作してみました。

まず、Linuxのコマンド一覧は以下で取得できるらしいです。これはMacでも使えます。 ただWindowsはコマンドを一覧表示できないようなので、cygwinの利用を想定するしかないかも知れません。
compgen -ac
このリストを利用したコマンド補完は以下で良さそうです。
require 'readline'  # gem install rb-readline

commands = `bash -ic "compgen -ac"`.split("\n")
Readline.completion_proc = proc {|s|
  last_command = Readline.line_buffer.split(/\s*[|&]\s*/)[-1]
  if last_command =~ /^.*\s+/
    Dir.glob('*').grep( /^#{Regexp.escape(s)}/ )
  else
    commands.grep( /^#{Regexp.escape(s)}/ )
  end
}
Readline.readline("Execute: ")
これでbashの補完とまったく同じ補完が実現できるようになりました。 以前作成したスクリプトも更新しておきました。

2017年6月19日月曜日

peco/fzfは外部コマンド実行用コマンドを作っておくと便利

前回に続きpecofzfネタです。 peco/fzfはとても便利なのですが、コマンドを実行するのは少し面倒だったりします。 具体的には、pecoでファイル一覧を表示、ファイルを選択してdirディレクトリにコピーする場合、以下のコマンドを打ちます。
cp `ls -a -p | peco` dir              # 方法1
ls -a -p | peco | xargs -J% cp % dir  # 方法2
「`」だったり細かなオプションが問題で、割と入力しにくいんですよね。 またxargsのオプション覚えるのも面倒だし、事前に何を実行するかわからない事も多い。 そこで以下のスクリプトをxコマンドとして登録してみる事にしました。
#!/usr/bin/env ruby

require 'readline'  # gem install rb-readline

# 入力を置換したいキーワードを登録
keyword = '@'


commands = `bash -ic "compgen -ac"`.split("\n")
if ARGV[0]
  dir = ARGV[0]
else
  dir = '.'
end

# list = `ls -a -p "#{dir}" | peco`
# list = `ls --color -a -p "#{dir}" | fzf --ansi -m`
list = `ls --color -a -p "#{dir}" | fzf --ansi -m --preview 'ls -C #{dir} --color=auto'`
puts list.split("\n").take(10).join(', ')

# Linuxコマンド補完をできるように
# https://marmooo.blogspot.jp/2017/06/linuxruby.html
Readline.completion_proc = proc {|s|
  last_command = Readline.line_buffer.split(/\s*[|$]\s*/)[-1]
  if last_command =~ /^.*\s+/
    Dir.glob(dir).grep( /^#{Regexp.escape(s)}/ )
  else
    commands.grep( /^#{Regexp.escape(s)}/ )
  end
}
cmd = Readline.readline("Execute: ")

exit if cmd.empty?
cmd = "#{cmd.strip} #{keyword}" if !cmd.include?(keyword)
cmd.gsub!(keyword, list.gsub("\n", ' '))
system("bash -ic '#{cmd}'")
xコマンドを実行すると、peco/fzfで何らかの処理したいファイルを選択した後、入力画面が出てきます。 そこで処理したいファイル郡@に対して任意の処理を指定する事ができます。 最初の例を実行する場合は以下のように入力すればOKです。 dir部分の入力にはシェルと同様の補完が効いています。
cp @ dir
備考ですが、置換キーワードに「@」を登録したのはファイル名に使われにくく入力しやすいからです。 また"bash -ic '#{cmd}'"という形式で実行しているのは、~/.bashrcの設定を引き継ぎながら実行するためです。 この設定がないと、例えばlsを実行した時などに--color=autoの設定が反映されません。

このxコマンドは前回紹介したcコマンドと組み合わせる事もできます。以下のようにcコマンドを実行後にxコマンドを起動するようにすれば、ディレクトリを移動した後にすかさずxコマンドでフィルタリングできます。ただ少しやり過ぎかなとも思うので、私はlsにしています。
function c { cd "`cdp $1`"; ls; }  # from
function c { cd "`cdp $1`"; x; }   # to
xコマンドを登録して、大量ファイルの処理が少し楽になりました。

peco/fzfをファイラにしたら予想以上に便利だった件

pecofzfという、コマンドライン上でインクリメンタル検索ができるツールがある事を知りました。 cdの替わりに使ってディレクトリ遷移できるファイラにすると便利そうと思っていたら、同じ事を考えている人はたくさん居たみたいです。 ただしっくり来るものがなかったのでスクリプトを書いてみました。 (最終更新: 2018-10-20)
#!/usr/bin/env ruby

pwd = ARGV[0]
pwd = Dir.pwd if !pwd
pwd = pwd[0..-2] if pwd[-1] =~ /[\\\/]/
pwd = '/' if pwd == ''

if !File.exist?(pwd) || !File.directory?(pwd)
  puts "Usage: #{__FILE__} [dir]"
  exit
end

if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
  symbol = '\\'
else
  symbol = '/'
end

begin
  if File.directory?(pwd)
    # # プレビューなし
    # # dir = `(echo '../'; echo #{pwd}/; ls -F #{pwd}) | grep / | peco --on-cancel error --select-1`.strip
    # dir = `(echo '../'; echo #{pwd}/; ls -F #{pwd}) | grep / | fzf --ansi --select-1`.strip
    # プレビュー付
    view = "echo #{pwd}#{symbol}{}; echo; ls --color #{pwd}#{symbol}{}"
    dir = `(echo '../'; echo './'; ls -F #{pwd}) | grep / | fzf --header=#{pwd} --reverse --preview '#{view}' --select-1`.strip
    case dir
    when "", "./"
    when "../"
      pwd = File.dirname(pwd)
    else
      pwd = "#{pwd}#{symbol}#{dir.chop}"
    end
  else
    pwd = File.dirname(pwd)
  end
end while $?.exitstatus == 0
puts pwd
上記をcdpなどという名前でパスの通った場所に保存し、~/.bashrcに以下のような短縮コマンドを登録します。 私はcコマンドとして登録しました。上下どちらの形式でも大丈夫です。
alias c='cd "`cdp ${1}`"; ls'
function c { cd "`cdp $1`"; ls; }

cコマンドの使い方: cコマンドを叩くとpeco/fzfが起動します。 引数にディレクトリを指定すると、そのディレクトリから検索を開始します。何も指定しない場合はカレントディレクトリからです。 ディレクトリを選択するとpeco/fzfが起動し直すので、再帰的に辿れます。 表示中のディレクトリでシェルに戻りたくなったらESCキーでpeco/fzfを終了します(標準設定なら)。

それなりに拡張しやすく作れたと思うので、表示形式を弄ったり、ブックマークを追加したり、動作を変更したり、ご自由に。 pecoとfzfはどちらを使っても良いのですが、fzf --ansiを使うとファイルとディレクトリの色分けができて視認性が良かったり、プレビューができて便利なため、私はfzfを使っています。 Windowsではls, sedコマンドの用意が必要です。

ディレクトリ移動にcd ..を打つ必要が完全になくなったので、予想以上に便利かも。 外部コマンド実行用コマンドを作っておくとさらに便利なので、こちらもおすすめ。

2017年6月3日土曜日

便利だけど忘れやすいLinuxコマンドまとめ

毎回忘れて検索している気がするので、探しやすいようにメモしておきます。

見辛いCSVを整形してターミナルで閲覧

# 他にも色々あるけどインストールが楽 (go get github.com/Konboi/csviewer)
csviewer -p test.csv
cat test.csv | csviewer

見辛いHTMLを整形してターミナルで閲覧

# xmllintは色々面倒なのでtidyを利用 (apt-get install tidy)
tidy -i -utf8 < test.html | vim -

見辛いJSONを整形してターミナルで閲覧

# apt-get install jq
jq -C . test.json | less -R

文字列を含む箇所を前後5文字を含めて抽出

# 改行のない最小化ファイルの検索に便利
grep -o -E '.{,5}foobar.{,5}' test.txt

ディレクトリの使用量を見やすい形で表示

du -sh dir

時刻/日付をUNIX TIMEに変換

date +%s                            # 現在時刻
date +%s --date `date -Idate`       # 今日の日付
date +%s --date 2017-06-01          # 指定した日付
date +%s --date "2017-06-01 12:00"  # 指定した時刻

UNIX TIMEを時刻に変換

date --date @1496242800

プロセスの表示 (ps)

# chromeなどの表示がうざいので折り返しなしで表示
ps aux --sort -pcpu | less -S  # CPU使用量順
ps aux --sort -rss  | less -S  # メモリ使用順

プロセスの表示 (top)

top  # まずは平凡に起動
# Shift+PキーでCPU使用量順
# Shift+Mキーでメモリ使用順順
# Shift+Tキーで実行時間順

プロセスをcgroupのtreeで表示

systemd-cgls

プロセスの停止

ps aux | fzf | awk '{ print \$2 }' | xargs kill -9
ps aux | fzf --preview 'echo {}' --preview-window=bottom:wrap | awk '{ print \$2 }' | xargs kill -9  # おすすめ

OSのバージョン表示

uname -a                 # カーネルのバージョン
cat /etc/lsb-release     # Ubuntuのバージョン
cat /etc/debian_version  # Debianのバージョン

ユーザ設定を読み込んでコマンドを実行

bash -lc 'command'  # ~/.bash_profileを読み込んで実行
bash -ic 'command'  # ~/.bashrcを読み込んで実行

サーバ上のXを利用 (SSH上で画像を開く等)

# サーバ側でSSHのX11 Forwardingを有効にする事前準備は必要
ssh -XC user@host    # X11転送を有効にしてSSHクライアントを起動
# display image.png  # ログイン後はXが必要なコマンドも使える

気軽にHTTPサーバを立てる

python -m SimpleHTTPServer [port]  # python2
python -m http.server [port]       # python3
ruby -run -e httpd . -p [port]     # ruby1.9.3-

ユーティリティコマンド

bc                  # 電卓
cal                 # カレンダー
curl wttr.in/tokyo  # 天気
time hoge           # hogeの実行時間を計測

関連付けに従って開く

gnome-open https://www.google.co.jp/
xdg-open test.txt

文字化けするzipファイルを展開

unzip -O cp932 test.zip

unzipで複数ファイルを一括展開

unzip '*.zip'  # シングルコーテーションなどで囲うのが重要
unzip \*.zip  # これでも動くけど上のほうが覚えやすいかな?

最近は使われない圧縮形式の展開

# x付ければ大抵なんとかなる
# 最近はatool (apt-get install atool) を使ったほうが楽かも
lha x foo.lzh    # LZH (apt-get install lha)
unrar x foo.rar  # RAR (apt-get install unrar)

コマンド終了時にメール送信

# メールコマンドのインストールと設定が必要
# 最近はTwitterに通知するスクリプトを作ったほうが気楽かも知れない
command && echo [content] | mail -s [subject] [to@example.com]

Git関連

git config --global core.quotepath false  # 日本語ファイルの文字化け回避

エイリアスに登録

alias ps-cpu="ps aux --sort -pcpu | less -S"
alias ps-mem="ps aux --sort -rss | less -S"
alias osversion="cat /etc/lsb-release"
alias unixtime="date +%s"
alias unixdate="date +%s --date `date -Idate`"
alias weather="curl wttr.in/tokyo"
alias pskill="ps aux | fzf --preview 'echo {}' --preview-window=bottom:wrap | awk '{ print \$2 }' | xargs kill -9"
頻繁に使うものや特に忘れやすいものはエイリアスに登録しておくと楽です。 ~/.bashrcに上記を登録しても良いかも知れません。

2017年6月1日木曜日

グループで最大のレコードを取得するSQL

同一グループの中で最大のレコードを取得するSQLはたまに必要になります。 このような時はGROUP BYを使ってグループをまとめながら最大値を求めてその結果を…までは良いのですが、そこから2種類の方法があるので書いておきます。 key,value, year, month, timeを持つテーブルからkey=xに関してyearごとにmax(time)を持つvalueを求める場合は以下のようになります。

1. INNER JOINを使う

explain query plan
select distinct(s.year),value from [table] as s inner join (
  select year,max(time) as max_time from [table] where key=[x] group by year)
  as m on s.year = m.year and s.time = m.max_time and key=[x];

1|0|0|SEARCH TABLE [table] USING COVERING INDEX [table_idx] (key=?)
0|0|0|SEARCH TABLE [table] AS s USING INDEX [table_idx] (key=?)
0|1|1|SCAN SUBQUERY 1 AS m

explainで実行計画も載せてみましたが、まずsの条件で検索をしてから、mの検索結果とjoinして表示しているようです。

2. サブクエリを条件式に使う

explain query plan
select distinct(year),value from [table] as s where key=[x] and time=(
  select max(time) from [table] as m where s.year = m.year and key=[x]);

0|0|0|SEARCH TABLE [table] AS s USING INDEX [table_idx] (key=?)
0|0|0|EXECUTE CORRELATED SCALAR SUBQUERY 1
1|0|0|SEARCH TABLE [table] AS m USING COVERING INDEX [table_idx] (key=?)
0|0|0|USE TEMP B-TREE FOR DISTINCT

こちらではsの条件で検索してから、サブクエリとしてmを発行し、mの検索結果を一時的に構築したB-Treeを使いながらdistinctしてるみたいです。

explainの結果を見る限りでは1のほうが早そうです。 参考になるかはわかりませんが、実際に運用しているスクリプトの実行時間は1=25分、2=33分でした。 EXISTSを使う方法もあるみたいですね。