f

アーカイブ

2016-06-13

Ubuntu 16.04でmozcが起動できない問題の対処

2016-06-04にUbuntu 16.04にアップグレードして1周間程度経過した。なぜかわからないが,急にIMEのmozcが起動しなくなり,日本語入力ができなくなってしまい困ってしまった。どうにか解決できたのでそのときの対処方法を記す。

PCにログインすればmozcは自動起動しているが,コマンドでも起動できる。mozcが動作しなくなったので以下のコマンドでmozcを起動させようとしたらエラーが出てしまった。

fcitx
(ERROR-7517 /build/fcitx-J2yftF/fcitx-4.2.9.1/src/lib/fcitx/instance.c:440)     + Exiting.   

半角/全角キーなどを押下してもIMEが切り替わらない。仕方がないので,一旦mozcを再インストールすることにした。以下のコマンドで再インストールした。

sudo aptitude reinstall fcitx-mozc

しかし,再インストールしても結果は変わらなかった。いろいろと操作をしていたら解決できた。

右上のタスクトレイのキーボードアイコンをクリック,またはターミナルから以下のコマンドを入力

fcitx-configtool

[Configure]>[+]>[Mozc]を選択

何らかの原因で,Input Method一覧からMozcが消えてしまったのが原因のようだった。ひとまず解決してよかった。

2016-06-11

How to exclude binary file or directory in Vim internal/external grep

テキストエディターVimでは,起動中に内部grep(:vimgrep)と外部grep(:grep)により複数のファイルから文字列を検索できる。この検索からバイナリーファイルや特定のディレクトリを除外する方法をまとめる。

大量のファイルから特定文字列を探していると,検索に時間がかかり,不要なマッチがあると重要なマッチが埋もれてしまうので,無関係のファイルは検索対象から除外したい。特に,バイナリーファイルや,.git.svnなどのバージョン管理のためのディレクトリは,文字列を検索する文字列を検索する意味がないので検索対象から除外したい。

調べてみたが,あまりまとまった情報がなかったのでまとめてみた。設定一覧は最後のまとめの節に掲載している。

目次

  1. 内部grepと外部grep
  2. 内部grep(:vimgrep)
    1. 検索対象外ファイル・ディレクトリの設定
    2. wildignore設定時の注意点
    3. Vim script構文を使ったスマートな設定方法
    4. vimgrepで気づいたこと
  3. 外部grep(:grep)
    1. findstr(Windows)の設定
    2. GNU grepコマンドのオプション
    3. Unixでのgrepprgの既定値の/dev/null
    4. Vimの外部grep(:grep)とシェルのgrepコマンドのオプションの共通化
  4. まとめ
内部grepと外部grep

まず,Vimの内部grep(:vimgrep)と外部grep(:grep)について簡単に説明する。

内部grep(internal grep,:vimgrep)とは,Vimの内部コマンドによるgrep検索のことである。利点と欠点は以下となる。

利点
  • Vimで直接ファイルを一つずつ開いて検索をかけるため,Vimがもつ文字エンコーディングの自動判別や正規表現が使える。
  • Vim標準機能であるため,どのOSでも利用可能で,操作や結果も同じ。
  • 検索後の問い合わせがなく検索結果にシームレスにアクセスできる。
欠点
  • ファイル数が増えると検索に時間がかかる。

外部grep(external grep,:grep)とは,Vimから外部のコマンドを使用するgrep検索のことである。既定ではUnix系OSではgrepコマンドが使われ,Windowsだとfindstrコマンドが検索に使われる。利点と欠点は以下となる。

利点
  • 外部の専用検索コマンドを使うことで,ファイル数が多くても検索速度が早い。
欠点
  • 外部コマンドに依存するため,OSによりコマンドや操作・結果に若干の違いが生じる。
  • 文字エンコーディングの自動判定などの機能が内部grepよりは貧弱。
  • 検索実行後,Press Enter or type commmand to continue問い合わせメッセージが表示されるので,頻繁に検索を実行する場合煩わしい。

使ってみた印象だと,検索対象ファイル数が数十ファイルを越えるなら,確実に外部grepを使ったほうがよい。ただ,外部grepを使うと,検索後に検索結果一覧を表示してメッセージが表示される。頻繁に検索を繰り返す場合,これが煩わしく感じるかもしれないので,検索対象ファイル数が少なければ,内部grepでもよい。

Vimからgrepを使うことで,Vimを離れることなく検索したファイルにアクセスができる。QuickFix-windowと組み合わせることで,大量のファイルから該当文字列を含むファイルだけをつぎつぎとアクセスでき,とても作業効率がよくなる。

内部grep(:vimgrep)
検索対象外ファイル・ディレクトリの設定

Vimの内部grep(:vimgrep)でバイナリーファイルやディレクトリを検索対象外にするには,wildignoreで設定する(参照:5.1 Vimの内部grepの使い方 - quickfix - Vim日本語ドキュメント)。

設定はVimの正規表現の形式で設定する。例えば,ファイルを対象外にするには以下の通りに拡張子で指定したり,ファイル名で指定する。

set wildignore=*.dll,*.exe,tags

:vimgrepではファイルを自動的に判別して,バイナリーファイルを検索対象外にすることはできないようなので,考えられるバイナリーファイルの拡張子を複数指定して検索対象外にする。

ディレクトリを対象外にするには,vimやbash,zshで特有の正規表現である**を使い,ディレクトリ配下の全ファイルを無視するように指定する(参考:vim - How to exclude file patterns in vimgrep? - Stack Overflow)。

set wildignore+=obj/**,.git/**,.svn/**
wildignore設定時の注意点

wildignoreで値を設定するときは,help: wildignoreで以下のとおりにすることが推奨されている。

リストにパターンを追加するときにはコマンド |:set+=|、リストからパターンを除くときにはコマンド |:set-=| を使うのがよい。こうすると将来のバージョンで異なった既定値が使われるようになったときに、問題が起きるのを防げる。

wildignore - options - Vim日本語ドキュメント

上記の通り,設定するときは既定の設定を壊さないように,:set+=で追加し,:set-=で解除するのがよいだろう。

併せて,「内部grepでバイナリファイルを対象外にする - 座敷牢日誌」で言及されているように,wildignoreの設定は,Vimのgrep以外にも影響を与えるので,そのまま設定するのは危険だ。

前述の記事でなされている通り,Vimでgrep検索するときに発生するイベントであるquickfixをautocmdで捕捉する。:vimgrepを使うときだけwildignoreで検索対象外ファイルをsetlocalで追加設定し,grep検索が終了したらすぐにwildignoreで追加した項目を除外するのがよいだろう。

例えば,以下のように設定することになるだろう。

autocmd QuickFixCmdPre *
\  setlocal wildignore+=*.o,*.obj,*.exe,*.dll,*.bin,*.so,*.a,*.out,*.jar,*.pak
\| setlocal wildignore+=*.zip,*gz,*.xz,*.bz2,*.7z,*.lha,*.deb,*.rpm
\| setlocal wildignore+=*.pdf,*.png,*.jpg,*.gif,*.bmp,*.doc*,*.xls*,*.ppt*

autocmd QuickFixCmdPost *
\  setlocal wildignore-=*.o,*.obj,*.exe,*.dll,*.bin,*.so,*.a,*.out,*.jar,*.pak
\| setlocal wildignore-=*.zip,*gz,*.xz,*.bz2,*.7z,*.lha,*.deb,*.rpm
\| setlocal wildignore-=*.pdf,*.png,*.jpg,*.gif,*.bmp,*.doc*,*.xls*,*.ppt*
Vim script構文を使ったスマートな設定方法

上記で一応設定できたが,この書き方だと,Vimのgrepでの検索前後であるQuickFixCmdPreQuickFixCmdPostとで,2回wildignoreを設定する必要がある。片方への書き忘れがあったり,検索対象外ファイルが増えてきたら行数が増えるので,賢いやり方ではないだろう。Vim scriptの構文を少し使えばスマートに設定できる。

以下の通りに,スクリプトローカル変数s:ignore_listに検索対象外ファイルを記述し,executeで文字列を実行してやれば検索対象外ファイルの記述の重複がなくなる。それに加えて,古いVimではwildignroeが使えないこともあるので,Vimのwildignore機能が有効なときだけwildignoreを設定するようにすれば完璧だろう。

"" vim grep
""" ignored files in vimgrep
let s:ignore_list  = ',.git/**,.svn/**,obj/**'
let s:ignore_list .= ',tags,GTAGS,GRTAGS,GPATH'
let s:ignore_list .= ',*.o,*.obj,*.exe,*.dll,*.bin,*.so,*.a,*.out,*.jar,*.pak'
let s:ignore_list .= ',*.zip,*gz,*.xz,*.bz2,*.7z,*.lha,*.lzh,*.deb,*.rpm,*.iso'
let s:ignore_list .= ',*.pdf,*.png,*.jp*,*.gif,*.bmp,*.mp*'
let s:ignore_list .= ',*.od*,*.doc*,*.xls*,*.ppt*'

if exists('+wildignore')
  autocmd QuickFixCmdPre  * execute 'setlocal wildignore+=' . s:ignore_list
  autocmd QuickFixCmdPost * execute 'setlocal wildignore-=' . s:ignore_list
endif
vimgrepで気づいたこと

Vimの内部grepである:vimgrepを試していて気づいたことがあるので参考までに記載しておく。

Linuxのgrepコマンドでは,とくに指定なければ,検索対象ファイルに隠しファイル.*を含めて再帰的に検索すると,1階層上を示す..が検索対象ディレクトリにヒットしてしまい,ファイルシステム全体を検索することになって危険だった。

grep -r "pattern" .* *

これを防ぐには,正規表現で..を除外させるか,オプション--exclude-dir..を除外すれば回避できた(参考:My Future Sight for Past: How to match grep command for all files including hidden files)。

grep -r "pattern" .[!.]* *
grep -r "pattern" .* * --exlucde-dir=..

しかし,:vimgrepでは以下の通りに検索してもヒットするのは1階層上のディレクトリ配下までであり,ファイルシステム全体を検索してしまうようなことはなかった。:vimgrepの方がgrepコマンドより安全にできているのかもしれない。

:vimgrep 'pattern' .**/
外部grep(:grep)

外部grep(:grep)でバイナリーファイルやディレクトリを検索対象外にするには,grepprgを設定する。grepprgは外部grepを使うときに実際に実行されるコマンドとオプションとなっている(参考:grepprg - options - Vim日本語ドキュメント)。

grepprgの既定値はOSごとに以下となっている。

grepprgの既定値
OS既定値
Unix"grep -n $* /dev/null"
Win32"findstr /n""grep -n"

このように,OSごとに使われるコマンドが異なるので,設定を分岐させる必要がある。grepコマンドはPOSIXでも規定されており,LinuxやMac,WindowsにおいてもCygwinやMSYS2など使える可能性が高い。そこで,grepコマンドが存在すれば,grepコマンドのオプションとして設定し,そうでなければWindowsとみなしてfindstrコマンドのオプションとして設定するのがよいだろう。

if executable('grep')
  set grepprg=grep\ -n
else
  set grepprg=findstr\ /n\ /p
endif
findstr(Windows)の設定

Windowsでは標準で外部grepコマンドにfindstrコマンドが使われる。

このコマンドはWindowsにおけるgrepコマンドに相当するもので,正規表現を使ってファイル内の文字列を検索できる。しかし,grepと異なり検索対象外のファイルやディレクトリやを設定することはできない。

しかし,/pオプションをつけることで,バイナリファイルを検索対象外ファイルに指定できる。したがって,grepprgには以下のとおりに設定するのが最善だろう。

set grepprg=findstr\ /n\ /p

その他,findstrコマンドではディレクトリを再帰的に検索するのに/sオプションを使うなど,grepとはコマンドの体系が異なっている。ヘルプを確認して使い方を習熟しておく必要があるだろう。

個人的には,findstrコマンドを使うくらいなら,コマンド体系を統一できる内部grepである:vimgrepコマンドを優先したい。

GNU grepコマンドのオプション

Windows以外であれば,基本的には外部grep(:grep)にはgrepコマンドが使われる。POSIXで規定されているgrepには検索対象外のファイルやディレクトリを指定できないが,多くの環境で使われていると思われるGNU grepであれば,オプションが用意されている(参照:Man page of GREP)。

GNU grepで検索対象外に関するオプション
オプション説明
--binary-files=without-match, -Iバイナリーファイルを無視する。
--exclude-dir指定したディレクトリを無視する。
--exclude指定したファイルを無視する。ファイルはパスなしのファイル名であるベースネームのみで判定する。

--exclude-dirと--exludeではマッチングにglobまたはワイルドカードとして,*?[...]が使える。また,一度に複数の項目を設定するには,値を波括弧{}で囲み,項目はカンマ,で区切る。または,--exclude-dirなどのオプションを複数回繰り返して指定してもよい。ただし,波括弧で囲めるのは値が複数個あるときだけ。1個しかないのに波括弧を使うと,その設定は無視される。値はでも有効なので,をつけよう。なお,波括弧を引用符で囲むと波括弧自体が文字列として解釈されるので注意しよう。

grep "pattern" * --exclude-dir={..,.git}
grep "pattern" * --exclude-dir=.. --exclude-dir=.git
grep "pattern" * --exclude-dir={..} # これは無視される
grep "pattern" * --exclude-dir={,..} # これはOK
grep "pattern" * --exclude-dir="{,..}" # これはNG

例えば,以下のように設定すればよいだろう。

set grepprg=grep\ -n\ -I\ --exlucde-dir={..,.git,.svn,.obj} --exclude={tags,GTAGS,RTAGS,GPATH}

なお,--exclude-dirオプションはGNU grep 2.5.2から導入されたらしく,それ以前のgrepでは--excludeを使えば代用できるとの情報を見かけた(参考:grepで.svnディレクトリを除外して再帰検索 - 日々の報告書)。しかし,--exclude=*.svnなどとしてもディレクトリを除外できなかったので,2.5.2以降のバージョンでは--excludeオプションの仕様が変わったのかもしれない。

Unixでのgrepprgの既定値の/dev/null

Unixでのgrepprgの既定値は,以下となっている(参考:grepprg - options - Vim日本語ドキュメント)。

grepprg=grep\ -n\ $*\ /dev/null

grepでは検索対象を指定しなければ,標準入力から読み込まれるとみなされ動作が停止してかのようにみえてしまう。Unixだけ後ろに$*\ /dev/nullが付いているのは,検索対象の指定を忘れたときの予防のためだろう。検索対象に/dev/nullが指定されるようにすることで,標準入力からの入力を待ち続けることを回避できる。

しかし,この指定があると通常のgrepと挙動が変わってくる。それは,-rオプションで再帰的にディレクトリを指定するときだ。

echo "backup" > a.sh
## これはヒット
grep -r --include=*.sh backup

## vim
vim
## これはヒットしない
:grep -r --include=*.sh backup

grepprg=grep\ -n\ $*\ /dev/nullとなっていれば,ディレクトリに/dev/nullが指定されたとみなされ,現在ディレクトリを探しにいかない(参考: Differences when using grep in terminal and :grep in vim - Stack Overflow)。

この問題を避けるためにも,grepprgの既定値から$*\ /dev/nullを削除した。また,次の節で説明するが:grepの実行後のQuickFix-Windowにおいて,ステータスラインに表示されるスペースを省略するためにも有効だ。
Vimの外部grep(:grep)とシェルのgrepコマンドのオプションの共通化

grepprgの値としてgrepコマンドを使う時の設定例として以下を掲載した。

set grepprg=grep\ -n\ -I\ --exlucde-dir={..,.git,.svn,.obj} --exclude={tags,GTAGS,RTAGS,GPATH}

これには以下2点の問題がある。

  1. :grep実行後のQuickFix-Windowでのステータスラインの逼迫。
  2. 通常のgrepコマンドとVimの外部grepとで設定の重複。

1.の:grep実行後のステータスラインが逼迫されることについて説明する。

:grepを実行後のQuickFix-Windowでは,検索コマンドがステータスラインに表示される。しかし,grepprgが長いと画面に入りきらず,一部が省略されて表示されてしまう。例えば以下の画像のように先頭部分が>で省略されてしまっている。見た感じが悪いのでできれば回避したい。

2.について説明する。基本的には検索対象外ファイルの設定は通常のgrepコマンドでもVimの:grepでも同じ設定になるはずだ。片方の設定忘れなど保守が悪いので,共通化したい。

この2点の問題を解決する方法がある。それは,以下だ。

grepにオプションを追加したaliasを設定しておいて,vimから使えるようにする。

こうすれば,Vimのgrepprgで設定する必要がなくなりbashなどと設定を共通化できる。また,ステータスラインの表示スペースも節約できる。

Vimからシェルのaliasを使う方法は「My Future Sight for Past: Use of alias for Vim external command」に詳しくまとめている。

例えば,~/.bashenvファイル(~/.bashrcでも可)に共通設定を記入を記入しておき,そのファイルをVim起動中に環境変数BASH_ENVに設定する。

## .bashenv

## grep 2.21 later, deprecated GREP_OPTIONS environmental variable GREP_OPTIONS="--color=auto -I --exclude-dir={.,..,.git,.svn,obj}" GREP_OPTIONS="${GREP_OPTIONS} --exclude={tags,GTAGS,GRTAGS,GPATH}" alias grep="grep ${GREP_OPTIONS}"
## .vimrc

if executable('grep') "" シェルと共通化させない場合の設定
" set grepprg=grep\ -n\ -I\ --exlucde-dir={..,.git,.svn,.obj} --exclude={tags,GTAGS,GRTAGS,GPATH}
set grepprg=grep\ -n
else
"" for Windows
set grepprg=findstr\ /n\ /p endif

"" Enable alias for external command if filereadable(glob('~/.bashenv')) | let $BASH_ENV=expand('~/.bashenv') | endif

こうすることで,grepのオプションをVimとシェルとで共通化でき,ステータスラインのスペースも確保できる。

まとめ

Vimの内部grep(:vimgrep)と外部grep(:grep)でバイナリーファイルや特定のディレクトリを検索対象外にする方法をまとめた。これでVimでファイルを検索するのが快適になるだろう。

最後に,今回の設定をまとめて掲載する。

## .vimrc

"" vim grep """ ignored files in vimgrep let s:ignore_list = ',.git/**,.svn/**,obj/**' let s:ignore_list .= ',tags,GTAGS,GRTAGS,GPATH' let s:ignore_list .= ',*.o,*.obj,*.exe,*.dll,*.bin,*.so,*.a,*.out,*.jar,*.pak' let s:ignore_list .= ',*.zip,*gz,*.xz,*.bz2,*.7z,*.lha,*.lzh,*.deb,*.rpm,*.iso' let s:ignore_list .= ',*.pdf,*.png,*.jp*,*.gif,*.bmp,*.mp*' let s:ignore_list .= ',*.od*,*.doc*,*.xls*,*.ppt*' if exists('+wildignore') autocmd QuickFixCmdPre * execute 'setlocal wildignore+=' . s:ignore_list autocmd QuickFixCmdPost * execute 'setlocal wildignore-=' . s:ignore_list endif

if executable('grep') "" シェルと共通化させない場合の設定
" set grepprg=grep\ -n\ -I\ --exlucde-dir={..,.git,.svn,.obj} --exclude={tags,GTAGS,GRTAGS,GPATH}
set grepprg=grep\ -n
else
"" for Windows
set grepprg=findstr\ /n\ /p endif

"" Enable alias for external command if filereadable(glob('~/.bashenv')) | let $BASH_ENV=expand('~/.bashenv') | endif
## .bashenv

## grep 2.21 later, deprecated GREP_OPTIONS environmental variable GREP_OPTIONS="--color=auto -I --exclude-dir={.,..,.git,.svn,obj}" GREP_OPTIONS="${GREP_OPTIONS} --exclude={tags,GTAGS,GRTAGS,GPATH}" alias grep="grep ${GREP_OPTIONS}"

How to use ShellScript file arguments in user defined function

シェルスクリプトで,ユーザー関数でスクリプトファイルの引数を使う方法がわかったのでメモしておく。端的に結論を書くと,ユーザー関数の引数に$@を渡せばよい。

シェルスクリプトの引数はスクリプトでよく使われるパラメーターだろう。例えば,シェルスクリプトに渡す引数の既定値を設定したり,引数の数や内容を確認して,ユーザーに問い合わせるような処理に使われるだろう。

こうしたスクリプトファイルの引数を使った定型処理をユーザー関数にして,他で使いまわしたり管理しやすくしたい。

スクリプトファイルの引数は,位置パラメーターである$$1$2,…,$n$@$*などで参照できる。しかし,シェルスクリプトのユーザー関数内ではこれらの位置パラメーターはユーザー関数の引数として解釈されてしまう。そのため,引数が何個渡されるかわからないときに対応しにくい。

一応,以下のようにスクリプトファイルの引数をユーザー関数の引数に渡して実行することは可能だ。

echo << EOF > arg_parse.sh
#!/bin/bash
## \file arg_parse.sh

arg_parse(){
[ "$#" -gt 4 ] && echo "arguments $# is too much!" && return 1
echo $1 $2 $3
return
}

arg_parse $1 $2 $3 $4
EOF
./arg_parse.sh one two three four
arguments 4 is too much!

しかし,これだと引数の個数を決め打ちで指定する必要があり,引数の個数が何個くるかわからないときの処理が難しく,見た感じもよくない。

この解決策としては,ユーザー関数の引数に$@を渡せばよい

echo << EOF > arg_parse.sh
#!/bin/bash
## \file arg_parse.sh

arg_parse(){
[ "$#" -gt 4 ] && echo "arguments $# is too much!" && return 1
echo $1 $2 $3
return
}

arg_parse "$@" # <- here
EOF
./arg_parse.sh one two three four
arguments 4 is too much!

$@は,スクリプトファイルに渡された全ての引数が代入されている。ユーザー関数の引数にスクリプトファイルの全ての引数を渡すことで,スクリプトファイルの引数が何個であろうともユーザー関数で取り扱うことができる。

$@と類似のパラメーターとして,$*がある。こちらも全ての引数が格納されている。こちらは$@とは異なり各引数の区切り文字がIFS変数となっている。$*IFS変数と併用して引数をカンマ区切りにしたり加工するのに便利だが,通常の利用であれば$@で十分だろう。

これで,スクリプトファイルの引数が何個であろうと,ユーザー関数でもスクリプトファイルの引数を処理できるようになった。今後はシェルスクリプトでのスクリプトファイルの引数はユーザー関数で処理してやろう。

2016-06-08

Solution for "Gtk-WARNING **: cannot open display:" on Ubuntu 16.04

先日Ubuntu 14.04からUbuntu 16.04にアップグレードした。しかし,Gnomeのアプリを起動しようとすると起動できなかった。解決できたのでメモしておく。

結論を先に書くと,環境変数DISPLAYは自分で設定するのではなく,~/.bashrcなどで以下のとおりに設定されていないときだけ設定すればよい。

[ "${DISPLAY}" ] || export DISPLAY=:0

Trouble

例えば,テキストエディタであるgeditをターミナルから起動しようとすると以下のエラーが出た。

geditGtk-Message: Failed to load module "unity-gtk-module"
No protocol specified

** (gedit:19866): WARNING **: Could not open X display
No protocol specified

(gedit:19866): Gtk-WARNING **: cannot open display: :0.0

環境変数DISPLAYの値は:0.0としている。以下のコマンドでDISPLAY変数の値を一時的に無効化してgeditを起動するとエラー内容が若干変化した。

DISPLAY="" geditGtk-Message: Failed to load module "unity-gtk-module"

(gedit:20071): Gtk-WARNING **: cannot open display:

EvinceやGIMPなどのGTK+をベースとした優れたGnomeのアプリを端末から使えないのは不便なので,解決策を調べた。

Solution

まず,最初に両方のエラーに表示されていた以下のメッセージに注目して調べてみた。

Gtk-Message: Failed to load module "unity-gtk-module"

すると,最初に以下のサイトにたどり着いた。

Ubuntu 16.04, D1000 Docking Station, displaylink-driver-1.1.62 - DisplayLink Forum

このサイトで回答されている通りに,以下のコマンドを入力してみたのだけど,解決しなかった

sudo apt-get install --reinstall ubuntu-desktop

続いて,cannot open display:のメッセージからX Window System関係の問題かなと思って調べてみたところ,以下のサイトにたどり着いた。

command line - Can't launch graphical apps from terminal after updating to 15.10 - Ask Ubuntu

このサイトでsssemilが回答しているように,DISPLAY=:1のとおりに環境変数に値を設定して起動してみる。

DISPLAY=:1 gedit

起動に時間がかかったものの,うまく起動できた

Cause

今までよくわからないまま,~/.bashrcなどで環境変数DISPLAYには以下のとおりに:0.0を設定していた。

export DISPLAY=:0.0

これがまずかったようだ。ちょっと調べてみた。man Xコマンドかこのサイトでヘルプを確認できる。Display Namesセクションを一部翻訳しながらまとめてみる。

DISPLAY環境変数の値の書式は以下のとおりになっている。

DISPLAY=<hostname>:<displaynumber>.<screennumber>

この情報は,デフォルトでサーバーとスクリーンに接続する方法を決めるために,アプリケーションで使われる。

項目説明既定
hostnameディスプレイが物理的に接続されているマシン名を指定する。指定しなければ,サーバーと最も効率のよいマシン名が使われる。自動
displaynumberディスプレイとは,キーボードやマウスポインターを共有するモニターを指す。多くのPCでは1PCにつき1モニターを持つ。マルチユーザーシステムでは,ログインユーザーごとにディスプレイが存在し,それぞれ割り当てられる。混乱を避けるため,Xサーバーの起動時に,各ディスプレイに0から始まるディスプレイ番号が割り当てられる。ディスプレイ番号はいつもディスプレイ名で与えられないといけない。-
screennumberキーボードとマウスポインターを共有するディスプレイ。Xサーバーの起動時に,各モニターは自分のウィンドウを持ち,各スクリーンは0から始まるスクリーン番号を割り当てられる。番号を指定しなければ0が使われる。0

<hostname>と<screennumber>は既定値があるので,基本的には<displaynumber>だけ自分で設定すればよさそうだ。また,以下で言及があるようにPOSIX準拠である大半のLinuxではDISPLAY環境変数の既定値が存在する模様。

On POSIX systems, the default display name is stored in your DISPLAY environment variable. This variable is set automatically by the xterm terminal emulator. However, when you log into another machine on a network, you may need to set DISPLAY by hand to point to your display.

このことから,基本的には自分で設定する必要はないだろう。しかし,CygwinやMSYS2などでは環境変数DISPLAYの値が設定されていない環境もありえるため,エラーを回避する必要がある。そこで,DISPLAY環境変数が存在すればそのままにし,存在しなければ自分で設定すればよいだろう。設定値についても,基本的には既定値を使えばよい。

これらのことを考慮して,以下の設定を~/.bashrcなどに書いておけば今後も大丈夫だろう。

[ "${DISPLAY}" ] || export DISPLAY=:0

Solution for gcc "x86_64-linux-gnu/crti.o: unrecognized relocation (0x2a) in section `.init'" on Ubuntu 16.04

先日Ubuntuを14.04から16.04に更新した。すると,gccでC言語のソースコードをコンパイルしようとすると以下のエラーがでるようになってしまった。

cat << EOF > hi.c
#include <stdio.h> int main(void){ puts("HI"); return 0; }
EOF
gcc -o hi.{exe,c}
/home/senooken/local/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o: unrecognized relocation (0x2a) in section `.init' /home/senooken/local/bin/ld: final link failed: Bad value collect2: error: ld returned 1 exit status

原因を調べていると以下のページにたどり着いた。

GNAT GPL 2015 and Ubuntu 16.04 => ld: /usr/lib/x86_64-linux-gnu/crti.o: unrecognized relocation - Google グループ

この投稿でPascal Obryの回答を参考にすると,どうやらコンパイルに使ったgccとリンカldのバージョンなどの整合性が取れていないことが原因のようだ。

実際に以下のコマンドでldがどこにあるか確認した。

which ld/home/senooken/local/bin/ld

どうやら,自分でローカル環境にインストールしていたldを使っていたようだ。

確認してみると,ローカルでbinutils-2.25をインストールしていて,gccでのリンク時にこのldが使われていたのが原因だった。binutils-2.25をアンインストールしたら解決した。

gccはOSのシステムと密接になっているので,注意しよう。

2016-06-06

How to upgrade Ubuntu 16.04 from 14.04

2016-04-28にUbuntu 16.04(コードネームはXenial Xerus)がリリースされた。2年前に自分のデスクトップPCに初めてインストールしたLinuxであるUbuntu 14.04から16.04に更新した。いくつか問題があったので手順を記しておく。 

結論からいうと,14.04から16.04へのアップグレードは2016-06-21まで待ったほうがいい。Ubuntuでは2年毎に長期サポート版(LTS:Long Term Support)がリリースされている。最初のリリースから3ヶ月後の16.04.1LTSのリリースで,14.04から16.04への移行用のアップグレードが可能となる予定となっている。このリリースがされたら,UbuntuのSynapticsからアップグレードが可能となる。それまでに,バグなどが直る可能性があるので,緊急でなければまだ待ったほうがいいだろう。

インストール手順

インストール手順は「Ubuntu 16.04 LTSをインストールする - Narrow Escape」を参考にした。以下にインストール手順を記す。

  1. 必要なデータをバックアップ。

    アップグレードで何か問題があったときのために,HDDに大事なデータをバックアップする。ただし,時間がかかるので,バックアップするファイルは絞る。例えば,オンラインストレージやgithubで管理している データは既にバックアップがとれているようなものなので,対象外とし,本当にローカルPCでしか管理していないデータをバックアップする。例えば以下のファイルをバックアップした。

    • VirttualBoxのイメージ:~/VirtubalBox VMs
    • メール:~/.thunderbird
    • 動画・音声:~/Videos
  2. Ubuntu 14.04のアップデートを完了させる。
    sudo apt-get update -y
    sudo apt-get upgrade -y
    sudo apt-get dist-upgrade -y
  3. アップグレードに必要なパッケージをインストールしアップグレード。
    sudo apt-get install update-manager-core -y
    sudo do-release-upgrade -d

    コマンドを実行すると,最初に2回ほど質問されるのでyで回答しておく。

    パッケージのダウンロードに20分ほどかかり,その後の展開・インストールで合計1-2時間ほどかかった。

不具合対応

通常であれば,この後に再起動すればうまくいくのだと思われる。しかし,この再起動で問題が起きた。「Broken Ubuntu 16.04 installation after upgrade (corrupt kernel?) - Ask Ubuntu」で質問されているように,起動時に以下のメッセージが表示されて起動できなかった

[     8.253532] Kernel panic - not synching: Attempted to kill init! exitcode=0x00007f00

おそらく,いくつか対処方法があるのだと思うが,自分が行った方法を記しておく。基本的には,「Ubuntu 14.04から16.04への更新でハマった場合の対処法 (Ubuntu) - Qiita」を参照した。

おそらく,一番確実なのはLive CDから起動することだろう。ただし,いちいちCDやUSBにOSを書き込むのは面倒くさいので,今回はこの方法は使わなかった。

PCの起動

参照:起動時にカーネルパニックする - Ubuntu 14.04から16.04への更新でハマった場合の対処法 (Ubuntu) - Qiita

上記のKernel panicが起きた状態ではどうしようもないので,電源を長押しして強制的に電断する。そして起動する。すると,GNU GRUBの画面(以下の画像の左側)が表示される。

ここで,[Advanced options for Ubuntu]>[Utuntu, with Linux 3.13.x-xx-generic (recovery mode)]にカーソルを合わせ,eキーを押下する。

起動時のコマンドの設定画面(以下の画像の左側)になるので,下から2行目のlinuxから始まる行の最後に,init=/sbin/upstartを追加する。その後,C-x(CTRL+x)で保存して終了する。すると,リカバリーモードでPCが起動する(以下の画像の右側)。

ここで,[resume]を選択すると,PCを起動しようとするが,telinitが見つからなくて失敗して固まる。

upstart-sysvのインストール

参照:telinitもしくはrunlevelが見つからなくて起動できない - Ubuntu 14.04から16.04への更新でハマった場合の対処法 (Ubuntu) - Qiita

これを防ぐために,[root]を選択し,upstart-sysvのインストールを試みる。

しかし,リカバリーモードだと以下2点の制限を受ける。

  • ファイル書き込みができない。
  • インターネットにつながらない。

まず書き込みができるように以下のコマンドで,ファイルシステムをマウントしなおした。

mount -o remount,rw /dev/sda2

今回は/dev/sda2を指定したが,別にルート/を指定しても大丈夫な気がする。

書き込みができるようになったので,今度は以下のコマンドでインターネット接続を有効にする。

dhclient eth0

eth0がダメならeth1を試す。これでインターネット接続が有効になるので,以下のコマンドでupstart-sysvパッケージをインストールする。

apt-get install upstart-sysv

なお,このインストール時に依存関係で他のパッケージをアンインストールしないと,upstart-sysvがインストールできなかった。Ubuntu 16.04が起動できたらインストールし直そう。

ここまで終わったら,exitC-dでターミナルを終了させる。その後,[resume]でPCが起動できるはずだ。

起動後に,GUIが立ち上がらなければ,upstart-sysvのインストール時に,依存関係を解消させるためアンインストールしてしまったのかもしれない。その場合,例えば以下のコマンドで必要なGUI環境をインストールすればよいだろう。

sudo apt-get install unity  # Ubuntu default desktop
sudo apt-get install gnome-desktop # GNOME desktop

まとめ

ひとまずこれでUbuntu 14.04からUbuntu 16.04にアップグレードしてPCを操作できるようになった。

しかし,この他にコンパイラーのgccの実行時に以下のエラーが出るなど,他にも問題はありそうだ。

/home/senooken/local/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o: unrecognized relocation (0x2a) in section `.init'
/home/senooken/local/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status

冒頭で説明したとおり,急ぐ必要がないのであれば,Ubuntu 14.04LTSから移行のためのリリースである2016-06-21のUbuntu 16.04.1LTSまで待ったほうがよいだろう。