自己解凍・実行形式のシェルスクリプト2016年07月22日 11時45分08秒

シェルスクリプトを実行すると、自分で組み込まれたファイルを展開し実行する実装をしたことがある。一つのプログラムとしてまとめるのが困難で、いくつもの細かいプログラムを使って処理する必要があった。しかし、複数のファイルを各々インストールするのも手間が掛かりすぎるために、一つにまとめたのだ。

tar でファイルをまとめて、uuencode を使って文字に変換し、エディタで開いたりしても、問題ない形にする。実行するときは、自身に uudecode をかけて、まとめた分を展開し、処理を移行すれば良い。

-C を複数回使うと、付属の tar では動作が異なり GNU tar を使うのが一番移植性があった。二度目の -C が一度目からの相対位置になるか、プログラムが実行された位置を元にするのか等の違いがある。gtar は前回の位置を基準にする。

$ gtar cvf - program.pm -C ../../dir1 *.pm -C ../../dir2 *.pm | compress -c uuencode encoded.tar.Z >> driver.sh
driver.sh と言う名前のシェルスクリプトを使った。そして、driver.sh の方で行うのが展開作業。
mkdir $tmp
cat $0 | uudecode -o /dev/stdout | uncompress -c | ( cd $tmpdir ; tar xf - )

...
$tmp/program.pm
引数の処理などをしてから、$0 で自分自身を読み出す。uudecode は -o /dev/stdout の形がどの UNIX でも使えるようだ。そして、tar に入れておいた program.pm を呼び出して、プログラムの実行を移す。

AIX で purify2015年11月24日 17時38分14秒

AIX で purify を使う場合は実行ファイルに対して行う。Solaris 上とは違い、オブジェクトファイルには細工をしないようだ。
% purify -process-executables <program>
Purify 7.0.1.0-006 131211 AIX (32-bit L) (C) Copyright IBM Corporation 1992, 2013 All Rights Reserved.
Instrumenting: <program>
Done

AIX のリンカは 2GB 以上のファイルを扱えない2014年10月09日 11時40分31秒

AIX でリンカーが落ちる。

ld:open(): Value too large to be stored in data

で調べると、IBM IV58929 が出てきた。つまり、2GB 以上のファイルを扱えないとの事。

個々のオブジェクトだったら 2GB を越えることはないが、それらを集めた .a の書庫では越えてしまった。

どの UNIX でも ccyymmdd 形式で date を取得2014年07月19日 12時12分58秒

特に Solaris に入っている date が古くて、どのプラットフォームでも動くシェルスクリプトを書くのには気をつかう。Linux などは、/bin/sh といっても現実は bash であり、FreeBSD も ash、AIX などは ksh になっている。Solaris の sh は本当に最古参の物なので、変数を export する時にはしっかり改行しないといけなかったりする程。特に苦手だったのは date の出力の統一。

やっと一番好きな書式をどこでも出せるものを知った。


% date "+%Y%m%d"

で、四桁の西暦、二桁ずつの月日が出せる。

まずは、Solaris から、順次アクセス出来るプラットフォームで。


Solaris % /usr/bin/date "+%Y%m%d"
20140718
Solaris % /usr/xpg4/bin/date "+%Y%m%d"
20140718
AIX % date "+%Y%m%d"
20140718
FreeBSD % date "+%Y%m%d"
20140718
Liunx % date "+%Y%m%d"
20140718

whoami と id -un と cron2014年01月30日 14時06分46秒

whoami は現在利用中のユーザの名前を表示する。id -un と等価だが、コマンド名が示す通りの動作で覚えやすいので普段はこちらを使っている。

この度、corn にて whoami を使ったら末端に繋がっていないとエラーを出して、ユーザ名が得られなかった。そこで、id -un に交換である。AIX でしか、確認はしていないのだが…。

corn は普段から頻繁に更新するものではないので、あれこれ変わった動作を見付けては驚くことが多い。まず最初に誰もが当たるのが、PATH では無いだろうか。他にも、環境変数が色々と無かったり、末端では問題なく使っていたコマンドが動かなかったり。

-qlinedebug はあまり役にたたない2013年09月10日 12時31分51秒

AIX のコンパイラには -qlinedebug というオプションがある。デバッグオプションの -g とは別途用いる。

IBM のサイトをみると以下のように説明されている。

目的 デバッガー用に行番号およびソース・ファイル名の情報のみを生成する。 -qlinedebug が有効な場合、コンパイラーは最小限のデバッグ情報しか生成しないため、 結果として得られるオブジェクト・サイズは、 -g デバッグ・オプションを指定した場合に生成されるオブジェクトよりも 小さくなります。 デバッガーを使用してソース・コードをステップスルーすることができますが、変数情報を表示したり照会することはできません。 トレースバック・テーブルを生成させると、 行番号が組み込まれます。

つまり、デバッガで各々の行を実行できるが変数は見ることは出来ない。コードにも依るのだが、-qlinedebug-g に比べると、一割から二割程度のバイナリサイズを削減できる。なお、strip を使うと、七割から九割ぐらいのぜい肉をそぎ落とすことが出来る。実行ファイルのほとんどは余分な情報なのだ。

core ファイルを開くと、最後のトレースは見られるが変数が一切見られないので、確かに落ちた辺りは見られるが、既に痕跡は無くなっているのだ。core を調べる時に役に立つのがやはり、変数の値。最適化によって、直には見られなくても、大きな手がかりになるので、これが無くなってしまうのは痛い。

AIX では strip された実行ファイルからの core であっても、strip する前の実行ファイルを使えるらしい。もし、それが真実だとすると、linedebug を使うよりも strip した方が core は見やすいことになる。まだ、自身での経験はあまりないので、もっと見極める必要がある。

AIX のリンカーと動的ローダの癖2013年09月06日 12時27分41秒

AIX のリンカーと動的ローダは、機能的には Solaris のより上だと思うが、その分だけ癖があり、若干厄介な面もある。

具体的な利点としては、ライブラリをリンクする時の Solaris のリンクの単位がファイル毎名のに対し、AIX ではシンボル単位なのだ。Solaris は呼ばれない関数が一つのファイルにあったら、否応なしに全てが取り込まれる。AIX では必要な関数だけだ。

AIX では呼ばない関数をリンクしないので Solaris に比べて実行ファイルが大きくなりにくい。動的ライブラリも併せて行なったときに、その呼ばない関数の切り落としをやりすぎてしまうみたいだ。

AIX では実行ファイルをロードしている時に全てのシンボルを読み込もうとする。もし、プログラムの中に呼ばれないが、外部参照されている関数などがあると、実行できなくなる。リンクは出来るのに起動できないのだ。

リンク時に -blazy を渡すとこれらのシンボルも探すようになる。

-xs を用いずにデバッグシンボルを組み込むのを抑制する2013年08月04日 11時20分32秒

動作するプログラムを書くことを出来る人は大勢いるが、正しくコンパイルする事、効率的にコンパイル、リンクをする事が出来る人はほとんどいない。大部分の人達が、自分達が毎日、自分で何を行なっているかを把握していないのだ。知らず知らずのうちに、無駄を毎日の様に繰り返していることもしばしば。少しの変更で、大きな無駄を省けることも多々ある。一人で一割、二割の時間を有効に使えるようになるとしよう。それが、百人に及んだら事は重大だ。

Solaris には -xs というコンパイルオプションがある。これによりオブジェクトファイルなしに dbx でデバッグできるようになる。これを用いると、各々のオブジェクトファイルに入っているデバッグ情報が、実行ファイルへ全てコピーされるのだ。これが、リンクオプションで無いのは若干の不思議。

さて、このオプションを有効にするには -xs を指定する必要がある。これは、コードや中間生成物を共に移さない、本番系に有効なオプションだ。

つまり、このオプションを利用しないと、プログラムのサイズが大幅に小さくなる。元の一割やそれ以下の大きさにまでなってしまった等というのは日常茶飯事だ。

これを省くと、プログラムのリンクの時間が大幅に減る。デバッガの起動と再読み込みの時間も大きく減る。そして、ディスクの圧迫も回避となる。

もちろん、これを使わない不利益は、オブジェクトファイルが必要なこと。何かあった時に心配との事で、常に指定している組織も多い。

逆にいえば、一般的な開発環境では必要無い。そして、本番環境にオブジェクトファイルが無くても、core ファイルを持ってくれば、しっかりとデバッグが出来る。つまり、コードとオブジェクトファイルとしっかりと保持する運用体勢を確立していれば、用いなくても問題はない。または、本番用にのみ指定する運用でもいい。

成果物からは、弘法筆を選ばずの世界なのだが、その過程には、過ぎたるは猶及ばざるが如し、そして、労多くして功少なしの世界でもあるのだ。

なお、-xs は -xdebugformat=stabs を指定し、stabs 形式のデバッグ情報を用いる時に有効になる。-xdebugformat=dwarf で古い型の dwarf 形式を用いている場合には無意味だ。

AIX はある筋の情報によると stabs で、実行ファイル外にデバッグ情報を置くことは出来るが、dbx がオブジェクトファイルからその情報を読むことがまだ出来ない為、似たようなオプションは無いそうだ。

static 関数を extern "C"2013年02月23日 12時33分49秒

C++ はとても複雑だ。新しいことを見つける度に思う。 AIX の C++ コンパイラには -qlanglvl=staticstoreoverlinkage というオプションがある。 具体的に示すと、

extern "C" static void func(void*);

は文法的に間違った記述なのだそうだ。

extern "C"
{
    static void func(void*);
}

が由緒正しき記述なのだそうである。 注意しないと行けない点は、この関数が static なこと。この static が無い、extern "C" void func(void*); は正しい記述だ。

数々の本を漁っても extern "C" に触れているのがあればいい方で、その正しき文法の詳細を記述した本を見たことは無い。

sh -c へ引数を渡す2012年08月28日 15時14分44秒

sh -c を用いてファイルに書かずに、シェルスクリプトを実行出来る。Makefile などで、わざわざ別にファイルを作る程でもない時、csh 系のシェルを使っていてループを使いたい時など、幾つかの場面がある。

そのスクリプトに更に引数を渡すことも出来る。

ただ、厄介なのがその引数の扱われ方。sh -c でコマンドを渡した後に残ったものが、引数として扱われる。そして、面白いことに、一つ目の引数は $0 になり、それに順次続くといった動作になっている。


Solaris % sh -c 'echo $1' one two three
two
AIX % sh -c 'echo $1' one two three
two
FreeBSD % sh -c 'echo $1' one two three
two
linux% sh -c 'echo $1' one two three
two

通常のシェルスクリプト内では、$0 はプログラム自体で、$1 は一つ目の引数になる。sh -c の $1 は二つ目の引数だ。

あちこちのホストで試してみた。長いこと知らなかった。