2012/12/27

指定された日の曜日を取得する

先日開催された 第2回チキチキ!シェル芸人養成勉強会 の課題の中で自分の中で話題になった事。

指定された日の曜日を取得したい場合、 gnu 版の date (1) がインストールされている場合は 以下のコマンドで取得可能(0 が日曜日を示す)。

$ date -d YYYYMMDD '+%w'
0
    
gnu 版の date (1) がインストールされていない場合でも、 FreeBSD 版の date (1) は独自拡張されているので 以下のコマンドで取得可能だと言う事を教わった(0 が日曜日を示す)。
$ date -j YYYYMMDDHHMM '+%w'
0
    

但しどちらも posix に準拠していない独自拡張な機能を利用しているため 環境依存となってしまうので、 環境に依存しない方法での解決方法を考えてみた。
日付から曜日を取得するためには ツェラーの公式 という計算式があるので、 posix に準拠したシェルの機能だけでその計算式を実装してみる。

$ y=YYYY; m=MM; d=DD
$ test $m -lt 3 && y=$(($y - 1)) && m=$(($m + 12))
$ echo $(($(($y + $y/4 - $y/100 + $y/400 + $(($((13 * $m + 8))/5)) + $d)) % 7))
    
年と月の値によって調整が必要なのでワンライナーにはならないのが残念だけど、 基本的には単純な四則演算なのでシェルの機能のみで実装が可能だ。
これで gnu 版の date (1) がインストールされていない環境でも 任意の日付の曜日が簡単に取得できる!。

2012/12/26

dog (1)

以前、某雑誌のシェルスクリプト大喜利に投稿したネタ。
こんな感じのお題に対する回答。

cat コマンドの作者が遺言を遺しました。
「私の人生における最大の公開は、 dog コマンドを作り忘れてしまったことだ。 あぁ誰か作ってくれぇー。ガクッ」
さぁ、一体どんなコマンド?

お題がネタっぽいので回答もネタっぽくしてみた。
more (1) を拡張して less (1) が、 yacc (1) を拡張して bison (1) が作られた様に、 dog (1) は cat (1) が拡張されたものという発想で httphttps そして ftp といった schema に対応した cat (1) というスクリプトにしてみた。
内容はとても単純なので cat → dog という発想だけが勝負のスクリプトである。

雑誌では割愛されてしまったネタとしてのマニュアルも掲載してみる。
というか、実はマニュアルの方が書きたかったという…(笑

  1#!/bin/sh
  2#
  3# NAME
  4#   dog -- concatenate and print files
  5#
  6# SYNOPSIS
  7#   dog [cat options] [file ...]
  8#
  9# DESCRIPTION
 10#   Dog is a file concatenate and print command in the style of cat(1).
 11#   It should be upwardly compatible with original cat.
 12#
 13#   The dog utility reads files sequentially, writing them to the standard
 14#   output.  The file operands are processed in command-line order.  If file
 15#   has protocol schema such as 'http://', 'https://' or 'ftp://', dog utility
 16#   gets the file from remote system and display it, otherwise dog assumes
 17#   local file. The dog utility uses wget(1), curl(1), or lynx(1) to retrieve
 18#   the file from remote system. If dog utility could not find these commands
 19#   from standard command path, disable remote file display.
 20#   All option of dog is same as cat(1) except '-h'.
 21#
 22# EXIT STATUS
 23#   The dog utility exits 0 on success, and >0 if an error occurs.
 24#
 25# SEE ALSO
 26#   Rob Pike, "UNIX Style, or cat -v Considered Harmful", USENIX Summer
 27#   Conference Proceedings, 1983.
 28#
 29# STANDARDS
 30#   The dog utility is *NOT* complient with the IEEE Std 
 31#   1003.2-1992 (``POSIX.2'') specification, even now and forever.
 32#   
 33
 34curl="-s -o -"
 35wget="-q -O -"
 36lynx="-source"
 37
 38# get command name and path
 39for i in wget curl lynx
 40do
 41    for j in /bin /usr/bin /usr/local/bin /opt/bin /opt/local/bin
 42    do
 43        if [ -x ${j}/${i} ]
 44        then
 45            command="${j}/${i} `eval echo '$'${i}`"
 46            break 2
 47        fi
 48    done
 49done
 50
 51# args
 52while [ ${1} != "" ]
 53do
 54    case ${1} in
 55        -h )
 56            sed -n '2,/^$/s/^#//p' ${0}
 57            exit 1
 58            ;;
 59        -- )
 60            shift
 61            break
 62            ;;
 63        -* )
 64            opt="${opt} $1"
 65            ;;
 66        *  )
 67            break
 68            ;;
 69    esac
 70    shift
 71done
 72
 73for i in $*
 74do
 75    if expr ${i} : "https*://" > /dev/null
 76    then
 77        if [ -n "${command}" ]
 78        then
 79            ${command} ${i} | cat ${opt}
 80        else
 81            echo "${0##*/}: cannot get remote file: ${i}" 1>&2
 82        fi
 83    elif expr ${i} : "ftp*://" > /dev/null
 84    then
 85        case ${command} in
 86            *wget*|*curl* )
 87                ${command} ${i} | cat ${opt}
 88                ;;
 89            * )
 90                echo "${0##*/}: cannot get remote file: ${i}" 1>&2
 91                ;;
 92        esac
 93    else
 94        cat ${opt} ${i}
 95    fi
 96done
    

今回のシェルスクリプト大喜利にも投稿したので、 某雑誌が発売されたら紹介する予定。

2012/12/14

Mountain Lion

iPhone を導入した事を切っ掛けに Mountain Lion 化してみた。
見た目や使い勝手は使い慣れた Snow Leopard の方が好きなのだが、 iPhone との iCloud による完全連携の魅力には逆らえない。
世間では既に言い尽くされている事なのだろうが、 「確かに非常に便利で素晴らしい」、というのが第一印象。
例えば iPhone で撮影した画像は自動的に iPhoto に取りこまれていたり、 Mac で編集した連絡先がいつのまにか iPhone でも変更されていたり、 更には複数台の Mac 間で Mail.app のフィルタ設定まで同期されている。
今まで Dropbox や WebDAV とアイデアを駆使して何とか実現してきた事が、 ID を登録するだけでいとも簡単に実現できてしまうのは凄い事だ。

2012/11/30

IPv4 アドレスがネットワークに属しているか調べる

ネットマスクとはその名の通り IPv4 アドレスのうち、 どこまでのビットがネットワークアドレスなのかを示しているので、 IPv4 アドレスとネットマスクの論理和はネットワークアドレスとなる。
そのため、取得した論理和とネットワークアドレスが等しい場合は IPv4 アドレスはネットワークに属す事になる。

C で実装する場合はこんな感じかな。

  1int     checkAddr(const char *network, const char *netmask, const char *ipaddr)
  2{
  3
  4    struct  in_addr net,
  5                    mask,
  6                    addr;
  7
  8    if(inet_aton(network, &net) &&
  9            inet_aton(netmask, &mask) &&
 10            inet_aton(ipaddr", &addr))
 11        return((addr.s_addr & mask.s_addr) == (net.s_addr & mask.s_addr));
 12    else
 13        return(0);
 14
 15}
    

ちなみに仕事で実装した java のコード。
  1boolean checkAddr(String network, String netmask, String ipaddr)
  2{
  3
  4    try {
  5        int     n = 0,
  6                m = 0,
  7                a = 0;
  8        byte[]  net = InetAddress.getByName(network).getAddress(),
  9                mask = InetAddress.getByName(netmask).getAddress(),
 10                addr = InetAddress.getByName(ipaddr).getAddress();
 11
 12        for(int i=0; i<4; i++)
 13            n |= ((int)net[i] & 0xff) << (8 * (3 - i));
 14        for(int i=0; i<4; i++)
 15            m |= ((int)mask[i] & 0xff) << (8 * (3 - i));
 16        for(int i=0; i<4; i++)
 17            a |= ((int)addr[i] & 0xff) << (8 * (3 - i));
 18
 19        return((a & m) == (n & m));
 20
 21    } catch (Throwable e){
 22        return false;
 23    }
 24
 25}
    
java はそれほど詳しくないのでコーディングが冗長だけど、 取りあえず動作している(気がする)。

2012/11/02

CentOS 6.3 インストール直後にする作業

仕事では Linux サーバ、特に CentOS の導入~初期設定の機会が多いので、 全ての CentOS 6.3 サーバの導入時に必ず行っている作業をメモする。

インストール作業は CentOS 6.3 の DVD から起動して行う。
CentOS 6 からはテキストベースのインストーラの場合、 パーティションの設定やファイルシステムの指定、 初期インストールされるパッケージの追加・削除など指定できないので、 GUI を利用した通常のインストールを実行する。

インストールが終了したら初期環境設定を行う。
以下の作業は CentOS 6.3 を導入したマシンでほぼ必ず実施する作業。

自分のユーザを作製する
# adduser アカウント
# password アカウント
            
自分のアカウントを wheel グループに追加する
sudo (1) 可能な様に自分のアカウントを wheel グループに追加する。
# (rm /etc/group; sed 's/^wheel.*/&,アカウント/g' > /etc/group) < /etc/group
            
sudo(1) の設定を変更する
sudo(1) しても元の環境変数を保持する様設定
Defaults env_reset を Defaults !env_reset に変更
# Defaults env_keep += "HOME" のコメント記号 `#' を削除
            
wheel グループのアカウントのみ sudo (1) 可能にする
# %wheel  ALL=(ALL)   ALL のコメント記号 `#' を削除
            
# visudo
            
termcap を使用するアプリケーション対応
# ln -s /usr/lib/libtinfo.so /usr/lib/libtermcap.so.2
            
selinux を無効にする
# (rm /etc/sysconfig/selinux; sed 's/^SELINUX=.*/SELINUX=disabled/' > /etc/sysconfig/selinux) < /etc/sysconfig/selinux
# ex -s /etc/syconfig/selinux << EOF
> /^SELINUX=/s/.*/SELINUX=disabled
> wq
> EOF
            

2012/11/09 追記

CentOS の /etc/sysconfig/selinux/etc/selinux/config へのシンボリックリンクで 実際は /etc/selinux/config を参照しているのだが、 rm (1) と sed (1) を使った方法だと /etc/sysconfig/selinux が通常ファイルになって 書き換わってしまい、元の /etc/selinux/config が 変更されていないために selinux が無効にならない という問題を指摘して頂いたので編集方法を修正しました。

システム更新
# yum update
            
最低限必要なソフトウェアのインストール
システムを運用する上でこれだけは必要だとうソフトウェアを導入する。
# yum install openssh-clients libtermcap-devel tcpdump
# yum install ntpdate telnet wget tcpdump unzip mlocate rsync bind-utils
            
開発環境のインストール
ソフトウェアを開発する場合は導入する。
通常はサーバでは開発しないので導入は不要な筈…
# yum install make gcc bison flex
            
ファイアウォールの設定
グローバル環境に晒されるサーバであれば 最低限のファイアウォールは設定する必要がある。
基本的には以下のポリシーで必要に応じて追加・削除する。
#!/bin/sh
iptables=/sbin/iptables

# 全て初期化
${iptables} -F

# デフォルトルール
${iptables} -P INPUT DROP
${iptables} -P OUTPUT ACCEPT
${iptables} -P FORWARD DROP

# ループバックを有効にする 
${iptables} -A INPUT -i lo -p all -j ACCEPT

# 接続済みのパケットを受け付ける
${iptables} -A INPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT

# http, httpsはどこからでもアクセス可
${iptables} -A INPUT -p tcp --dport 80 -j ACCEPT
${iptables} -A INPUT -p tcp --dport 443 -j ACCEPT
${iptables} -A INPUT -p tcp --dport 8080 -j ACCEPT
${iptables} -A INPUT -p tcp --dport 9080 -j ACCEPT

# dns パケットを許可
${iptables} -A INPUT -p tcp --sport 53 -j ACCEPT
${iptables} -A INPUT -p udp --sport 53 -j ACCEPT
${iptables} -A INPUT -p tcp --dport 53 -j ACCEPT
${iptables} -A INPUT -p udp --dport 53 -j ACCEPT

# ntp パケットを許可
${iptables} -A INPUT -p tcp --dport 123 -j ACCEPT
${iptables} -A INPUT -p udp --dport 123 -j ACCEPT

# そのほか必要なルールを記載
    :
    :
            
上記設定を /tmp/firewall.sh に格納した場合、 ファイアウォールを有効にして設定をセーブする。
# sh /tmp/firewall.sh
# /etc/init.d/iptables save
iptables: ファイアウォールのルールを /etc/sysconfig/iptableに保存中: [  OK  ]
            
起動サービスの設定
起動サービスを設定する。
基本的には atd crond iptables network ntpd rsyslog sshd のみを有効にする
# for i in `chkconfig --list | awk '/:on/{ print $1 }'`
> do
>  case $i in
>      atd|crond|iptables|network|ntpd|rsyslog|sshd )
>          ;;
>      * )
>          chkconfig $i off
>          chkconfig --del $i
>          ;;
>  esac
> done
                

ここまで設定して再起動する。
MTA や httpd など固有のサービスは個々に設定する。

2012/10/31

hbstudy まとめ

先日参加した hbstudy #38 ハンズオンの 課題 の幾つかを シェルのみ 縛りで回答してみる。
流石にシェルのみで解く場合はワンライナーは無理なので、 普通のシェルスクリプトの型式で記述している。

問題1: ユーザの抽出
/etc/passwd から、ユーザ名を抽出したリストを作って下さい
  1IFS=:
  2while read id other
  3do
  4    echo ${id}
  5done  </etc/passwd
            
IFS: に変更して /etc/passwd を 直接フィールド分割して変数 uread している。
問題2: ユーザの抽出
/etc/passwd から、次を調べて下さい。
ログインシェルが bash のユーザと sh のユーザどちらが多い?
  1IFS=:
  2while read id p u g e d shell
  3do
  4    case ${shell} in
  5        */sh )
  6            sh=$((${sh} + 1));;
  7        */bash )
  8            bash=$((${bash} + 1));;
  9    esac
 10done < /etc/passwd
 11echo "bash: " ${bash}
 12echo "sh:   " ${sh}
            
問題:1 同様に IFS: に変更して /etc/passwd を直接フィールド分割して 変数 ushellread し、 変数 shbash に出現回数をカウントしている。
問題5: FizzBuzz
1,2,3,4,5…と数えていって…
  • 3 の倍数だったら数字の代わりに「Fizz」
  • 5 の倍数だったら数字の代わりに「Buzz」
  • 15 の倍数だったら数字の代わりに「FizzBuzz」
と出力する
  1i=1
  2while [ ${i} -lt ${1:-100} ]
  3do
  4    if [ $((${i} % 15)) -eq 0 ]
  5    then
  6        echo -n "FizzBuzz,"
  7    elif [ $((${i} % 5)) -eq 0 ]
  8    then
  9        echo -n "Buzz,"
 10    elif [ $((${i} % 3)) -eq 0 ]
 11    then
 12        echo -n "Fizz,"
 13    else
 14        echo -n "${i},"
 15    fi
 16    i=$((${i} + 1))
 17done
 18echo
            
素直に 1 から $1 (指定されていない場合は 100) までを $((…)) にて % 演算子で剰余計算して出力している
問題8: CPU 使用率
top の出力からどのユーザが 何% CPU を使用しているか集計して下さい。
  1exec 3<&0 0<< EOF
  2`ps aux`
  3EOF
  4
  5while read u p c other
  6do
  7    if [ "${u}" != "USER" ]
  8    then
  9        case ${users} in
 10            *${u}* )
 11                ;;
 12            *    )
 13                users="${users} ${u}"
 14                ;;
 15        esac
 16        eval "$u=\$((\$$u + ${c%%.*}))"
 17    fi
 18done
 19
 20for i in ${users}
 21do
 22    echo "${i} : `eval echo '$'$i`"
 23done
            
CPU 使用率は小数点以下切り捨てで計算している。

出現したユーザID を users 変数に格納しておく。
eval$((…)) を利用して、 ユーザID を変数名とした変数に CPU 利用率を集計している。
最後に for ループで users に格納された ユーザID とユーザIDを変数名とした変数に格納されている 集計値を表示している。
FreeBSD では top -b -n 1 が利用不可なので ps aux の出力を集計する

全てのスクリプトは FreeBSD 上の /bin/sh で動作確認済み。
手元に Linux 環境が存在しないので Linux での動作は未確認だけど、 posix 機能だけしか使用していない(筈)なので Linux でも動作すると思う。

2012/10/30

Wishing you all the best on your speacial day!


Happy Birthday! Hope your birthday is just the beginning of a year full of happiness.

2012/08/27

きんぎょ♪

奥さんの実家近所のお祭りで娘がげっとしたきんぎょ達
しーこ ちゃんと ころこ ちゃん

Image: kingyo.jpg

伝統的な金魚鉢の中で楽しそう

そこの 誰かさん
準備、そろそろOK?
それじゃ お知らせしましょ
わぴこの 元気予報

わぴこ元気予報!

2012/08/12

行き当たりばったり

折角の新車なのでドライブでも…と早朝 5 時に起床して出発。
新東名の駿河湾沼津 SA のリサとガスパールのカフェに行ってみたいので、 とりあえず本栖湖を目的地に設定し朝 6 時に出発する。

が、しかし既に東名高速は渋滞している様で、 VICS 信号を受信したナビは鶴川方面を通過して 高尾山の辺りから中央高速経由のルートを指示してくる。 ナビに従って走り続けると殆ど渋滞もしていない中央高速から 朝 9 時には河口湖に到着してしまう。
折角河口湖に早い時間に到着したので当初の目的をすっかり変更し 白鳥のボートなどに乗りつつ河口湖を満喫してると空腹になってきたので、 以前何度か行った山中湖のレストランに向かう事にする。
山中湖に向かうと丁度ランチタイムに無事到着。 別荘地の森の中にある隠れ家の様な燻製レストランの とっても気持ち良いテラスで美味しいランチを楽しんでいると 気持ちはすっかり泊まりたいモードに…
お盆休みの始まる週末に空き室なんかある宿がある訳ないだろうと思いつつ レストランのご主人に聞いてみた所、 偶然空いているペンションを紹介していただけたので計らずも宿が決定。 宿が決まったので安心して 3 人乗り自転車で山中湖を堪能したり、 温泉にのんびりつかったりと山中湖も満喫。

Image: IMG_1981.JPG

別荘地の静かなペンション

夕食はアメリカン風のコース料理だったのだが、 スターターからセンス良い味付けで最後まで大満足。
翌日は当初の目的通り本栖湖を経由して新東名高速へ。 念願のリサとガスパールカフェに立ち寄って軽く食事をして自宅へ。

Image: IMG_1985.JPG

新東名

Image: IMG_1989.JPG

リサとガスパール カフェのデザート

行き当たりばったりで日帰りドライブの予定で出かけた筈なのに、 殆ど渋滞らしい渋滞にも遭遇せずに とっても充実した一泊の家族旅行が楽しめました。

家族一緒だと とっても楽しいね☆

2012/08/04

... has come

Image: IMG_1963.JPG

MAZDA CX-5

2012/08/03

Coming soon ...

Image: CX5.png

Zoom-Zoom!

2012/08/01

キーボードショートカットのカスタマイズ

Mac では システム環境設定キーボードキーボードショートカット から アプリケーション毎、もしくはシステム全体で有効な キーボードショートカットが独自に作成できる。
この機能を利用するとメニューに項目があるが ショートカットキーが定義されていない機能に 独自のショートカットキーが簡単に定義できるので非常に便利だ。
但し登録できるショートカットキーに制限があり スペースやバックスペースなどは登録できないので、 KeyRemap4MacBook を組み合わせて利用する事で任意のショートカットが利用できる様に設定する。

例えば プレビュー では ツール メニューから 水平方向に反転垂直方向に反転 などの機能が利用できるが、 これらの機能には標準ではショートカットが定義されていない。
そこで前述の キーボードショートカット 機能を利用して プレビュー水平方向に反転のショートカットや、 垂直方向に反転のショートカットを コントロールやシフトなどとの組み合わせで指定する。
ここでは 水平方向に反転コマンド+シフト+H垂直方向に反転コマンド+シフト+V を登録したとする。
ショートカットが登録できたら プレビュー を起動して 指定したショートカットが正しく動作するか確認しておく。

次に KeyRemap4MacBook でスペースを コマンド+シフト+H に、 バックスペースを コマンド+シフト+V に置き換える設定を行えば、 スペースやバックスペースなどでショートカットが実行可能になる。 この時、キーの置き換えは特定のアプリケーション (今回は プレビュー)だけに限定すると良いだろう。
そのためには KeyRemap4MacBook の Misc & Uninstall から Open private.xml をクリックして以下の設定を追加する。

  1<?xml version="1.0"?>
  2    <root>
  3        <!-- アプリケーション "プレビュー" の定義 -->
  4        <appdef>
  5            <appname>PREVIEW</appname>
  6            <equal>com.apple.Preview</equal>
  7        </appdef>
  8
  9        <!-- "プレビュー" に対するキーマッピングの定義 -->
 10        <item>
 11            <name>Preview Key</name>
 12            <identifier>private.PreviewKeys</identifier>
 13                <only>PREVIEW</only>
 14                <!-- スペースはコマンド+シフト+H に置換 -->
 15                <autogen>--KeyToKey-- KeyCode::SPACE, KeyCode::H, ModifierFlag::COMMAND_L | VK_SHIFT</autogen>
 16                <!-- バックスペースはコマンド+シフト+V に置換 -->
 17                <autogen>--KeyToKey-- KeyCode::DELETE, KeyCode::V, ModifierFlag::COMMAND_L | VK_SHIFT</autogen>
 18        </item>
 19    </root>
    
これらの設定を追加する事で プレビュー 上では スペースは コマンド+シフト+H に置換されてアプリケーションに 通知されるので、 登録したショートカット(水平方向に反転)が実行される事になる。

2012/07/27

シェルスクリプトで便利な小技

シェルスクリプトを作成するときに知っておくと便利な小技集。

exec > ファイル
以降の標準出力を全て ファイル に出力するので この設定をしておくと簡単な実行ログが取得出来る。
同様に exec 2> ファイル とすると 標準エラー出力が全て ファイル に出力されるので エラーログが取得できる。
当然 exec > ファイル 2>&1 とすれば 標準出力も標準エラー出力も取得できる。
ファイル/dev/null を指定すれば スクリプト実行中の出力は全て抑止されるので、 cron (8) から実行される場合などでは便利な場合もある。
  1#!/bin/sh
  2
  3exec 2> ${TMP:-/tmp}/myname.log
  4    :
            
set -e
スクリプト実行時に制御文以外でエラーが発生した場合に スクリプトを終了させる。
スクリプト中で実行すべきコマンドを typo した場合などで、 以降の処理が実行されると困る場合などに特に役立つ。
  1#!/bin/sh
  2    :
  3echo "call myfunc ..."
  4mtfunc                                          # 関数名を typo している
  5echo "end myfunc ..."
  6    :
            
この様なコードの場合 set -e されていると、 mtfunc がエラーになった時点でスクリプトが エラー終了するので以降の処理が実行されない。
当然 ifwhile&&|| などでコマンドの結果が評価される場合は エラーにはならない。
set -u
スクリプト中で値が設定されていない変数を参照した場合に エラーメッセージを表示してスクリプトを終了させる。
シェル変数や環境変数を typo した場合など、 変数に値が設定されていない事で発生する問題が回避できる。
  1#!/bin/sh
  2tempdir=/var/tmp/
  3mynam=myname
  4    :
  5rm -rf ${tmpdir}/${myname}                      # 変数名を typo している
  6    :
            
この様なコードの場合、 変数名 ${tempdir}${mynam} を それぞれ typo しているので、 結果として rm -rf / に展開されてしまうが、 set -u されていると実行が防げる。
set -n
こちらはどちらかと言うとスクリプトの開発時に有効な設定で、 コマンドは実行されないのでスクリプトの文法チェックに最適。
set -x
こちらも開発時に有効な設定で、 コマンドが実行される前にコマンドを stderr に出力する。
上の exec と組み合わせると 詳細な実行ログが取得できるのでデバッグに便利
  1#!/bin/sh
  2
  3exec 2> ${TMP:-/tmp}/debug.log
  4set -x
  5    :
            

2012/07/23

英辞郎の辞書をコマンドラインから参照してみる

英単語の意味を調べるのに man(1) を使ってしまう人に贈る1行 という記事を拝見して、 自分の FreeBSD マシンで英辞郎の辞書を参照している方法を記事にしてみる。
要は英辞郎の辞書を EPWING 型式に変換して ndt サーバ経由で ndtpc コマンドから検索しているだけで、 検索コマンド自体は 1 行だが多少準備が必要となる。

英辞郎、和英辞郎、略辞郎、音辞郎の辞書を変換する。
英辞郎の辞書を EPWING 型式に変換するのは ports を利用すれば 何の問題もなく可能だ (若干ディスク容量が必要だが、 今となっては問題とならない容量だと思う)。
作業は英辞郎の CD-ROM をマウントした状態で実施すれば良いのだが、 CD-ROM は I/O が遅いのでローカルのファイルシステムに 辞書ファイルをコピーして実行しても良い。
ローカルにファイルをコピーした場合は DICT_PATH で 英辞郎の辞書ファイルのパスを指定すれば良い。
# cd /usr/ports/japanese/eijiro-fpw
# make DICT_PATH=辞書ファイルのパス install clean
# cd /usr/ports/japanese/waeijiro-fpw
# make DICT_PATH=辞書ファイルのパス install clean
# cd /usr/ports/japanese/ryakujiro-fpw
# make DICT_PATH=辞書ファイルのパス install clean
# cd /usr/ports/japanese/otojiro-fpw
# make DICT_PATH=辞書ファイルのパス install clean
            
この変換作業は結構時間が必要である。

ndtpd をインストールする
ndtpd は EB や EPWING などの辞書型式に対応している辞書サーバで、 FreeBSD では ebnetd という ports から導入可能である。
ports から導入すると ndtpd 以外に ebnetd、ebhttpd が インストールされるが今回は ndtpd のみ利用する。
# cd /usr/ports/japanese/ebnetd
# make install clean
# vi /usr/local/etc/ebnetd.conf
# cat << EOF >> /etc/rc.conf
> ndtpd_enable="YES"
> EOF
# chown nobody:nobody /var/run/ebnetd
# /usr/local/etc/rc.d/ebnetd.sh start
            
ebnetd.conf はコメントを参照にすれば簡単に設定できる。 今回はローカルサーバの自分しか利用しないので、 接続ホストや接続数を制限した。
### Port number `ndtpd' binds.
ndtp-port       ndtp

### Owner of the server process.
user            nobody

### Group of the server process.
group           nobody

### How many clients can be connected to the server at the same time.
max-clients     1

### Which hosts can or cannot connect to the server.
hosts           127.0.0.1

### Timeout seconds until the server disconnects an idle connection.
timeout         900

### Path to a working directory.
work-path       /var/run/ebnetd

### How many hit entries the server tries to find at a search.
max-hits        50

### The maximum size of text the server may send as a response to a client.
max-text-size   32768

### Syslog facility
syslog-facility local0

###
### Book entry
###
begin book
    ### Name of the book.
    name        eijiro

    ### Title of the book.
    title       eijiro

    ### Path to a top directory of the book.
    path        /usr/local/share/dict/eijiro-fpw

    ### How many clients can access the book at the same time.
    max-clients 1

    ### Which hosts can or cannot access to the book.
    hosts       127.0.0.1
end

begin book
    #
    # 以下 waeijiro の設定
    #
end

begin book
    # 
    # 以下 ryakujiro の設定
    #
end

begin book
    #
    # 以下 otojiro の設定
    #
end
            

作成された辞書を ebzip で圧縮する
EB ライブラリで使用されている圧縮方式で、 辞書のためのディスク容量を多少なりとも削減するために辞書を圧縮する。
# cd /usr/local/share/dict/eijiro-fpw
# ebzip --level 5
# cd /usr/local/share/dict/waeijiro-fpw
# ebzip --level 5
# cd /usr/local/share/dict/ryakujiro-fpw
# ebzip --level 5
# cd /usr/local/share/dict/otojiro-fpw
# ebzip --level 5
            

ndtpc をインストールする
ndtpc は perl で書かれた NDTP クライアントで、 こちら で公開されているので利用する。
今の所 FreeBSD の ports にはなっていない様なので、 ソースを取得してインストールする。
$ fetch http://www.tanu.org/~sakane/download/ndtpc-20050323a.tgz
$ tar -C /usr/local/src -zxvf ndtpc-20050323a.tgz
$ ./configure
# make install
            

これで以下のコマンドラインで辞書検索が可能になる。

$ ndtpc hello
DICT: 英辞郎
<4c33:560>hello
hello
【発音】helo'u 【変化】《複》hellos
[間投] こんにちは, あのう, やあ
<4c33:5f0>hello
Hello
[人名] エロ
DICT: 和英辞郎
DICT: 略語辞典
DICT: 音辞郎
    
ちなみに jvimkeywordprgndtpc を登録すると 単語の上で 'K' をタイプする事で辞書を検索できるので便利。

2012/07/12

久々の迷惑メイル

先ほど携帯に届いた迷惑メイル。
やりくちがあまりにも下衆なので修正なしで晒す。

From: info@wu30oa22rs81rr.info
To: XXXX@docomo.ne.jp
Date: 2012/ 7/12 18:30
Subject: ※レターバック※宛先不明により集中センター保管中※受け取り先ご確認をお願い致します

現在、お客様宛のレターメールをお預かりしております。
配送先のご確認が必要となっておりますのでお手数ではございますが、下記よりアクセスの上、配送確認をお願い致します。
本メール配送便に関しまして、お届け先不着の状態ですとお届けが出来ませんのでご注意下さいませ。
http://wu30oa22rs81rr.info/XXXXXXXXXXXXXXXXXXXXXX/1307240/sagawa/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
From アドレスや本文に書かれているの URL のドメイン部分が 怪しさ満点な wu30oa22rs81rr.info だったり、 URL の中に無理矢理入れた sagawa の文字だったり、 レターバック 等という低脳としか思えない間違いだったり etc. … 普通の人は引っかかる事はないと思いますが 注意喚起の意味をこめて公開します。
そもそもレターパック(バック?)を騙るなら sagawa じゃなくて japanpost だろうと思うのだが…、 というかレターバッグって郵便配達の方が持っている鞄か?。
一生懸命にそれっぽく作ろうとしている努力は認めなくもないが、 文章の端々からにじみ出す本当の意味での頭の悪さを思うと 人ごとながら同情を禁じ得ない。

最近のドコモの携帯はメイル表示画面から迷惑メイルの報告ができる様なので、 勿論通報しておいた。

2012/06/27

shebang

スクリプトの先頭行に書かれている #!/bin/sh の事を shebang (シェバング) と呼ぶ。
古くはプログラムが実行された際にローダがファイルの先頭 2 バイトを参照して ファイルの形式を調査した事に由来しているらしく、 例えば FreeBSD や Linux の最近の実行可能ファイルであれば 7f45(16) になっている (その後 4c46(16) と続いているので、 ファイルをダンプすると ELF と読める)。
ファイル先頭のこの値の事を magic number とも呼び、 初期の file (1) コマンドはこの magic number を読み取って ファイルの種類を報告していた (今の file (1) も基本は magic number を利用している)。

shebang は通常はスクリプトを実行するインタプリタのパスを記述するのだが、 実は実行可能なプログラムであればインタプリタ以外も記述できるので、 あるプログラムの設定ファイルの先頭にプログラム名自身を書いておくと その設定でプログラムが実行できて実は便利である。

例えば sshd/usr/local/etc/sshd/mysshd.conf を設定ファイルとして起動したい場合、通常はこの様に起動する。

# /usr/sbin/sshd -f /usr/local/etc/sshd/mysshd.conf
    
ところが設定ファイル /usr/local/etc/sshd/mysshd.conf の 先頭の shebang に #!/usr/sbin/sshd -f を追加して /usr/local/etc/sshd/mysshd.conf に実行権限を与えると、 設定ファイルを実行する事で sshd が起動できるので便利だ。
# cat /usr/local/etc/sshd/mysshd.conf
Port        20022
Protocol    2
    :
# ex -s !$ <<- EOF
ex -s /usr/local/etc/sshd/mysshd.conf <<- EOF
>  0i
>  #!/usr/sbin/sshd -f
>  .
>  w!
>  EOF
# head !$
head /usr/local/etc/sshd/mysshd.conf
#!/usr/sbin/sshd -f
Port        20022
Protocol    2
    :
# chmod 755 !$
chmod 755 /usr/local/etc/sshd/mysshd.conf
# !$
/usr/local/etc/sshd/mysshd.conf
# netstat -anf inet | egrep '^Active|^Proto|\.20022'
Active Internet connections (including servers)
Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)
tcp4       0      0  *.20022                *.*                    LISTEN
    
実行例で利用している "!$" は GNU bash (1) の コマンドライン履歴機能で直前に実行したコマンドの最後の引数に展開される。
GNU bash (1) には他にも 色々な履歴の参照機能 があるので、 覚えておくとコマンドの入力が格段に便利になる。

2012/06/26

Bloxsom プラグイン

先日のコーディング晒しでソースコードを表示する際、 当初は <pre> タグにファイルの中身を貼り付けたり、 直接ソースコードを記述した後で、 手作業でタブの展開や特殊キャラクタの置換の作業をしていたのだが、 あまりにも面倒に感じたのと、 折角ソースコードを表示するならせめて行番号は表示したいので 結局プラグインをでっち上げた。
似たような機能では SyntaxHighlighter が有名だが、

  • JavaScript を利用している
  • 普段から Syntax による色づけは大嫌いなので使っていない
  • 表示するコードの種類が複数ある場合に使い方が煩雑
などの理由で使いづらく感じたので blosxom 用のソースコード/コマンドライン表示用プラグイン を作ってみた。

慣れない perl で適当に書いたのであまり自慢できる代物ではないのだが、 Bloxsom を利用している人の役に立てば嬉しいので公開してみる。 例によって動作に関しては全くの無保証だが、 もしも問題や改善案などがあれば是非連絡して欲しいと思う。

2012/06/25

太陽のたまご♪

すでに毎年恒例の家内行事となった完熟マンゴー。

Image: RIMG0617.JPG

太陽のたまご

今年も 4 個です。
素晴らしく甘く美味しい自然の恵みです。

2012/06/21

シェルスクリプトでの排他処理

業務系のシステムでシェルスクリプトを使用している場合など、 多重起動の防止などで排他制御が必要な場合が多々ある。
その様な場合に役に立つ(と思われる) シンボリックリンクを利用したシェルスクリプトでの排他制御方法。
ただし、あくまでもシェルスクリプトによる処理なので、 厳密な意味での排他制御にはなり得ないために タイミングによってはどうしても多重起動してしまう危険性がある事は 考慮しておく必要がある。

この処理では自分自身のプロセス ID をロックファイルとして シンボリックリンクして利用しているので、 排他制御を実施したプロセス ID が簡単に確認できる様にするとともに、 何らかの理由で不正にロックファイルが残ってしまった場合に プロセス ID を確認する事で簡単にリカバリ可能としている。
なお、プロセス確認のために /proc を参照しているので、 Linux など /proc が存在する システムのみで利用可能となっているが、 ps (1) などを利用してプロセスを特定すれば /proc が無いシステムでも利用可能だろう。

  1# ロックファイル
  2lockfile=${TMP:-/tmp}/${0##*/}
  3
  4# 多重起動防止のために symbolic link を作成する
  5# link が作成できたら trap を設定し終了時に link を自動削除する
  6# synbolic link が存在しても /proc/${PID} がない場合は
  7# ロックファイルが不正に残っているので削除して処理を続行する
  8while true
  9do
 10    if ln -s $$ ${lockfile} 2> /dev/null
 11    then
 12        # ロック取得できた
 13        break
 14    else
 15        # ロックファイルが既に存在している場合
 16        # ロックファイルから作成元の PID を取得する
 17        if [ -d /proc/`ls -l ${lockfile} | sed 's!.* !!g'` ]
 18        then
 19            # プロセスが存在する場合
 20            echo "${0##*/}: exist another instance" 1>&2
 21            exit 1
 22        else
 23            # プロセスが存在しない場合はロックファイルを削除してリトライ
 24            rm -f ${lockfile}
 25        fi
 26    fi
 27done
 28
 29# 終わる時は必ず lockfile を削除する
 30trap 'rm -f ${lockfile}; exit' 0 1 2 3 11 15
 31
 32# 実際の処理
 33    :
    

2012/06/20

コーディングスタイル ネタ編

twitter で話題になったので shell スクリプトにしてみた。
一応 posix 準拠の shell で動作すると思うんだけど、 $((…)) を使ったビット演算は果たして posix 準拠なのか微妙
あくまでもネタという事で笑って見て貰えれば嬉しい。

  1#!/bin/sh
  2# All rights reserved, copyright (C) 2012, Mitzyuki IMAIZUMI
  3#
  4
  5#
  6# 10進数を2進数に変換して表示
  7#
  8printb()
  9{
 10
 11    local   i str num
 12    
 13    i=0
 14    num=${1}
 15
 16    while [ $i -lt 32 ]
 17    do
 18        str="$((num & 1))${str}"
 19        num=$((num >> 1))
 20        i=$((i + 1))
 21    done
 22
 23    echo "${str}  (${1})"
 24
 25}
 26
 27#
 28# usage メッセージ表示
 29#
 30usage()
 31{
 32
 33    echo "Usage: ${1} [-v][-h|-o] num [... num]" 1>&2
 34    
 35    return 255
 36
 37}
 38
 39#
 40# ヘッダ表示
 41#
 42header()
 43{
 44
 45    local   i
 46
 47    i=0
 48
 49    while [ ${i} -lt 4 ]
 50    do
 51        printf "       *"
 52        i=$((i + 1))
 53    done
 54
 55    echo
 56
 57}
 58
 59#
 60# メイン処理
 61#
 62
 63# オプション解析
 64if args=`getopt voh $*`
 65then
 66    set -- ${args}
 67    for i
 68    do
 69        case "${i}" in
 70            -v )
 71                verbose=true;;
 72            -o )
 73                prefix="0";;
 74            -h )
 75                prefix="0x";;
 76            --  )
 77                shift
 78                break;;
 79        esac
 80        shift
 81    done
 82else
 83    usage ${0##/}
 84    exit
 85fi
 86
 87# ヘッダ出力
 88${verbose:-false} && header
 89
 90# 2進数変換
 91for i
 92do
 93    printb `printf %d ${prefix}${i}`                # 変換出力
 94done
    
どうだ、某会長(謎

ちなみに bc (1) を利用すると簡単に実現できちゃいますが、 プログラミング自体を楽しむという事で…

$ echo "ibase=10; obase=2; 10" | bc
1010
$ echo "ibase=16; obase=2; 20" | bc
100000
    

2012/06/19

コーディングスタイル

ちょっと間が空いてしまったが、 自分のコーディングスタイルを晒すために 簡単なプログラムを書いてみた。
プログラムは与えられた数値を2進数にして表示するだけの 簡単なプログラムなのであまり参考にはならないと思うが、 コーディングスタイルで取り上げた要素がほぼ網羅されていると思う。
条件演算子は無理矢理使ったので良い子は真似しない様に

  1/*
  2 * All rights reserved, copyright (C) 2012, Mitzyuki IMAIZUMI
  3 *
  4 * $Id: printb.c,v 1.1 2012/06/18 10:57:54 mitz Exp $
  5 */
  6#include    <stdio.h>
  7#include    <stdlib.h>
  8#include    <string.h>
  9#include    <unistd.h>
 10
 11/*
 12 * 10進数を2進数変換に変換して表示
 13 */
 14void    printb(int  num)
 15{
 16
 17    int     x = num;
 18    char    str[sizeof(int)*8+1],
 19            *ptr;
 20
 21    memset(str, '\0', sizeof(str));
 22
 23    for(ptr=str+sizeof(str)-2; ptr>=str; x>>=1)
 24        *ptr-- = '0' + (x & 1);
 25
 26    printf("%s  (%d)\n", str, num);
 27
 28}
 29
 30/*
 31 * usage メッセージ表示
 32 */
 33int     usage(const char    *myname)
 34{
 35
 36    fprintf(stderr, "Usage: %s [-v][-h|-o] num [num ...]\n", myname);
 37
 38    return(255);
 39
 40}
 41
 42/*
 43 * ヘッダ表示
 44 */
 45void    header(void)
 46{
 47
 48    size_t  i;
 49
 50    for(i=0; i<sizeof(int); i++)
 51        printf("       *");
 52    printf("\n");
 53
 54}
 55
 56/*
 57 * メイン処理
 58 */
 59int     main(int argc, char **argv)
 60{
 61
 62    int     verbose = 0,
 63            base = 0;
 64    char    ch,
 65            *myname;
 66
 67    /* basename $0 取得 */
 68    myname = (myname = strrchr(*argv, '/')) ? myname++ : *argv;
 69
 70    /* オプション解析 */
 71    while((ch = getopt(argc, argv, "voh")) != EOF)
 72        switch(ch){
 73            case 'v':
 74                verbose = 1;
 75                break;
 76
 77            case 'o':
 78                base = 8;
 79                break;
 80
 81            case 'h':
 82                base = 16;
 83                break;
 84
 85            default:
 86                exit(usage(myname));
 87        }
 88
 89    argc -= optind;
 90    argv += optind;
 91
 92    /* ヘッダ出力 */
 93    if(verbose)
 94        header();
 95
 96    /* 2進数変換 */
 97    while(*argv)
 98        printb(strtol(*argv++, NULL, base));
 99
100    exit(0);
101
102}
    

2012/06/18

posix shell で標準入力同士の diff (1) を実現する方法

コマンド command1 の出力結果と コマンド command2 の出力結果を diff (1) で比較したい場合、 一番手軽なのはそれぞれの出力を一時ファイルに出力して比較する方法である。

$ command1 > ${TMP:-/tmp}/output1
$ command2 > ${TMP:-/tmp}/output2
$ diff ${TMP:-/tmp}/output1 ${TMP:-/tmp}/output2
    :
$ rm ${TMP:-/tmp}/output1 ${TMP:-/tmp}/output2
    
しかし、この方法では一時ファイルを作成するので 処理効率が悪く一時ファイルの削除など後処理をする必要がある。
例えば bash (1) の場合は以下の様にする事で簡単に比較できる。
$ diff <(command1) <(command2)
    :
    
これは bash (1) 独自の機能なのだが、 posix 準拠のシェルでも以下の様に記述すると比較可能である。
$ command1 | ( command2 | diff /dev/fd/3 -) 3<&0
    :
    
ちょっとだけ理解しづらい記述であるが、 command1 の結果は 3<&0 の記述に従って ファイルディスクリプタ 3 へ出力される。 diff の第1引数は /dev/fd/3 が指定されているので、 結果として command1 の出力を受け取ることができ、 command2 の結果は直接標準出力を経由して diff の 第2引数へと渡されるので結果として command1 の出力と command2 の出力が比較できる。

2012/06/06

コーディングスタイル

自分のコーディングスタイル晒しの続き。

空白
linux コーディング規約とは若干異なっている。
基本的に余計な空白はなるべく入れない様にしているので、 ifswitchcasefordowhile の後ろに空白は入れない。 それ以外は linux コーディング規約とほぼ同様だと思う。
2 項演算子や3 項演算子の演算子の前後には空白を入れるが、 例外的に for 文の中では演算子の前後に空白を入れていない。
    /* 2 項演算子 */
    i = j * 2;

    /* 3 項演算子 */
    x = x < y ? x : y;

    /* if 文 */
    if(condition)
        do_condition();

    /* for 文 */
    for(i=0; i<max; i++)
        do_loop();
            

名前の選択
これも linux コーディング規約に賛成。
例えばループのインデックスに利用するローカル変数は ij で十分だと思う。
基本的にローカル変数は単純な名前で十分意味が通じると思うし、 変数名を工夫して可読性を高める必要があるのであれば、 そもそも関数のサイズやロジックを見直す必要があると思う。
それから、変数名に長めの英単語や英単語の組み合わせを使う場合に 特に我々が気をつけたいのはスペルミスだと思う。 他人に見られる可能性があるならせめて正しいスペルにするべき。 変な名前の変数だと逆に意味不明になりかねないし、 そもそも見る方も見られる方も恥ずかしい。
ハンガリアン記法に反対している部分も大賛成で、 コンパイラ言語ではコンパイラが正しく判断してくれる情報を わざわざ変数名に付けても得られる事は殆どないと思う。

typedef
linux コーディング規約とは若干異なっていて、 基本的には typedef で実態を隠蔽する方法を多用する。
fopen (3)fclose (3) の様に パラメタを全て隠蔽した上で閉じたインタフェイスを提供した方が 使い方や記述がすっきりするのは勿論の事、 保守性も可読性も向上すると思っているので、 基本的には typedef したインタフェイス設計をする事が多い。
当然 typedef した場合はメンバーを完全に隠蔽するので、 例えば構造体を typedef した場合に、 構造体のメンバーに直接アクセスする様な事は絶対にしない。

関数
これも linux コーディング規約に賛成。
そもそも数学的な関数とは 入力値に対して一意な値を決定する規則の事なので、 プログラミングにおける関数も同様に、 単純に単一の事だけを実行して値を返すべきだと思う。
そのためにも関数の大きさはできれば 10 ~ 20 行程度、 長くても 50 行以内にはまとめたいと思う。

goto 文
これも linux コーディング規約に賛成。
何が何でも goto を嫌う人もいるが、 場合や使い方によってはとても便利だし可読性も高くなる。
linux コーディング規約では終了処理の共通化が上げられているが、 他にも多重ループの内側から全てのループを抜ける際などにも便利。
    while(condition1){
        while(condition2){
            if(anyconditions){
                do_something();
                break;
            }
            else if(otherconditions){
                do_anything();
                goto END;
            }
        }
    }
END:
    next_statement;
            
たとえば上記の処理を goto 使わないで実現すると、 変なフラグ変数を利用して内側のループ終了で フラグを判定する処理などが必要になるので却って煩雑になる。 なお、別解としてループ全体を関数とする方法もあり、 処理によっては関数化の方がより一層エレガントな場合もある。

最初に c に触れた時に参考にしたのは『初めての C』だったが、 この書籍はずぶの素人への入門としてはそれほど悪くなかった様に思う。 その後、『プログラミング言語 C』(いわゆる K & R) で 基本的なコーディングスタイルなどをほぼ独習した形なので、 それなりに独自のスタイルでコーディングしているのだと改めて思う。 独自のスタイルな事の言い訳ではないのだが、 自分のスタイルで統一した書き方のソースであれば、 多少独特な書き方でもそれほど見づらくはないと思う。
特にコーディングに自由度が高い c の場合、 絶対的に正しいスタイルなど存在しないと思うので、 常識的な範囲でそれぞれが独自のスタイルを作れば良いのではないだろうか。

追記

本文中での 3 項演算子 は一般名称として ternary operator の意味で 2 項演算子(binary operator) との対比として使用している。
c では条件演算子(conditional operator) `?:' が 唯一の 3項演算子なので 条件演算子 `?:' の事を 3 項演算子と呼ぶ人も多くいる様だが、 厳密には意味が異なるので要注意。

2012/06/05

コーディングスタイル

世の中にはプログラマの数だけコーディングスタイルがあると思うけど、 Linux カーネルのコーディング規約 と対比しながら、自分のコーディングスタイルを晒してみる。
勿論これはスクラッチで自分が自由に作成出来る場合のスタイルであって、 プロジェクトなどによりコーディング規約が定まっている場合や 他人が作成したソースを修正する場合は基本的にはそのスタイルに合わせる。

インデント
linux コーディング規約とは完全に意見を異にするが、 基本的にタブは 4 文字にしている。
通常の 80 カラムのディスプレイで見たときに タブが 8 文字だと間延びして見えてしまう事と、 ネストが 3 段以上になると格段に見づらくなる事が主な理由。
勿論、「3 段以上のネストは悪い」という意見は 原則的に賛成だが、 そうは言っても実際はもっと深くネストする事が 避けられない場面も多々ある。

case
linux コーディング規約とは若干異なっていて、 switchcase はインデントさせている。
深い理由はないのだが何となく見やすいと思う。
    swich(suffix){
        case 'G':
        case 'g':
            mem <<= 30;
            break;

        case 'M':
        case 'm':
            mem <<= 20;
            break;

        case 'K':
        case 'k':
            mem <<= 10;
            /* fall through */

        default:
            break;
    }
            

1行に複数の文を書かない
これは linux コーディング規約に賛成。
特に if に続くセンテンスは必ず改行して書いて欲しい所だ。
    if(condition)
        do_this;
    do_something_everytime;
            

インデントはタブを利用する
これも linux コーディング規約に賛成。

まともなエディタを利用する
何を「まとも」とするかで宗教戦争が勃発しちゃいそうだが、 例えば 1 行を 80 カラム以上で横にスクロールさせながら表示する エディタは使って欲しくないと思う。 そういうエディタを使っていると往々にして 1 行が 80 カラム以上の見づらいソースになりがちだから。

行の長さは 80 カラムが限界で強く推奨される
これも linux コーディング規約におおむね賛成。
ただし分割した 2 行目以降を 「かなり短く、かなり右におく」 事はしない。
基本的には 1 行目の先頭からタブ 1 個から 2 個分 右にインデントした位置にしている。
    if(condition)
        printf(KERN_WARNING "Warning this is a log printk with "
                "3 parameters a: %u b: %u c: %u\n",
                a, b, c);
    else
        next_statement;
            

ブレースの位置
これも linux コーディング規約におおむね賛成。
ifswitchforwhiledo においてはブロックの開始行の行末に開きブレース、 ブロックの最終行の行頭に閉じブレースというスタイルで書いている。 関数定義のブレースについての書き方も同様に賛成で、 開きブレースは次の行の行頭に書いている。
ただし else 節を開始する開きブレースは 昔から次の行の行頭に置くクセがあるので今でもそのまま書いている。
    /* 関数定義 */
    int     function(int i)
    {

        body;

    }

    /* for 文 */
    for(i=0; i<max; i++){
        do_condition_1;
        do_condition_2;
    }

    /* while 文 */
    while(condition){
        do_condition_1;
        do_condition_2;
    }

    /* do ~ while 文 */
    do{
        do_condition_1;
        do_condition_2;
    }while(condition);

    /* if ~ then ~ else 文 */
    if(condition == 1){
        do_condition_1_1;
        do_condition_1_2:
    }
    else if(condition == 2){
        do_condition_2_1;
        do_condition_2_2:
    }
    else{
        do_condition_other_1;
        do_condition_other_2:
    }
            

必要のないブレースを使わない
これも linux コーディング規約に賛成。
特に if の後ろには単文でも必ずブレースを使う人がいるが、 ソースが煩雑になるので不要なブレースは省略している。
    if(condition)
        this();
    else
        otherwise();
            

長くなってしまったので今回はここまで。
(多分)続く…

2012/05/26

gdb の意外に便利な使い方

開発環境には必ずインストールされている gdb (1) コマンド。
本来の機能はプログラムのシンボリックデバッガなのだが、 若干意外とも言える便利な使い方がある。

それはプログラマなら重宝する 16 進数電卓としての用法。

$ gdb -q
(gdb) p/x 10                     ← 10 を 16進数で表示
$1 = 0xa
(gdb) p/x 20                     ← 20 を 16進数で表示
$2 = 0x14
(gdb) p/x $1 + $2                ← 10 + 20 を 16進数で表示
$3 = 0x1e
(gdb) p $2 - $1 / 3              ← 20 - (10 / 3) を 10進数で表示
$4 = 17
(gdb) p ($2 - $1) / 3            ← (20 - 10) / 3 を 10進数で表示
$5 = 3
(gdb) q
    
例にある様に一度利用した値は変数 $n に保存されるので、 計算結果を後から参照するのも便利だし、 四則演算の優先順位もちゃんと解決してくれるので電卓としても結構使える。
ちなみに入力データは Unix/C の原則に従って、 0 が先行する値は 8 進数、 0x が先行する値は 16 進数として認識される。
ちょっとした便利機能の紹介でした。

2012/04/02

ご近所さん

我が家の近所には同じ年頃の子供を持つ家庭が偶然多く暮らしているので、 非常に楽しく素敵なご近所つきあいをさせて頂いている。

ご近所さんのうちの1家族は震災の随分前に福島から引っ越してきたのだが、 同じ年の子供がいたのでとっても仲良くさせて頂いていた。 いつも仲が良いとっても素敵な家族で子供達も凄く素直で優しい良い子達だった。 その家族が仕事の都合で先日引っ越してしまったのだが、 引っ越した先が京都だという事を訊いて何とも言えない気分になった。

横浜を経由しているとは言え福島から来た家族がいじめられたりしないだろうか? 今思うとその家族が乗っている自動車のナンバーは福島ナンバーだったんだけど、 特に子供達がいじめや仲間はずれになっていないだろうか。 がれき受け入れの記事を読んで人ごとながら胸が痛くなる思いで一杯だ。

京都に限らず福島からの人々が虐められ迫害されているという記事や ニュースを見る度に本当に心が痛くなる。
彼ら・彼女らこそが一番の被害者なのに、 一番傷付き疲れ果て様々な迷惑や不便を味わっているのに、 何故そんな仕打ちを受けなければならないのだろうか。

「自分たちの子供のため」などと言って思考停止していないで、 足りないなら足りないなりにその頭で考えて欲しい。
本当に自分の子供の事を考えているのであれば、 親として自分の子供に見せるべきはどういう姿なのか、を。
よく言われているが子供は親の背中を見て育つのだ。
理由も根拠もなく他者を差別し迫害し虐げる親のその姿をね。

2012/02/22

C 中級者が意外と陥りやすいワナ

ある程度プログラミングにも慣れてきて 言語仕様もそこそこ理解した中級者にとって 意外と陥りやすくセキュリティホールの温床になりがちなワナ。
勿論オンラインマニュアルにも仕様として明記してあるのだが、 直感とは反する仕様のなので特に中級者にこれらのミスが多い気がする。

  1. strncpy(3)``\0'' 終端してくれない場合がある
  2. strcpy(3) はバッファオーバーフローの危険性があるから strncpy(3) を利用する様によく言われるが、 ここにワナが潜んでいる。

    strncpy(dst, src, len);

    とした時に 文字列 src の長さが len バイト未満の場合は dst``\0'' で終端される。
    終端どころか dst の残り領域は何故か 全て ``\0'' が詰められるという 無駄とも思われる謎仕様。
    ところが文字列 src の長さが len バイト以上の場合、 dstlen バイトだけコピーするという memmove(3) 同様の動作となってしまい、 dst``\0'' で終端されない。
    文字列操作の関数なのにも関わらず得られた結果が 文字列として扱えないという不思議な仕様なのだ。
    snprintf(3) などの様に len - 1 バイトを dst にコピーして ``\0'' で終端される事を期待していると 痛い目に遭ってしまう。

    文字列操作関数なので、 殆んどの場合 ``\0'' 終端が必要になると思うので 以下の様な処理が代替になる。

     strncpy(dst, src, sizeof(dst) - 1);
     *(dst + sizeof(dst) - 1) = '\0';
     			

  3. strncat(3) のサイズ指定はコピー先のサイズを指定するのではない
  4. strcat(3) も同様にバッファオーバーフローの危険性があるので strncat(3) を利用する様によく言われるが、 ここにもワナが潜んでいる。

    strncat(dst, src, len);

    とした時の len はコピーする src の長さであって、 前述の strncpy(3)snprintf(3)の様に dst のサイズ指定ではない ここで dst のサイズのつもりで len を指定すると バッファオーバーフローの原因になってしまう。

    殆んどの場合は dst の最大値が重要になると思うので 以下の処理が代替になる。

    strncat(dst, src, sizeof(dst) - strlen(src) - 1);
    			

  5. snprintf(3) は領域が重なってはいけない
  6. 前述した通りに使いやすいとは言いづらい strncpy(3)strncat(3) の代用として、 コピー先のサイズ指定が直感的な snprintf(3) を利用する場合も多いと思うが、 strncat(dst, src, len) の置き換えとして

    snprintf(dst, sizeof(dst), "%s%s", dst, src);

    とすると正しく動作しない(結果は処理系依存になる)。
    ANSI X3.159-1989 (ANSI C) や ISO/IEC 9899:1999 (ISO C99) 、 IEEE Std 1003.1-1988 (POSIX.1) 等をざっと探したけど 関連する記述は見つける事はできなかったが、 snprintf(3) は重なった領域では正しく動作しない様だ。

以上、C によるプログラミングに慣れた頃に陥りがちなワナなので 注意が必要だと思う。

2012/02/10

Happy Birthday My Dearest !

Image: img_0429.jpg

Happy! Happy!

You were perfect the day I met you, but you just get better every year.

2012/01/11

OWC SSD for MacBook Air

MacBook Air Late 2010 のディスク容量が逼迫してきていたので、 折からの円高に後押しされ(た事にして) OWC の Mercury Aura Pro Express を購入した。
MacBook Air はその薄さ故か、 通常の HDD タイプの SSD ではなく 独自な専用設計の SSD モジュールを使用している。 そのため汎用的な HDD タイプの SSD は利用できず、 Apple からは交換用の SSD モジュールは発売されていないのだが、 OWC では互換性のある交換用の SSD モジュールを販売しているので、 それらの SSD モジュールのうち 240GB のものを購入した。

国内でも OWC の SSD モジュールを扱っている販売店はあるのだろうが、 折から円高でもあるので OWC のサイトから普通に購入する。
取り外した純正の SSD モジュールを外付けディスクとして流用するための インタフェイスボックスを同時購入すると多少割引になるとの事なので、 折角なのでそれも同時に購入する事にする。
大体 1 ドル 88 円弱の換算となったので 送料を払った上でも国内で購入するよりも随分と格安にて購入できた。

交換自体は非常に簡単なので困ったり迷ったりする事もなく順調に作業完了。
OWC の Web Site でも 機種毎の交換作業の動画 が公開されていので 一通り閲覧しておけば作業の手順もつかめるので良いだろう。

  1. TimeMachineを利用したバックアップ

    今回は USB 接続の外付け HDD に TimeMachine を利用して バックアップを取得した。

  2. 裏蓋の取外し

    MacBook Air の裏蓋はペンタローブという 特殊な形式のネジが使用されているが、 SSD に同梱されている専用のドライバを利用すれば簡単に ネジをはずす事ができる。

    Image: RIMG0517.JPG

    裏蓋を外した状態

  3. SSD モジュールの取外し

    トルックスネジで固定されているオリジナルの SSD モジュールを これも同梱されてる専用のドライバを利用してゆるめると、 オリジナルの SSD は簡単にはずれる。

    Image: RIMG0518.JPG

    オリジナルの SSD モジュール

  4. SSD モジュールの組み付け

    OWC の SSD モジュールをオリジナル同様に差し込んで、 トルックスネジを締め付けて固定すれば完了。

    Image: RIMG0520.JPG

    OWC の SSD モジュール

  5. 裏蓋の組み付け

    取り外した裏蓋を組み付ける。 ネジは仮締めしておき、最後に対角線上のネジを 交互に本締めするのがコツ(というか基本ですね)。

  6. 環境の復元

    環境をバックアップした外付け HDD を接続した状態で MacBook Air に標準添付される USB メモリから起動。 自動で実行されるインストーラのバックアップから復元 を選択して新しい SSD に環境をレストアして作業完了。

環境の復元まで全て終了したら MacBook Air を起動すると、 無事に新しい SSD への移行が完了。
システムプロファイラシリアル ATA の項目を確認すると OWC Merucry Aura Pro Express SSD と表示されている。
但し、現在の Mac OS X (10.6.8) では社外品の SSD に対する trim 機能は無効とされてしまっているので、 システムプロファイラTRIM サポート項目は いいえと表示されている。
この状態でも勿論利用可能なのだが、 折角高速な SSD の機能を最大限に使うために Trim Enabler を利用して trim 機能を有効にして再起動する。
再度 システムプロファイラ 上で確認すると、 今度は TRIM サポートはいと表示されるので、 SSD の性能が最大限に活用できる気がして嬉しい。

2012/01/04

「今度ね…」

子供を育てていると、ついつい「今度ね」とか「後でね」とか言ってしまう。
大人にとってはその場しのぎで、次の瞬間には忘れてしまう様な事でも、 子供は「今度」や「後で」を楽しみに心待ちにしている事が多い。
そして結局「今度」や「後で」が来ない事を知ると落胆してしまう。
約束を守って貰えない親だと子供に思わせれるのは親として情けないと思うし、 約束を守って貰えない親だと思うしかない子供は不憫だと思う。

だから、自分の子供に「今度」や「後で」の約束をしたら、 必ずこちらから「今度」や「後で」を実現させてあげる様にしようと思うし、 なるべく「○○できたら」とか「○○の後で」と条件を明確にする様にしている。

極論かもしれないが、そういう細かな不信感の積み重ねで 大人になった時に約束は守らなくても良いと思う様な 責任感が希薄な人に育っていくのだとさえ思う。

だから軽々しい口約束は極力控えて約束したらちゃんと守るようにしないとと思う。

2012/01/01

A Happy New Year

新年
  明けまして
    おめでとう
      ございます

今年もよろしくお願いします。


Copyright © Mitzyuki IMAIZUMI 2008,2009. All rights reserved.