■ #!/usr/bin/C でワンライナーを書く
Kazuho@Cybozu Labs: C - a pseudo-interpreter of the C programming language
Perl や Ruby では、ワンライナーで処理が書けて便利です。でも、なぜか C では書くことができません。仕事上の都合で、小さな処理を C 言語で書く必要があったので、ワンライナーも書くことのできる C 言語のインタプリタ(?)を作ってみました。
面白そうなので、早速RPMパッケージを作ってみました。
* ソースRPM:
http://takesako.31tools.com/redhat/SRPMS/C-0.01-0.src.rpm
Red Hat系Linuxならインストールは以下のコマンド一発です。
# rpm -ivh http://takesako.31tools.com/redhat/RPMS/noarch/C-0.01-0.noarch.rpm
こんなことができるようになります。
[1] C言語でワンライナー:
C -e 'printf("hello world\n");'
[2] shebangでスクリプト実行:
#!/usr/bin/C printf("hello world\n");
処理させる内容によってはPerlの約100倍早く動作することもあるようです。
半分ジョークプログラムみたいですが、いや、これ、実は結構便利かも。。。
まだ tarball が揃っていないソフトウェアなので、specファイルはこんな感じになりました。
Summary: C - a peudo-interpreter of the C programming language Name: C Version: 0.01 Release: 0 License: GPL Group: Development/Languages Source0: http://labs.cybozu.co.jp/blog/kazuho/archives/C Source1: C.pod URL: http://labs.cybozu.co.jp/blog/kazuhoatwork/ Vendor: Cybozu Labs, Inc. Requires: perl, gcc AutoReqProv: no BuildArch: noarch BuildRoot: %{_tmppath}/%{name}-%{version}-root %description C (pronounced large-C) is a pseudo-interpreter of the C programming language. Without the need of compilation, developers can rapidly create scripts or write one-liners using the C programming language that runs at the native-code speed. %prep %build %install %{__rm} -rf $RPM_BUILD_ROOT %{__mkdir} -p -m 0755 $RPM_BUILD_ROOT%{_bindir} %{__install} -m 0755 %{SOURCE0} $RPM_BUILD_ROOT%{_bindir}/C %{__mkdir} -p -m 0755 $RPM_BUILD_ROOT%{_mandir}/man1 pod2man -s 1 -c "%{vendor}" -r "%{name}-%{version}" %{SOURCE1} \ > $RPM_BUILD_ROOT%{_mandir}/man1/%{name}.1 %{__chmod} 0644 $RPM_BUILD_ROOT%{_mandir}/man?/* %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) %{_bindir}/C %{_mandir}/man1/* %changelog * Fri Jan 6 2006 Cybozu Labs0.01-0 - Initial package.
PODファイルから pod2man で man1 のファイルを生成しているのがミソです。
そういえば、たまにspecファイルの書き方/勉強の仕方を聞かれることがあるのですが、紹介しやすい参考文献ってあまりないですよね。他人の(ディストリビュータが)書いたspecファイルを参考にするのが早いけど、変な癖がつくし。お勧めできるものとしては、ちょっと古いですが、Maximum RPMの13. Inside the Spec Fileあたりでしょうか。
■追記(2006-01-07)
Plamo Linux/TOWNS の nmaruichi さんから動作報告をいただきました。ありがとうございます。
RPMバイナリを Plamo Linux の rpm2tgz で変換することでインストールできました。
以下の100万回の単純なループで bash と比較すると 100 倍位になりました。
この /usr/bin/C って面白いですね。(変な比較をしてすいません)
bash-3.00# time C -e 'int counter;for(counter=0;counter<1000000;++counter) printf("%d\n", counter);' >/dev/null
real 0m0.614s
user 0m0.610s
sys 0m0.010s
bash-3.00# time for((counter=0;counter<1000000;++counter)) do printf "%d\n", $counter ; done >/dev/null
real 0m59.351s
user 0m57.790s
sys 0m0.770s
ちなにみ先にダウンロードしたのは、ソースだったんですが『中身を見て「これを元にビルドスクリプト」を書くのは、さらに意味が無いな…』って思ったので rpm2tgz にしたと言うのが、実際です。
まぁ、RPMバイナリが noarch だったんでなんとなくよめてました。
■追記(2006-01-09) /usr/bin/C をデバッガ(gdb)経由での使用
nmaruichiさんがブログのコメントにうまく書き込めなかったみたいなので原文を追記。
/usr/bin/C に実行ファイルを呼び出す段階で gdb 経由で呼び出すように修正し たコマンド "C.gdb" をつくって試してみました。 # なお、コマンド内で indent を使用しているのは、コンパイル前に成形してお いた方が分かりやすいかと思ったためです。 ---- $ C.gdb -c'g' -e "`echo 'int counter;for(counter=0;counter<10;++counter)printf("%d\n", counter)' | indent -kr`" GNU gdb 6.1.1 Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i586-pc-linux"...Using host libthread_db library "/lib/libthread_db.so.1". (gdb) list 1 #include2 #include 3 4 5 int main(int argc, char** argv) 6 { 7 #line 1 8 int counter; 9 for (counter = 0; counter < 10; ++counter) 10 printf("%d\n", counter); (gdb) list 11 return 0; 12 } (gdb) break 8 No line 8 in file "/tmp/filedCHG4R.c". (gdb) run Starting program: /tmp/filedCHG4R 0 1 2 3 4 5 6 7 8 9 Program exited normally. ---- 上記のテスト結果では、ブレークポイントを設定しているにも 係わらず。機能していないようです。 それで、list 表示の中にある #line のプリプロセッサが良く 分からないので、省くために /usr/bin/C.gdb から取り除いて みました。 修正済みのもので試すと。 ---- $ C.gdb -c'g' -e "`echo 'int counter;for(counter=0;counter<10;++counter)printf("%d\n", counter)' | indent -kr`" GNU gdb 6.1.1 Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i586-pc-linux"...Using host libthread_db library "/lib/libthread_db.so.1". (gdb) list 1 #include 2 #include 3 4 5 int main(int argc, char** argv) 6 { 7 int counter; 8 for (counter = 0; counter < 10; ++counter) 9 printf("%d\n", counter); 10 return 0; (gdb) list 11 } (gdb) break 7 Breakpoint 1 at 0x8048338: file /tmp/file9CUgWl.c, line 7. (gdb) run Starting program: /tmp/file9CUgWl Breakpoint 1, main (argc=1, argv=0xbffff834) at /tmp/file9CUgWl.c:8 8 for (counter = 0; counter < 10; ++counter) (gdb) step 9 printf("%d\n", counter); (gdb) step 0 8 for (counter = 0; counter < 10; ++counter) ---- この場合は、上記のようにブレークポイントも正常に動きました。 この「#line 1」の行は必要なのでしょうか? この行があると無いとでは、gcc -E でプリプロセッサの処理を通したソース コードにも違いが見られます。 なお、「#line 1」を省いた C.gdb にするための差分を以下のよう にして $ diff -u C{,.gdb} | gzip -c | uuencode -m C.gdb.diff.gz.uuencode 固めたものを載せておきます。 begin-base64 644 C.gdb.diff.gz.uuencode H4sIAKEDwEMAA32RXW+CMBSGr9df8Qa3BOUjxfmBIozFi93vYldLDJaKTaAS KItm2X9fQdRsS9YL2tPnHN5z3jqOg/XdmNKZQz2HzjCmS2++fJy69LJg0QWl xLIsrN0s3d6yfdDxckKXk9mf7DiGMx3bM1j66yOOCQiKE+7rim0UP6rgHB4a tdmJnCOEKkqZFOYwINagY/zI2YYVqWbXPA1/obqshFQ709Ct4aE27FvyMLhp 9iLGFbrM6LGQLG9SXrd40AdY1SoVB3cfvcsfd7nYdpd9cZwxtjmUqg66kRe+ PYflUaonP8+se0ORCGm2h6TKmA22T6rRqA0+hgSfxBnkQnJ4BO26OnQOK66a SoJqua9OwvPOGt5Cb60GPwpl3j8hiuAPIXbQ51UYdSUEA7ReNYoTpz7Vihfm 1QEb8fPry1tr+AVdfL0hsJwnsinbd0EqOIxdomtTqMPlz1B7joxLXiVKg62Q SXXSDvW9hCG84N82vwGKFyuXhQIAAA== ==== 上記の部分を「cat > C.gdb.diff.gz.uuencode」などとして(ペーストすること で)ファイルに保存して以下のコマンドで取り出すことが出来ます。 $ uudecode C.gdb.diff.gz.uuencode $ gzip -cd C.gdb.diff.gz
コメント
既に若干情報が古くなりつつありますけど
http://www.amazon.co.jp/exec/obidos/ASIN/4797324937
とか悪くないと思いますよ
#自分の書いた書籍は怖いので照会しない(うひひ
投稿者: ELF | 2006年01月07日 19:50
/usr/bin/C の起動時に設定ファイルとして ~/.Crc を読むようにするとか言うのはどうでしょうか?
ここに設定を書いておくと反映してくれるのであれば、手間が緩和される場合も出てくるかも知れません。
# 例えば、-L とか -I の長めのオプションを設定したい場合は、便利かも?
但し、それによって起動に時間がかかるようになるといけませんけど。
---
ちなみに私が言っていた「よめていた」は、「予想できた」と言う意味で使ったつもりでした。(適切な言葉ではありませんね > 私)
今回は、実際には rpm を読む必要は無かったので…。
投稿者: nmaruichi | 2006年01月08日 01:00
nmaruichi さんへ:
ご利用いただき、ありがとうございます m(__)m
デバッグ機能を本体に追加させていただきました。行数の処理も改善を図ったつもりですが、まだいまいちかもしれません。
設定ファイルを読む機能については、設定ファイルではなく、環境変数で対応しようと思います。
関連して、C-0.02 ではソースコード内に #option -cWall -lm のように書けるようになったので、だいぶ実用的になったと思います。
よろしくお願いします。
投稿者: kazuho | 2006年01月10日 16:22
まぁ、~/.Crc って言うのは、半分冗談です。
でも私自身は、「もし設定したい内容が肥大化したときに ~/.Crc って言う方法はいいのでは?」とは思っています。
ただ、この機能をつけたために重くなるのでは本末転倒なので、「export CRC=".Crc" みたいに環境変数で設定されているときに設定ファイルを読み込むと言う手法はどうかな?」と思っています。
…とはいえ今は、まだ必要ないと思います。
---
なお、使用するデバッガを環境変数で設定できた方がいいかも知れません。
それから、この「/usr/bin/C」ってコマンドのアイデアは、私個人的にも刺激的なもので、以前に私のプログラムについて takesako さんに提案された「案」を実現してみたくなるような面白いものでした。
この様なプログラムを見せてもらえるなんて、ありがとうございます。
投稿者: nmaruichi | 2006年01月11日 01:05