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 コーディング規約とは若干異なっている。
基本的に余計な空白はなるべく入れない様にしているので、 if、switch、case、for、 do、while の後ろに空白は入れない。 それ以外は 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 コーディング規約に賛成。
例えばループのインデックスに利用するローカル変数は i や j で十分だと思う。
基本的にローカル変数は単純な名前で十分意味が通じると思うし、 変数名を工夫して可読性を高める必要があるのであれば、 そもそも関数のサイズやロジックを見直す必要があると思う。
それから、変数名に長めの英単語や英単語の組み合わせを使う場合に 特に我々が気をつけたいのはスペルミスだと思う。 他人に見られる可能性があるならせめて正しいスペルにするべき。 変な名前の変数だと逆に意味不明になりかねないし、 そもそも見る方も見られる方も恥ずかしい。
ハンガリアン記法に反対している部分も大賛成で、 コンパイラ言語ではコンパイラが正しく判断してくれる情報を わざわざ変数名に付けても得られる事は殆どないと思う。 - 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 コーディング規約とは若干異なっていて、
switch と case はインデントさせている。
深い理由はないのだが何となく見やすいと思う。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 コーディング規約におおむね賛成。
if、switch、for、while、 do においてはブロックの開始行の行末に開きブレース、 ブロックの最終行の行頭に閉じブレースというスタイルで書いている。 関数定義のブレースについての書き方も同様に賛成で、 開きブレースは次の行の行頭に書いている。
ただし 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/02/22
C 中級者が意外と陥りやすいワナ
ある程度プログラミングにも慣れてきて
言語仕様もそこそこ理解した中級者にとって
意外と陥りやすくセキュリティホールの温床になりがちなワナ。
勿論オンラインマニュアルにも仕様として明記してあるのだが、
直感とは反する仕様のなので特に中級者にこれらのミスが多い気がする。
- strncpy(3) は ``\0'' 終端してくれない場合がある
- strncat(3) のサイズ指定はコピー先のサイズを指定するのではない
- snprintf(3) は領域が重なってはいけない
strcpy(3) はバッファオーバーフローの危険性があるから
strncpy(3) を利用する様によく言われるが、
ここにワナが潜んでいる。
strncpy(dst, src, len);
とした時に
文字列 src の長さが len バイト未満の場合は
dst は ``\0'' で終端される。
終端どころか dst の残り領域は何故か
全て ``\0'' が詰められるという
無駄とも思われる謎仕様。
ところが文字列 src の長さが len バイト以上の場合、
dst に len バイトだけコピーするという
memmove(3) 同様の動作となってしまい、
dst が ``\0'' で終端されない。
文字列操作の関数なのにも関わらず得られた結果が
文字列として扱えないという不思議な仕様なのだ。
snprintf(3) などの様に
len - 1 バイトを dst にコピーして
``\0'' で終端される事を期待していると
痛い目に遭ってしまう。
文字列操作関数なので、
殆んどの場合 ``\0'' 終端が必要になると思うので
以下の様な処理が代替になる。
strncpy(dst, src, sizeof(dst) - 1); *(dst + sizeof(dst) - 1) = '\0';
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);
前述した通りに使いやすいとは言いづらい 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) は重なった領域では正しく動作しない様だ。
2011/12/14
cvs や svn のリビジョン番号
プログラムを作成する際、
特にまだ未完成で頻繁に修正とコンパイルを繰り返している間は、
どのリビジョンのプログラムを実行しているのか知りたい事はよく有る。
そんな時に、例えば実行時オプションとして -v を指定すると
cvs や svn のリビジョン番号とコミットした日時が表示されると便利だ。
しかし、多くの場合において -v オプションを解析し実行するだろう
main 関数が含まれるファイルが最終修正されているとは限らない。
従って main 関数の含まれるファイル中に
static char *id = "$Id$";などと定義しておいて commit 時に $Id$ を展開したとしても、 必ずしも最新のリビジョンにはならない場合が多い。
そこで -v オプションのハンドラ部分では
static char *id = "revision: " REVISION ", " COMMITED ;と記述としておいて make 時に自動で REVISION と COMMITED を全てのソース/ヘッダファイル中の cvs や svn の $Id$ キーワードから取得して設定する方法を検討してみる。
build 時に必ず実行する必要がある処理なので Makefile に記述して、 make 中で自動的に実行してしまうのが楽。
: # 変数定義 PROGRAM = myprog OBJECTS = sub.o pthread.o common.o util.o HEADER = myprog.h const.h config.h # 全ソース/ヘッダ中の `$Id' 行から最大のリビジョンを取得する REVISION = $(shell awk '/\$$Id/{ if($$4 ~ /[[:digit:]]/) \ if(max < $$4) max = $$4 } END{ print max }' \ $(OBJECT:.o=.c) $(HEADER) $(PROGRAM).c) # 全ソース/ヘッダ中の `$Id' 行から最大のリビジョンのコミット日時を取得する COMMITED = $(shell awk '/\$$Id.*'$(REVISION)'/{ \ print $$5 " " $$6; exit }' \ $(OBJECT:.o=.c) $(HEADER) $(PROGRAM).c) # コンパイルオプション(REVISIONとCOMMITEDを定義) CFLAGS = -g -Wall -Wextra -Werror -Wno-unused-parameter -I.-O2 \ -DREVISION=\""$(REVISION)"\" -DCOMMITED=\""$(COMMITED)"\" : : # ターゲット(デフォルトターゲットは touch と $(PROGRAM)に依存 all: touch $(PROGRAM) # $(PROGRAM).o ファイルの削除($(PROGRAM).o ファイルは必ずコンパイルする) touch: rm $(@).o $(PROGRAM): $(OBJECTS) $(CC) $(CFLAGS) -o $(@) $^ $(OBJECTS): $(HEADER) :これでどのファイルを commit した場合でも 必ず最新のリビジョンとコミット日付が -v で表示される。
当初ターゲット touch は touch $(PROGRAM).c としていたが、
とある方よりの指摘で rm $(PROGRAM).o に変更した。
ご指摘感謝です > N.S. さま
2011/11/26
shell での while ループにおける問題
シェルスクリプトでコマンドの出力をループ処理する場合は
パイプ | を使用するのが一般的な手法だと思うが、
パイプ | を使うとループが別なプロセスとして実行されるので
ループ内部で設定したシェル変数がループ外部で参照できないという問題があり、
(存在するとすれば) shell script 業界では割と FAQ 的な問題だ。
何が問題かと言って posix で定義されていないから
シェルの実装に依存しているのが一番の問題だったりするのだろう。
この問題に対しては、シェルビルトインの exec (1) を利用して
ディスクリプタを複製して while ループの入力を標準入力にしてしまうのが
一番汎用的で柔軟性のある対応だと思うのだが、
シェルを愛する面々は
皆同じ様な苦労をしているのだ
と改めて認識した。
当初は一時ファイルを作成する方法を考えついたのだが、
自分的にはなるべく一時ファイルを使いたくなかったので
ファイルから入力する部分をバッククォートによるコマンド実行に置き換え等、
色々と試行錯誤した結果ヒアドキュメントを使う方法に落ち着いた。
# これでは正常に動作しなかった exec 3<&0 0<`${command}` while read line :
勿論一時ファイルを作成しても良いのだが、 ヒアドキュメントを利用する事で一時ファイルが不要になる分だけ 処理的に美しいかなと思う (完全に好みの問題)。
1#!/bin/sh 2# コマンドの出力をバッククォートしてヒアドキュメントとして使用する 3 4command="/path/to/command" 5count=0 6 7# exec 3<&0: 標準入力 (FD0) を FD3 に複製 8# exec 0<<EOF: ヒアドキュメントを標準入力として使用 9exec 3<&0 0<<EOF 10`${command}` 11EOF 12 13while read line 14do 15 : 16 count=`expr ${count} + 1` 17done 18 19# exec 0<&3: FD3 に複製した標準入力 (FD0) を復帰 20# exec 3<&-: FD3 をクローズ</i> 21exec 0<&3 3<&- 22 23echo ${count}
もう一点は子プロセスで親プロセスの変数値を変更する方法。
こちらはプロセスが別になってしまうとファイルを使うしかないので、
同じプロセス内で関数呼び出しとして対応する方法が汎用的ではないだろうか。
1#!/bin/sh 2# eval を利用して呼び元の変数を設定する 3 4default="デフォルト値" 5 6sub() 7{ 8 9 local _val 10 11 : 12 _val="設定したい値" 13 14 eval "${1}='${_val:-${default}}'" 15 16} 17 18value="元の値" 19 20sub value 21 22echo ${value}
ちなみに上のリンク元にある parent.sh の中の処理で
${tmp_file} の内容を for ループで eval しているが、
これはシェルビルトインの . もしくは source を利用して
. ${tmp_file} としても同一の結果が得られるので楽だと思う。
1#!/bin/sh 2 3g1="aa" 4g2="bb" 5 6tmp_file=`mktemp /tmp/ps.XXXXXX` 7export tmp_file 8 9./child.sh # child.sh で、上で設定した変数 g1, g2 の値を変更する 10 11# for v in `cat $tmp_file`; do 12# eval $v 13# done 14# ${tmp_file} の内容は name='value' 形式が保証されているので 15# ファイル自体を `.' コマンドで読み込むだけで 16# 行毎に eval を実行しなくてもシェル変数に代入された値が利用できる 17 18if [ -f ${tmp_file} ] 19then 20 . ${fmp_file} 21 rm -f ${tmp_file} 22fi 23 24echo $g1 #=> cc に変更された 25echo $g2 #=> dd に変更されたブログに直接コメントしたかったのだが、 はてなのアカウントが無いので申し訳ないがここで意見させて頂く。
2011/11/16
メモリリークチェッカー
c で daemon 動作するサーバを作っていると
malloc (3) などで動的に確保したメモリの free (3) 忘れによる
いわゆるメモリリークを防ぐ事が非常に重要になってくる。
この様なメモリリークを防止するために役立つ
様々なツールが世の中には沢山存在しており
それぞれ非常に有用ではあるのだが、
いざ利用しようと思うと割と面倒な作業が発生したり、
自分の求めている機能に対して明らかにオーバースペックだったりするので、
自分の用途に合わせて簡単に使える『俺様ツール』を作ってみた。
動作原理は簡単で malloc (3) や free (3) したアドレスを
ログに出力して、
後からログを解析してメモリの確保と解放が対になっていない部分を
抽出するだけである。
そのためにまずは元となるソースコードを修正して、
malloc (3) や free (3) を独自に作成した wrapper 関数に置換えて
確保、解放したアドレスをログに出力する機能を組込む必要があるのだが、
malloc (3) などはエラー処理を含めて共通化するために
あらかじめ wrapper 関数化されている場合が多いと思うので、
この部分はフォーマットを決めるだけで割りと簡単に追加できると思う。
今回は syslog (3) を利用して malloc (3)、strdup (3)、
free (3) で確保/解放するアドレスを出力した。
1/* 2 * malloc() の wrapper 3 */ 4void *mymalloc(size_t size) 5{ 6 7 void *p; 8 9 if(!(p = malloc(size))) 10 /* エラー処理 */; 11 syslog(LOG_DEBUG, "malloc: %p\n", p); 12 13 return(p); 14 15} 16 17/* 18 * strdup() の wrapper 19 */ 20char *mystrdup(const char *s) 21{ 22 23 char *p = NULL; 24 25 if(s){ 26 if((p = strdup(s))) 27 syslog(LOG_DEBUG, "strdup: %p\n", p); 28 else 29 /* エラー処理 */; 30 } 31 32 return(p); 33 34} 35 36/* 37 * free() の wrapper 38 */ 39void myfree(void *p) 40{ 41 42 if(p){ 43 free(p); 44 syslog(LOG_DEBUG, "free: %p\n", p); 45 } 46 47}
次に出力されたログの解析処理であるが、
確保したメモリのアドレスをキーとしたハッシュテーブルを利用したいので
入力行の解析やハッシュテーブルが簡単に利用できる言語として
今回はコマンドライン版の php (1) を利用してみた。
もちろん awk (1) を駆使したり perl (1) を利用しても問題ない。
1<?php 2 /* 3 * Copyright (c) 2011 Mitzyuki IMAIZUMI, All rights reserved. 4 * 5 * $Id: memcheck.php 1677 2011-10-12 06:58:03Z mitz $ 6 */ 7 8 /* 冗長指定 */ 9 if($argv[1] == "-v"){ 10 $verbose = 1; 11 array_shift($argv); 12 } 13 14 if($fp = fopen($argv[1], "r")){ 15 $line = 0; 16 while($buf = fgets($fp, 1024)){ 17 $buf = ltrim($buf); 18 $line++; 19 /* 20 * malloc か strdup か free を含む行の場合 21 * コマンドを配列 $p[0] に、アドレスを配列 $p[1] に格納する 22 */ 23 if(preg_match("/^(malloc|strdup|free):\s(.*)/", $buf, $p)) 24 /* 解放処理 */ 25 if($p[1] == "free") 26 /* 確保済みテーブルに解放アドレスが存在する場合 */ 27 if($alloc[$p[2]]){ 28 if($verbose) 29 printf("%s: % 8d -> % 8d\n", $p[2], $alloc[$p[2]], $line); 30 $alloc[$p[2]] = 0; 31 } 32 33 /* 確保済みテーブルに解放アドレスが存在しない場合 */ 34 else 35 printf("%s: unknown free at % 8d\n", $p[2], $line); 36 else 37 /* アドレスをキーとしたハッシュテーブルに行番号を格納 */ 38 $alloc[$p[2]] = $line; 39 40 } 41 fclose($fp); 42 43 foreach($alloc as $k => $v) 44 if($v) 45 printf("%s: NOT free (% 8d)\n", $k, $v); 46 } 47?>このスクリプトを実行する事により malloc(3) もしくは strdup(3) で確保したメモリが解放されていない場合、 もしくは確保していないメモリを解放した場合が簡単に発見可能である。
これらは自分が必要とする機能のみを簡単に実装したものであり、 例えば解放していない事を意図しているメモリまで警告されてしまう等、 汎用的に使える事を目指したツールでは勿論ない。 ただ、日頃発生しうる面倒な作業がちょっとの工夫で 多少なりとも楽になるだろう例の一つとして公開してみた次第である。
2011/11/28 追記
awk (1) 版の方が汎用的なので簡単に作成してみた。 処理内容は上記 php (1) 版と同じである。1#!/bin/sh 2 3if [ "${1}" = "-v" ] 4then 5 verbose=true 6 shift 7fi 8 9awk ' 10 /^(malloc|strdup)/{ 11 alloc[$4] = NR; 12 } 13 /^free/{ 14 if(alloc[$4]){ 15 if("'${verbose:-false}'" == "true") 16 printf("% 5d %s: % 5d\n", NR, $4, $alloc[$4]); 17 alloc[$4] = 0; 18 } 19 else 20 printf("% 5d %s: unknown free.\n", NR, $4); 21 } 22 END{ 23 for(i in alloc) 24 if(alloc[i]) 25 printf("% 5d %s: NOT free.\n", alloc[i], i); 26 } 27' ${1} | sort ${2}
2011/10/18
電源関係の小物たち
いつも鞄に入れて持ち歩いている電源達関係の小物
- Apple 純正の AC アダプタ 普段から MacBookAir を持ち歩いているので、 Apple 純正の AC アダプタは必須 (IBM のバンドでまとめてあるのは愛嬌です)。
- USB 電源アダプタ 携帯電話や BlueTooth 機器など USB から充電できる機器が増えているので、 100V コンセントから USB の 5V を出力するアダプタも何かと便利。
- HyperMac 特許の関係で入手が不可になった HyperMac とそのケーブル。 これは MacBookAir からは通常の電源に見えるので、 純正のエアラインアダプタとは異なり充電もできる優れもの。 このおかげでお世辞にも長いとは言えない MacBookAir の駆動時間が 飛躍的に向上出来るのでモバイルには必須とも言えるアイテム。
- Griffin の USB Mini-Cableset。
Apple も特許が大切なのは判るんだけど、 こういう素晴らしい商品はライセンスするとか対応を考えて欲しいと思う。
一般的な USB の A 端子コネクタを Mini-USB、Micro-USB、 そして iPod 用の端子に変換するためのケーブルセットなのだが、 短めのケーブルなので収納に困らず邪魔にもならない。 普段から一緒に持ち歩いている iPod や Jawbone の BlueTooth ヘッドセットなど、 殆どの携帯機器の接続や充電が可能になるので非常に便利。
2011/07/07
パイプコマンド中の終了ステータス取得
シェルスクリプトでは頻繁に複数のコマンドをパイプで連結して使用するが、
パイプの途中のコマンドの終了ステータスは通常では参照できない。
そこでリダイレクトを利用してパイプの途中のコマンドの
終了ステータスを取得する方法を考えてみる。
1exec 3>&1 2ret1=`{ { command1; echo $? 1>&3; } | command2; } 3>&1` 3ret2=$?exec を利用してあらかじめ FD3 を利用可能にしておき、 command1 の終了ステータスを FD3 に出力している。
command2 の実行後に FD3 の出力を FD1(標準出力) に変更しているので、 ret1 は標準出力から command1 の終了ステータスが取得できる。 command2 の終了ステータスはそのまま $? を参照して ret2 に格納される。