真・標準出力に栓をする
うんうん。標準出力に栓をしたいことって、ありますよね!*1
記事で言及されているソートの結果やyesコマンドのように ある程度データが大きいときは大丈夫なのですが、残念ながら小さいデータの場合には期待通りに動きません。
通常、データを別のプロセスにパイプで渡すと、そのままでは一旦バッファに吸収されてしまいます。
実例を見るのが早いです。
#include <stdio.h> #include <fcntl.h> main() { int fd; fd = open("test1.dat", O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { return -1; } close(fd); printf("Hello\n"); fd = open("test2.dat", O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { return -1; } close(fd); return 0; }
副作用を起こすために、それぞれの段階でファイルを生成しています。
まずは sleep で実行してみます。
% gcc test.c % ls a.out test.c % ./a.out | sleep 10000 & # 実行 [1] 14439 3005 % ls # ↓ この時点で既に test2.dat まで生成されてしまってる a.out test.c test1.dat test2.dat % ps -p 14439 -p 3005 # ↓ sleep は居るけど、a.out は居ない PID TTY STAT TIME COMMAND 3005 pts/4 S 0:00.00 sleep 10000 % fg ./a.out | sleep 10000 ^C % ls a.out test.c test1.dat test2.dat %
確かに sleep は実行されていますが、a.out は先に終了しており、test2.dat が生成されてしまっています。
この現象は、printf によるパイプへの書き込みが、パイプのバッファサイズ(OSや設定に依存/通常4KB程度)以下の場合に起こります。 逆に printf のところで 4KB以上の書き込みを行うと、期待通りプロセスがブロックします。
これを、どんなデータ量でも printf のところで確実に停止させるには、次のテクニックが手軽で便利です。
% stty tostop # ラインディシプリンの設定 % rm test[12].dat % ls a.out test.c % ./a.out & # バックグラウンドで実行 [1] 15519 % ps -p 15519 PID TTY STAT TIME COMMAND 15519 pts/4 T 0:00.00 ./a.out [1] + Stopped (tty output) ./a.out % ls # ↓ test1.dat のところまで実行されてる a.out test.c test1.dat % fg # 再開 ./a.out Hello % ls # ↓ test2.dat が出てくる a.out test.c test1.dat test2.dat %
上記のように stty tostop
を発行してから、プロセスをバックグラウンドで実行するだけです。
標準出力先は通常の tty のままですが、ラインディシプリンによって最初の書き込みの直前に確実にプロセスを停止できます。
(よって tty上でしか効力を発揮できないため、スクリプトなどからは使えません…)
当たり前ですが、tostop が設定されていない通常の場合だと、以下のようになります。
% stty -tostop % rm test[12].dat % ls a.out test.c % ./a.out & [1] 19645 % Hello # ← 単なるバックグラウンド実行なので結果がすぐ出る ps -p 19645 # ↓ プロセスも居なくなってる PID TTY STAT TIME COMMAND [1] Done ./a.out % ls a.out test.c test1.dat test2.dat %
本当にニッチな技術ですが、覚えておくと何か*2に使えるかもしれません。
Oracle 8i で、無理やり JA16EUCTILDE (や JA16SJISTILDE) を使う方法
Oracle Database と戯れていると、チルダが文字化けするときがあります。 (Oracle に限らないけど)
例えば接続先のサーバのデータベース自体のキャラクタセットが JA16EUCTILDE とかになっているのに、クライアント側からは JA16EUC で接続する場合です。
こんな状況は、なにも考えずにサーバ側だけ Oracle 8i から Oracle 10g とかにアップグレードしてしまうと発生します。
(たぶん。詳しく知らない。誰だよ、やったやつ…)
こういう時はクライアント側でも同じ JA16EUCTILDE を使う必要があるのですが、残念ながら Oracle 8i には JA16EUCTILDE (や JA16SJISTILDE) がありません。
文字化けを解消したかったら、クライアントのバージョンを(9i以降に)上げるしか無いのです。
しかし現実には、クライアントのバージョンを上げることができませんでした…。
(データベース側のキャラクタセットを変更/調整するのは、もっとできない)
…そんな状況で見つけた黒魔術です。
ここでの環境は Oracle 8.1.7 です。
lx0boot.nlt
VERSION=2.1.0.0.0 CHARACTER_SET "JA16EUCTILDE" 837
lx20345.nlt
VERSION = 2.1.0.0.0 DEFINE character_set name = "JA16EUCTILDE" id = 837 base_char_set = 830 character_data = { 0xa1c1 : 0xff5e, } ENDDEFINE character_set
上記のファイルを作業ディレクトリに用意します。
さらに ${ORA_NLS33} にある lx0boot.nlb lx1boot.nlb lx2033e.nlb lx6033e.nlb を作業ディレクトリにコピーしてきます。
(逆に *.nlt を ${ORA_NLS33} に置いて、直接書き換えることもできるけど、怖いので…)
そしておもむろに以下のコマンドを実行します。
% lxinst oranls=. sysdir=. destdir=. NLS Data Installation Utility: Version 3.4.1.0.0 - Production Copyright (c) Oracle Corporation 1993, 1995, 1996, 1997, 1999, 2000. All rights reserved. CORE 8.1.7.0.0 Production Generating binary object and installation boot files... LXI-WARN-00510: In lx20345.nlt at line 10, unicode 0xff5e out of private use range LXI-WARN-00512: In lx20345.nlt at line 10, character 0xa1c1 is remapped Merging installation and system boot files... NLSRTL Object installation successfully completed
そしてできあがった lx0boot.nlb lx1boot.nlb lx20345.nlb lx60345.nlb を ${ORA_NLS33} に書き戻すと NLS_LANG で JA16EUCTILDE が使えるようになります。
(lx?boot.nlb は上書きされるので、バックアップはとっておきましょう)
同様にして JA16SJISTILDE も作れると思います。
自己責任でどうぞ。
vim でタブ文字と改行文字を見やすくする方法 (Unicode編)
簡単なんだけど、よく忘れるのでメモ。
まず、端末が UTF-8 を表示できることが前提。
~/.vimrc に以下のように記載。
set encoding=utf-8 scriptencoding utf-8 set listchars=tab:→\ ,eol:↲ set list " お好みに応じて、適切な場所で色設定 hi SpecialKey ctermfg=237 guifg=#3a3a3a hi NonText ctermfg=66 guifg=#5f8787
「→」と「↲」は、INSERTモード中にそれぞれ <C-v>uffeb
および <C-v>u21b2
で入力できる。
これの難点は、コピペするときに改行文字とかが混入すること、かな。
コピーする前に :set nolist
で消せば、だいじょーぶ。
ちなみに EUC-JP に(そんなもの無いけど無理やり)外字として「→」と「↲」を登録しておいて、iconv 自体を改造して UTF-8 とのマッピングを定義してあげれば set termencoding=euc-jp
でも動くよ。