- 名前
- 説明
- 出力ファイルハンドルを flush/unbufferするには? なぜ私はこれを やらなければならないのでしょうか?
- ファイルの一行を変更する/ファイルのある行を削除する/ ファイルの中程で一行挿入する/ファイルの先頭に追加するには?
- あるファイルの行数を数えるには?
- 一時ファイルの名前を作り出すには?
- どうやれば固定長レコードのファイルを操作できますか?
- どうすればファイルハンドルをサブルーチンに局所化できますか? サブルーチンにファイルハンドルを渡すには? ファイルハンドルの配列を作るには?
- どうすればファイルハンドルを間接的に扱えますか?
- write()と一緒に使うフッターのフォーマットのセットアップはどうやればできますか?
- 文字列に対してwrite()するのはどうやればできますか?
- 出力する数字にカンマを付加するのはどうやればできますか?
- どうやれば ファイル名の中にあるチルダ (~)を変換できますか?
- ファイルを読み書きモードでオープンしたときに内容をクリアしてしまうのはなぜ?
- なぜ <*> を使ったときに "Argument list too long" (引数リストが長すぎる)と なることがあるの?
- glob()に leak/bugはあるの?
- どうやれば 名前の先頭に">"があったり 末尾に空白があるようなファイルをオープンすることができますか?
- どうすれば信頼性のあるファイルのリネームができますか?
- どうすればファイルをロックできますか?
- なぜ単に open(FH, ">file.lock")とできないの?
- まだロックができない。ただ単にファイルにある数値をインクリメントしたいだけなんだけど。 どうすればいいの?
- バイナリファイルをランダムに更新するには?
- perlでファイルのタイムスタンプを取得するには?
- perlでファイルのタイムスタンプを設定するには?
- 一度に二つ以上のファイルに出力するには?
- ファイル全体を一度に読みこむにはどうすればいいですか?
- どうすればファイルをパラグラフ毎に読み出すことができますか?
- どうすればファイルからキャラクターを一つだけ読み出せますか?キーボードからは?
- ファイルハンドルがあったときにそれがキャラクターを待っているかどうかを 知るには?
- How do I do a
tail -f
in perl? - Perl でファイルハンドルの dup() をするには?
- 数値によるファイルディスクリプターをクローズするには?
- なぜ DOS のパスで "C:\temp\foo" が使えないのでしょうか? なぜ `C:\temp\foo.exe` はうまくいかないの?
- なぜ glob("*.*")で全てのファイルを得られないのでしょうか?
- Why does Perl let me delete read-only files? Why does
-i
clobber protected files? Isn't this a bug in Perl? - あるファイルからランダムに行を選択するには?
- 行の配列を出力したときになぜ余計なスペースがつくのでしょうか?
- AUTHOR AND COPYRIGHT
- POD ERRORS
名前¶
perlfaq5 - Files and Formats ($Revision$, $Date$)
perlfaq5 - ファイルとフォーマット ($Revision$, $Date$)
説明¶
このセクションでは、入出力と“f”に関する事柄: ファイルハンドル (filehandle)、フラッシング(flushing)、フォーマット(format)、 フッター(footer)を扱います。
出力ファイルハンドルを flush/unbufferするには? なぜ私はこれを やらなければならないのでしょうか?¶
Cの標準入出力ライブラリ(stdio)は、通常はキャラクターをバッファリングして デバイスへ送出します。これは効率上の理由で行われるので、 バイト毎にシステムコールを行ったりはしません。Perlでprint()や write()を使った場合、それは出力バッファを通ることになります。 syswrite()はstdioとバッファリングを回避します。
大部分のstdioの実装では、バッファリングの方式とバッファのサイズは デバイスの型に従ったものになっています。ディスクファイルはブロック バッファリングされ、たいていの場合バッファサイズは2キロバイト以上です。 パイプやソケットはほとんどの場合、0.5キロバイトから2キロバイトの間の バッファでバッファリングされます。 シリアルデバイス(モデムや端末など)は通常はラインバッファリングされ、 stdioは改行を受け取ったときに行全体を送出します。
Perlは本当のバッファリングなし出力をサポートしてはいません (syswrite(OUT, $char, 1)
のようにできることは除きます)。 Perlはその代わりに、“コマンドバッファリング”をサポートしています。 これは各コマンドの出力の後で物理的な書き出しが行われるというものです。 これはバッファリングなしにするよりも難しくはありませんが、 望んだとき、望んだ場所で出力が行われます。
デバイスに出力したときにそのキャラクタが反映されるようにしたいのなら、 そのハンドルを自動フラッシュするようにしたいでしょう。 自動フラッシュを制御するためにselect()と変数$|
を使います (perlvar/$ と "select" in perlfuncを参照してください):
$old_fh = select(OUTPUT_HANDLE);
$| = 1;
select($old_fh);
もしくは伝統的なイディオムを使って以下のようにします:
select((select(OUTPUT_HANDLE), $| = 1)[0]);
Or if don't mind slowly loading several thousand lines of module code just because you're afraid of the $|
variable:
$|
という変数を敬遠したい場合、数千行のモジュールを ロードして遅くなることを気にしないのなら以下のようにできます:
use FileHandle;
open(DEV, "+</dev/tty"); # ceci n'est pas une pipe
DEV->autoflush(1);
新しい IO::*モジュールでは:
use IO::Handle;
open(DEV, ">/dev/printer"); # しかしこれは?
DEV->autoflush(1);
こうもできます:
use IO::Socket; # これはパイプの類かい?
$sock = IO::Socket::INET->new(PeerAddr => 'www.perl.com',
PeerPort => 'http(80)',
Proto => 'tcp');
die "$!" unless $sock;
$sock->autoflush();
print $sock "GET / HTTP/1.0" . "\015\012" x 2;
$document = join('', <$sock>);
print "DOC IS: $document\n";
Note the bizarrely hardcoded carriage return and newline in their octal equivalents. This is the ONLY way (currently) to assure a proper flush on all platforms, including Macintosh. That the way things work in network programming: you really should specify the exact bit pattern on the network line terminator. In practice, "\n\n"
often works, but this is not portable.
八進表記で記述されているキャリッジリターンと改行に注意してください。 これは、Macintoshを含め全てのプラットフォームで適切にフラッシュを 行うための(現時点では)唯一の方法なのです。これはネットワーク プログラミングで動作させるための方法で、ネットワークでの 行終端子の実際のビットパターンで指定すべきなのです。"\n\n"
は ほとんどの場合はうまくいきますが、これは移植性に欠けるところが あります。
webを通じてURLの内容を取り込む例はperlfaq9を参照してください。
ファイルの一行を変更する/ファイルのある行を削除する/ ファイルの中程で一行挿入する/ファイルの先頭に追加するには?¶
これらの操作はテキストエディターの仕事です。Perlはテキストエディターではなく、 プログラミング言語です。あなたは問題をより低レベルな read、write、open、close、seekに分解する必要があります。
人はテキストファイルをトランプやパンチカードの山のように操作できる行の 並びのように簡単に考えてしまいがちですが、通常コンピューターは、 テキストファイルをバイトの並びと見ます。 一般的には、Perlにファイルの特定の行にシークさせるとか、 テキストをファイルに挿入させるとかテキストを取り除かせるような 直接的な方法はありません。
(There are exceptions in special circumstances. You can add or remove data at the very end of the file. A sequence of bytes can be replaced with another sequence of the same length. The $DB_RECNO
array bindings as documented in DB_File also provide a direct way of modifying a file. Files where all lines are the same length are also easy to alter.)
(例外となる特別な状況があります。 ファイルの一番最後の部分ではデータの追加や削除ができます。 あるバイト列を同じ長さをの別の バイト列で置き換えることができます。 さらにDB_Fileで説明されているような$DB_RECNO
配列割り付けも ファイルを修正する直接の方法を提供します。 全ての行が同じ長さを持っているファイルも簡単に変更できます。)
一般的な解決策は、行いたい変更を行ったテキストファイルのコピーを作り、 その後でオリジナルを上書きしてしまうというものです。 ここではロッキングはしないと仮定します。
$old = $file;
$new = "$file.tmp.$$";
$bak = "$file.orig";
open(OLD, "< $old") or die "can't open $old: $!";
open(NEW, "> $new") or die "can't open $new: $!";
# Correct typos, preserving case
while (<OLD>) {
s/\b(p)earl\b/${1}erl/i;
(print NEW $_) or die "can't write to $new: $!";
}
close(OLD) or die "can't close $old: $!";
close(NEW) or die "can't close $new: $!";
rename($old, $bak) or die "can't rename $old to $bak: $!";
rename($new, $old) or die "can't rename $new to $old: $!";
Perl はこの類のことをコマンドラインスイッチ -i
か、密接に関連した変数 $^I
を使って自動的に行うことができます。 -i
は一部の非UNIXシステムでは拡張子を要求することに注意してください。 これは使っている移植に付属していたドキュメントを参照してください。
# コマンドラインにあるテストの並びをリナンバリングする
perl -pi -e 's/(^\s+test\s+)\d+/ $1 . ++$count /e' t/op/taint.t
# スクリプトを形成する
local($^I, @ARGV) = ('.orig', glob("*.c"));
while (<>) {
if ($. == 1) {
print "This line should appear at the top of each file\n";
}
s/\b(p)earl\b/${1}erl/i; # 打ち間違いを(大小文字を無視して)訂正する
print;
close ARGV if eof; # $. をリセットする
}
あまり変更しないファイルの任意の行にシークする必要があるのなら、 そのファイルの行末のバイト位置のインデクスを構築することができます。 ファイルが大きいのであれば、十行毎、百行毎にインデクスを作れば シークと読み込みの効率を向上させることができるでしょう。 ファイルの内容がソートされているのであれば、look.plライブラリ(標準Perl 配布キットの一部です)を試してみてください。
ファイルの終端にある行の削除は得意なケースで、tell()やtruncate()が使えます。 以下のコードはあるファイルの最後にある行を、 コピーを作ったりファイル全体をメモリに読み込むことなしに削除するものです:
open (FH, "+< $file");
while ( <FH> ) { $addr = tell(FH) unless eof(FH) }
truncate(FH, $addr);
エラーチェックは読者の練習のために残されています。
あるファイルの行数を数えるには?¶
非常に効率の良いやり方の一つはファイルの改行の数を数えるというものです。 以下のプログラムはperlopで説明されているようなtr///の機能を使っています。 テキストファイルが改行で終わっていなければ、 それは適切なテキストファイルではありません。 そのため、期待しているよりも一少ない行数を報告するでしょう。
$lines = 0;
open(FILE, $filename) or die "Can't open `$filename': $!";
while (sysread FILE, $buffer, 4096) {
$lines += ($buffer =~ tr/\n//);
}
close FILE;
これは改行に絡む妙な動作がないと仮定しています。
一時ファイルの名前を作り出すには?¶
Use the new_tmpfile
class method from the IO::File module to get a filehandle opened for reading and writing. Use it if you don't need to know the file's name:
IO::Fileモジュールにあるクラスメソッドnew_tmpfile
を使って 読み書きのためにオープンされたファイルハンドルを取得します。 ファイルがどんな名前なのかを知る必要がない場合はこれを使ってください。
use IO::File;
$fh = IO::File->new_tmpfile()
or die "Unable to make new temporary file: $!";
ファイル名を知る必要がある場合には、 POSIXモジュールのtmpnam
関数を使って 自分でオープンするためのファイル名を取得することができます。
use Fcntl;
use POSIX qw(tmpnam);
#存在しないファイルが見つかるまで新しい一時名で試す。
#チェックは不要かも知れないが、用心するに越したことはない。
do { $name = tmpnam() }
until sysopen(FH, $name, O_RDWR|O_CREAT|O_EXCL);
#atexitスタイルのハンドラーをインストールするので、exitやdie
#したときに自動的にテンポラリファイルを削除する
END { unlink($name) or die "Couldn't unlink $name : $!" }
# ファイルを実際に使用する…
一時ファイルの作成を手作業で行いたいのなら、 プロセスIDやその時点での時刻(あるいはこれら両方)を使用してください。 一つのプロセスで複数の一時ファイルを使用するのであれば、 カウンターを使用しましょう:
BEGIN {
use Fcntl;
my $temp_dir = -d '/tmp' ? '/tmp' : $ENV{TMP} || $ENV{TEMP};
my $base_name = sprintf("%s/%d-%d-0000", $temp_dir, $$, time());
sub temp_file {
local *FH;
my $count = 0;
until (defined(fileno(FH)) || $count++ > 100) {
$base_name =~ s/-(\d+)$/"-" . (1 + $1)/e;
sysopen(FH, $base_name, O_WRONLY|O_EXCL|O_CREAT);
}
if (defined(fileno(FH))
return (*FH, $base_name);
} else {
return ();
}
}
}
どうやれば固定長レコードのファイルを操作できますか?¶
The most efficient way is using pack() and unpack(). This is faster than using substr() when take many, many strings. It is slower for just a few.
最も効率的なやり方はpack()とunpack()を使うものです。 これは文字列が大量にあるときにはsubstr()を使うよりも高速で、 速度低下もほとんどありません。
以下に挙げたのは幾つかの固定フォーマットをした入力行に対して 分解を行ったり書き戻しをするコード片の例で、出力は Berkeley形式のpsに準じています。
# 入力行のサンプル
# 15158 p5 T 0:00 perl /home/tchrist/scripts/now-what
$PS_T = 'A6 A4 A7 A5 A*';
open(PS, "ps|");
print scalar <PS>;
while (<PS>) {
($pid, $tt, $stat, $time, $command) = unpack($PS_T, $_);
for $var (qw!pid tt stat time command!) {
print "$var: <$$var>\n";
}
print 'line=', pack($PS_T, $pid, $tt, $stat, $time, $command),
"\n";
}
We've used $$var
in a way that forbidden by use strict 'refs'
. That is, we've promoted a string to a scalar variable reference using symbolic references. This is ok in small programs, but doesn't scale well. It also only works on global variables, not lexicals.
この例のような$$var
の使い方はuse strict 'refs'
したときには 使えないものです。 つまり、ここではシンボリックリファレンスを使って文字列をスカラー変数に するということを行っているのです。 これは小規模なプログラムには良いでしょうが、スケーラビリティが あるとは言えません。シンボリックリファレンスは大域変数でのみ働き、 レキシカル変数では使えません。
どうすればファイルハンドルをサブルーチンに局所化できますか? サブルーチンにファイルハンドルを渡すには? ファイルハンドルの配列を作るには?¶
最も早くてかつ単純で、 最も直接なやり方はファイルハンドルの型グロブを局所化するというものです:
local *TmpHandle;
Typeglobs are fast (especially compared with the alternatives) and reasonably easy to use, but they also have one subtle drawback. If you had, for example, a function named TmpHandle(), or a variable named %TmpHandle, you just hid it from yourself.
型グロブは(別の手段と比較した場合)高速で、使いやすいものではありますが、 分かりづらい欠点があります。 例えば、TmpHandle()という名前の関数や%TmpHandleという名前の変数を使ったときに、 あなたは自分自身でその名前を隠してしまうことになるのです。
sub findme {
local *HostFile;
open(HostFile, "</etc/hosts") or die "no /etc/hosts: $!";
local $_; # ← とても重要
while (<HostFile>) {
print if /\b127\.(0\.0\.)?1\b/;
}
# *HostFile はここで自動的にクローズと解放が行われる
}
Here's how to use typeglobs in a loop to open and store a bunch of filehandles. We'll use as values of the hash an ordered pair to make it easy to sort the hash in insertion order.
以下の例は、ループの中で複数のファイルハンドルをオープンしストアした場合の 型グロブの使い方です。 ここではハッシュの値を、ハッシュのソートがやりやすいように 順序付きペアとしています。
@names = qw(motd termcap passwd hosts);
my $i = 0;
foreach $filename (@names) {
local *FH;
open(FH, "/etc/$filename") || die "$filename: $!";
$file{$filename} = [ $i++, *FH ];
}
#配列にあるファイルハンドルを使う
foreach $name (sort { $file{$a}[0] <=> $file{$b}[0] } keys %file) {
my $fh = $file{$name}[1];
my $line = <$fh>;
print "$name $. $line";
}
関数に対してファイルハンドルを渡すのにもっとも簡単なやり方は それにアスタリスクをつけてfunc(*STDIN)のようにすることです。 詳しくは"Passing Filehandles" in perlfaq7を参照してください。
If you want to create many, anonymous handles, you should check out the Symbol, FileHandle, or IO::Handle (etc.) modules. Here's the equivalent code with Symbol::gensym, which is reasonably light-weight:
大量の無名ハンドルを作りたいのであれば、 Symbol, FileHandle, IO::Handle といったモジュールを参照してください。 以下の例は使いやすく軽い処理であるSymbol::gensymを使ったものです:
foreach $filename (@names) {
use Symbol;
my $fh = gensym();
open($fh, "/etc/$filename") || die "open /etc/$filename: $!";
$file{$filename} = [ $i++, $fh ];
}
use FileHandle;
foreach $filename (@names) {
push(@files, $fh);
my $fh = FileHandle->new("/etc/$filename") or die "$filename: $!";
$file{$filename} = [ $i++, $fh ];
}
Please understand that whether the filehandle happens to be a (probably localized) typeglob or an anonymous handle from one of the modules in no way affects the bizarre rules for managing indirect handles. See the next question.
ファイルハンドルが(おそらくは局所化された)型グロブになるかモジュールを使って 作成した無名ハンドルのどちらであるかを理解してください。 間接的なハンドルを管理するのに奇妙なルールが影響を及ぼすことはありません。 詳しくは次の質問を参照してください。
どうすればファイルハンドルを間接的に扱えますか?¶
An indirect filehandle is using something other than a symbol in a place that a filehandle is expected. Here are ways to get indirect filehandles:
間接ファイルハンドルは、あるファイルハンドルを期待している場所に置かれた シンボル以外のなにかを使っています。 以下に間接ファイルハンドルの例を挙げます:
$fh = SOME_FH; # bareword は strict-subs に反します
$fh = "SOME_FH"; # strict-refs hostile; 同じパッケージのみ
$fh = *SOME_FH; # 型グロブ
$fh = \*SOME_FH; # 型グロブのリファレンス (bless-able)
$fh = *SOME_FH{IO}; # blessed IO::Handle from *SOME_FH typeglob
Or, you can use the new
method from the FileHandle or IO modules to create an anonymous filehandle, store that in a scalar variable, and use it as though it were a normal filehandle.
あるいは、FileHandleモジュールやIOモジュールのnew
メソッドを使い、 それをスカラー変数に格納してからそれを普通のファイルハンドルと 同じように扱います。
use FileHandle;
$fh = FileHandle->new();
use IO::Handle; # 5.004以降
$fh = IO::Handle->new();
Then use any of those as you would a normal filehandle. Anywhere that Perl is expecting a filehandle, an indirect filehandle may be used instead. An indirect filehandle is just a scalar variable that contains a filehandle. Functions like print
, open
, seek
, or the functions or the <FH>
diamond operator will accept either a read filehandle or a scalar variable containing one:
上記のように作成して、後は普通のファイルハンドルと同じように使います。 Perlがファイルハンドルを期待しているところではどこでも 間接ファイルハンドルを使うことができるでしょう。 間接ファイルハンドルは、ファイルハンドルを保持しているスカラー変数に すぎません。print
, open
, seek
のような関数や、 ダイヤモンド演算子 <FH>
はファイルハンドルもファイルハンドルを 保持しているスカラー変数の両方とも受け付けます。
($ifh, $ofh, $efh) = (*STDIN, *STDOUT, *STDERR);
print $ofh "Type it: ";
$got = <$ifh>
print $efh "What was that: $got";
ファイルハンドルを関数に渡した場合、渡される関数は二種類の書き方ができます:
sub accept_fh {
my $fh = shift;
print $fh "Sending to indirect filehandle\n";
}
型グロブを局所化して、ファイルハンドルを直接使うことも可能です:
# ファイルハンドルをサブルーチンに渡す
sub accept_fh {
local *FH = shift;
print FH "Sending to localized filehandle\n";
}
両方の形式とも、オブジェクトでも実際のファイルハンドルの型グロブでも動作します (ある場合においては文字列でも可能ですが、これはちょっとリスクがあります)。
accept_fh(*STDOUT);
accept_fh($handle);
In the examples above, we assigned the filehandle to a scalar variable before using it. That is because only simple scalar variables, not expressions or subscripts of hashes or arrays, can be used with built-ins like print
, printf
, or the diamond operator. Using something other than a simple scalar varaible as a filehandle is illegal and won't even compile:
上の例では、ファイルハンドルを使う前にそれをスカラー変数に代入していました。 これは式でもなく、ハッシュや配列の添え字でもなく単純スカラ変数だけが print
やprintf
、ダイヤモンド演算子と一緒に使えるからです。 単純スカラ変数以外のものをファイルハンドルとして使うのは不正であり、 コンパイル時にエラーとなります:
@fd = (*STDIN, *STDOUT, *STDERR);
print $fd[1] "Type it: "; # 間違い
$got = <$fd[0]> # 間違い
print $fd[2] "What was that: $got"; # 間違い
print
やprintf
の場合には、ブロックを使ってその中にファイルハンドルを 含む式を置くことによって対処することができます:
print { $fd[1] } "funny stuff\n";
printf { $fd[1] } "Pity the poor %x.\n", 3_735_928_559;
# Pity the poor deadbeef.
これらのブロックは妥当なものですから、より複雑なコードをその中に 入れこむことができます。 以下の例はメッセージを二ヶ所のどちらかひとつに送り出します:
$ok = -x "/bin/cat";
print { $ok ? $fd[1] : $fd[2] } "cat stat $ok\n";
print { $fd[ 1+ ($ok || 0) ] } "cat stat $ok\n";
This approach of treating print
and printf
like object methods calls doesn't work for the diamond operator. That's because it's a real operator, not just a function with a comma-less argument. Assuming you've been storing typeglobs in your structure as we did above, you can use the built-in function named readline
to reads a record just as <>
does. Given the initialization shown above for @fd, this would work, but only because readline() require a typeglob. It doesn't work with objects or strings, which might be a bug we haven't fixed yet.
このアプローチはprint
やprintf
をオブジェクトメソッドの呼び出しのように 扱うものですが、これはダイヤモンド演算子には使えません。 なぜなら、ダイヤモンド演算子はカンマなしの引数を取る関数ではなくて本当の 演算子だからです。 さて、上記の例のように型グロブをあなたの作った構造に格納したとしましょう。 readline
という名前の組み込み関数を使って<>
が行うように レコードを読み込むことができます。 @fdを例にあったように初期化してやることでうまく動作します。 しかし、readline()は型グロブを要求するからです。 これはオブジェクトや文字列では動作しません。 このことはバグとも言えるもので現時点では修正されていません。
$got = readline($fd[0]);
Let it be noted that the flakiness of indirect filehandles is not related to whether they're strings, typeglobs, objects, or anything else. It's the syntax of the fundamental operators. Playing the object game doesn't help you at all here.
間接ファイルハンドルの妙な点はそれが文字列であるか、型グロブであるか、 オブジェクトであるか、はたまた別の何者であるかには関係しないということに 注意してください。 これは基本的な演算子の構文なのです。 オブジェクトをいじくりまわして遊ぶことはここでは何の助けにもなりません。
write()と一緒に使うフッターのフォーマットのセットアップはどうやればできますか?¶
これを行うための組み込みの方法はありません。 しかしperlfromには、これを可能にするためのニ、三の intrepid hacker向けの テクニックがあります。
文字列に対してwrite()するのはどうやればできますか?¶
"Accessing Formatting Internals" in perlformの swrite() 関数を参照してください。
出力する数字にカンマを付加するのはどうやればできますか?¶
一つのやり方:
sub commify {
local $_ = shift;
1 while s/^([-+]?\d+)(\d{3})/$1,$2/;
return $_;
}
$n = 23659019423.2331;
print "GOT: ", commify($n), "\n";
GOT: 23,659,019,423.2331
これを単に
s/^([-+]?\d+)(\d{3})/$1,$2/g;
のようにはできません。なぜなら、カンマを押し込んだ後で位置の再計算を 行わなければならないからです。
Alternatively, this code commifies all numbers in a line regardless of whether they have decimal portions, are preceded by + or -, or whatever:
以下の例は、小数点があるかどうか、+ や ー が先行しているかどうかに関係なく 行にある全ての数値を受け付けます:
# Andrew Johnson <ajohnson@gpu.srv.ualberta.ca> による
sub commify {
my $input = shift;
$input = reverse $input;
$input =~ s<(\d\d\d)(?=\d)(?!\d*\.)><$1,>g;
return scalar reverse $input;
}
どうやれば ファイル名の中にあるチルダ (~)を変換できますか?¶
Use the <> (glob()) operator, documented in perlfunc. Older versions of Perl require that you have a shell installed that groks tildes. Recent perl versions have this feature built in. The Glob::KGlob module (available from CPAN) gives more portable glob functionality.
perlfuncで説明されている <> (glob()) を使います。 古いバージョンの Perl では、チルダを展開するシェルが既に インストールされていることを要求します。 最近のバージョンの Perl は内部にこの機能を持っています。 Glob::KGlob モジュール(CPANで入手可能)はより移植性のあるglob機能を提供します。
Perl を使えば、これを以下のように直接的に行えます。
$filename =~ s{
^ ~ # 先行するチルダを探す
( # それを $1 に保存する
[^/] # スラッシュ以外のキャラクターの
* # 0回以上のくり返し(0にも意味がある)
)
}{
$1
? (getpwnam($1))[7]
: ( $ENV{HOME} || $ENV{LOGDIR} )
}ex;
ファイルを読み書きモードでオープンしたときに内容をクリアしてしまうのはなぜ?¶
ファイルを切り詰めて、その後で読み書きアクセスを提供するようなものを 使おうとしたからです。
open(FH, "+> /path/name"); # (ほぼ常に)間違い
おっと、ファイルがなかったときに失敗するような以下のやり方を使うべきでしょう。
open(FH, "+< /path/name"); # open for update
">"を使うと常に切り詰めか作成が行われます。 "<"を使った場合にはどちらも行いません。 "+"はこれらを変更することはありません。
ファイルをオープンする多くのやり方の例を以下に挙げます。 sysopenを使っているものはすべて
use Fcntl;
をしているものと仮定します。
ファイルを読み込みのためにオープンするには:
open(FH, "< $path") || die $!;
sysopen(FH, $path, O_RDONLY) || die $!;
ファイルを書き出しのためにオープンし、ファイルがなければ新しく作り あれば古いファイルを切り詰めるには:
open(FH, "> $path") || die $!;
sysopen(FH, $path, O_WRONLY|O_TRUNC|O_CREAT) || die $!;
sysopen(FH, $path, O_WRONLY|O_TRUNC|O_CREAT, 0666) || die $!;
ファイルを書き出しのためにオープンし、ファイルを新たに作成するが 慈善に存在していてはいけない場合:
sysopen(FH, $path, O_WRONLY|O_EXCL|O_CREAT) || die $!;
sysopen(FH, $path, O_WRONLY|O_EXCL|O_CREAT, 0666) || die $!;
ファイルを追加のためにオープンし、必要があればファイルを作成するには:
open(FH, ">> $path") || die $!;
sysopen(FH, $path, O_WRONLY|O_APPEND|O_CREAT) || die $!;
sysopen(FH, $path, O_WRONLY|O_APPEND|O_CREAT, 0666) || die $!;
ファイルを追加のためにオープンするが、ファイルが事前になければいけない場合:
sysopen(FH, $path, O_WRONLY|O_APPEND) || die $!;
ファイルを更新のためにオープンするが、ファイルが事前になければいけない場合:
open(FH, "+< $path") || die $!;
sysopen(FH, $path, O_RDWR) || die $!;
ファイルを更新のためにオープンし、必要があればファイルを作成する場合:
sysopen(FH, $path, O_RDWR|O_CREAT) || die $!;
sysopen(FH, $path, O_RDWR|O_CREAT, 0666) || die $!;
To open file for update, file must not exist: ファイルを更新のためにオープンするが、ファイルが事前に存在してはいけない場合:
sysopen(FH, $path, O_RDWR|O_EXCL|O_CREAT) || die $!;
sysopen(FH, $path, O_RDWR|O_EXCL|O_CREAT, 0666) || die $!;
ブロッキングなしでフィルをオープンし、必要があれば作成するには:
sysopen(FH, "/tmp/somefile", O_WRONLY|O_NDELAY|O_CREAT)
or die "can't open /tmp/somefile: $!":
Be warned that neither creation nor deletion of files is guaranteed to be an atomic operation over NFS. That is, two processes might both successfully create or unlink the same file! Therefore O_EXCL isn't as exclusive as you might wish.
ファイルの作成や削除はNFS越しの場合にはアトミックな操作ではないことに 注意してください。 つまり、二つのプロセスが同じファイルの作成や削除に成功するかもしれないのです! したがってO_EXCLはあなたが期待しているようには排他的ではないのです。
See also the new perlopentut if you have it (new for 5.6).
もしあれば 新しい perlopentut (5.6 対応) も参照して下さい。
なぜ <*> を使ったときに "Argument list too long" (引数リストが長すぎる)と なることがあるの?¶
The <>
operator performs a globbing operation (see above). In Perl versions earlier than v5.6.0, the internal glob() operator forks csh(1) to do the actual glob expansion, but csh can't handle more than 127 items and so gives the error message Argument list too long
. People who installed tcsh as csh won't have this problem, but their users may be surprised by it.
<*> 演算子はグロブ操作(globbing operation)を行います。 v5.6.0 以前の Perl では、glob() はcsh(1)を起動して、 実際のグロブを行います。 しかし、cshは127を越えるアイテムを扱うことができないので、 そういったエラーメッセージ Argument list too long
となるのです。 cshをcshとしてインストールしている人はこの問題に直面することはないでしょうが、 ユーザーがびっくりすることがあるかもしれません。
To get around this, either upgrade to Perl v5.6.0 or later, do the glob yourself with readdir() and patterns, or use a module like Glob::KGlob, one that doesn't use the shell to do globbing.
これに対処するには、Perl v5.6.0 以降にアップグレードするか グロブをreaddir()とパターンを使って 自分自身で実行するか、あるいはGlob::KGlobのようなモジュールを使って、 シェルによるグロブを行わないようにします。
glob()に leak/bugはあるの?¶
一部のオペレーティングシステム上の実装のために、 glob()関数やスカラーコンテキストでのアングルブラケットによる そのエイリアスを使った場合、メモリリークや予測できない振る舞いを 引き起こす可能性があります。 したがって、glob()はリストコンテキストでのみ使うのが最善です。
どうやれば 名前の先頭に">"があったり 末尾に空白があるようなファイルをオープンすることができますか?¶
通常、perl はファイル名の末尾にある空白を無視し、幾つかのキャラクターが 先行している場合(もしくは末尾に“|”がついている場合)には、 それを特別な意味として解釈します。 これを避けるために、以下のようなルーチンを使いたくなるかもしれません。 これは不完全なパス名を明確な相対パス名に変換し、 perlが(ファイル名に対して)何もしないようにさせるために 名前の末尾にあるナルバイトを記録します。
sub safe_filename {
local $_ = shift;
s#^([^./])#./$1#;
$_ .= "\0";
return $_;
}
$badpath = "<<<something really wicked ";
$fn = safe_filename($badpath");
open(FH, "> $fn") or "couldn't open $badpath: $!";
This assumes that you are using POSIX (portable operating systems interface) paths. If you are on a closed, non-portable, proprietary system, you may have to adjust the "./"
above.
これはあなたが POSIX(portable operating systems interface) パスを 使っているという仮定をしています。 もしあなたが閉鎖的で、移植性のない、独占的システムを使っているのであれば、 "./"
を調整する必要があるかもしれません。
It would be a lot clearer to use sysopen(), though:
このことはsysopen()ではっきりと変わります:
use Fcntl;
$badpath = "<<<something really wicked ";
sysopen (FH, $badpath, O_WRONLY | O_CREAT | O_TRUNC)
or die "can't open $badpath: $!";
より詳しい情報は、新規のperlopentutを参照してください。(5.6 対応).
どうすれば信頼性のあるファイルのリネームができますか?¶
Well, usually you just use Perl's rename() function. That may not work everywhere, though, particularly when renaming files across file systems. Some sub-Unix systems have broken ports that corrupt the semantics of rename()--for example, WinNT does this right, but Win95 and Win98 are broken. (The last two parts are not surprising, but the first is. :-)
えー、通常は Perl の rename() 関数を使うだけです。しかし、これはどこでも 働くわけではなく、とくにファイルシステムをまたがったリネームはできません。 非 Unix システムの中には rename() の意味論が崩壊している 壊れた移植がされているものがあります--例えば、 WinNT は正しく動きますが、Win95 と Win98 は壊れています。 (後者の二つは驚くに値しませんが、前者は驚きです。:-)
If your operating system supports a proper mv(1) program or its moral equivalent, this works:
あなたの使っているオペレーティングシステムが、適切な mv(1) あるいは それと等価なプログラムをサポートしているのなら以下のようなやり方が 使えるでしょう:
rename($old, $new) or system("mv", $old, $new);
File::Copy モジュールを使うのが、より魅力的かもしれません。新しい名前で 新しいファイルにコピーして(ここで戻り値をチェック)、 古いものを削除するだけです。 ただし、これはパーミッション、タイムスタンプ、inode情報などが本当の rename()とは同じにはなりません。
新しいバージョンのFile::Copyは関数move()をエクスポートしています。
どうすればファイルをロックできますか?¶
Perlに組み込みの flock関数(詳しくはperlfuncを参照)は、flock(2)が あればそれを、なければfcntl(2)を呼び出します(5.004以降の場合)。 そして、これら二つのシステムコールのいずれもない場合にはlockf(3)を 呼び出します。 一部のシステムでは、ネイティブなロッキングとは異なった使い方を するかもしれません。 以下に、Perlのflock()に関する罠(gotchas)を挙げておきます:
三つのシステムコールがどれもなければ(もしくは等価なものがなければ)、 致命的エラーを生成します。
lockf(3)は共有ロックをサポートしません。そして、書き込み用に(もしくは 追加モードか読み書きモード)オープンされているファイルハンドルを要求します。
-
Some versions of flock() can't lock files over a network (e.g. on NFS file systems), so you'd need to force the use of fcntl(2) when you build Perl. But even this is dubious at best. See the flock entry of perlfunc and the INSTALL file in the source distribution for information on building Perl to do this.
flock()の一部のバージョンはネットワーク越し(NFSファイルシステムなど)に ファイルをロックすることはできません。 このため、Perlをビルドするときにfcntl(2)を使うように強制する必要が あるかもしれません。しかしこれでも最善かどうかは疑わしいです。 perlfuncのflock()のエントリと、そのためのPerlをビルドする 情報のある、ソース配布中にあるINSTALLというファイルを参照してください。
Two potentially non-obvious but traditional flock semantics are that it waits indefinitely until the lock is granted, and that its locks are merely advisory. Such discretionary locks are more flexible, but offer fewer guarantees. This means that files locked with flock() may be modified by programs that do not also use flock(). Cars that stop for red lights get on well with each other, but not with cars that don't stop for red lights. See the perlport manpage, your port's specific documentation, or your system-specific local manpages for details. It's best to assume traditional behavior if you're writing portable programs. (If you're not, you should as always feel perfectly free to write for your own system's idiosyncrasies (sometimes called "features"). Slavish adherence to portability concerns shouldn't get in the way of your getting your job done.)
二つの潜在的に明らかでないけれども、伝統的な flock の手法があります。 一つはロックが与えられるまで無限に待ち、そしてそのロックは めったに忠告されません。このような自由度の高いロックはより柔軟ですが、 得るものはより少ないです。 つまり、 flock() でロックされたファイルは flock() を使っていない プログラムによって修正される可能性があるからです。 赤信号で止まる車同士ならうまくいきますが、片方が信号を守らないなら うまくいかないということです。 詳細については perlport man ページ、使っているバージョン独自のドキュメント、 システム独自のローカルな man ページを参照してください。 移植性のあるプログラムのためには、伝統的な振る舞いを仮定するのが最善です。 (そうしないなら、あなたは自分のシステムの癖(「仕様」とも呼ばれます)に 合わせて書くことをいつでも気にしないようにするべきです。 あなたの仕事をやり遂げる時に、盲目的に移植性を考慮するべきではありません。)
ファイルのロッキングに関する詳細は<perlopentut/"FileLocking">を 参照してください(new for 5.6)。
なぜ単に open(FH, ">file.lock")とできないの?¶
A common bit of code NOT TO USE is this:
以下のようなことをしてはいけません:
sleep(3) while -e "file.lock"; # こういったやり方を
open(LCK, "> file.lock"); # しないようにしてください
これは古典的な競合状態(race conditon)です: あなたはここで、 一つのステップでやらなければならないことを二つのステップでやっています。 つまりこれが、コンピューターのハードウェアがアトミックな test-and-setの命令を備えている理由です。理論的には、 これを動作するにさせるには:
sysopen(FH, "file.lock", O_WRONLY|O_EXCL|O_CREAT)
or die "can't open file.lock: $!":
残念なことに、NFSを通じた場合にはファイルの作成(と削除)は アトミックではありません。 このため、この例はネットワークを通した場合にはうまく動きません (少なくとも失敗する可能性があります)。 link()を含め、様々なやり方が既に提案されていますが、これらは busy-waitを 伴うものでありあまり望ましいものではありません。
まだロックができない。ただ単にファイルにある数値をインクリメントしたいだけなんだけど。 どうすればいいの?¶
Didn't anyone ever tell you web-page hit counters were useless? They don't count number of hits, they're a waste of time, and they serve only to stroke the writer's vanity. It's better to pick a random number; they're more realistic.
これまでただの一人もあなたにwebページのヒットカウンターは役たたずなんだと いうことを言わなかったんですか? ヒットカウンターはヒットした数は数えず、 時間を浪費し、さらに言えば作者のうぬぼれを叩くのに役立つだけです。 乱数を取り出したほうがよっぽどましです。
まあいずれにしろ、以下のようにしてやります:
use Fcntl qw(:DEFAULT :flock);
sysopen(FH, "numfile", O_RDWR|O_CREAT) or die "can't open numfile: $!";
flock(FH, LOCK_EX) or die "can't flock numfile: $!";
$num = <FH> || 0;
seek(FH, 0, 0) or die "can't rewind numfile: $!";
truncate(FH, 0) or die "can't truncate numfile: $!";
(print FH $num+1, "\n") or die "can't write numfile: $!";
close FH or die "can't close numfile: $!";
以下の例はもっと良いweb-page ヒットカウンターです:
$hits = int( (time() - 850_000_000) / rand(1_000) );
If the count doesn't impress your friends, then the code might. :-)
カウントがあなたの友達を感心させないのなら、このコードもそうでしょうね :-)
バイナリファイルをランダムに更新するには?¶
単にバイナリにパッチをあてたいというのなら、多くの場合は以下のように 単純にできます:
perl -i -pe 's{window manager}{window mangler}g' /usr/bin/emacs
もし固定サイズのレコードを持っているのなら、以下のようにして行うことも できます:
$RECSIZE = 220; # バイトで表した レコードのサイズ
$recno = 37; # 更新すべきレコード
open(FH, "+<somewhere") || die "can't update somewhere: $!";
seek(FH, $recno * $RECSIZE, 0);
read(FH, $record, $RECSIZE) == $RECSIZE || die "can't read record $recno: $!";
# munge the record
seek(FH, -$RECSIZE, 1);
print FH $record;
close FH;
ロックとエラーチェックは読者の練習として残してあります。 これらを行うことを忘れてはいけません。 さもなくばとても後悔することになるでしょう。
perlでファイルのタイムスタンプを取得するには?¶
そのファイルが最後に読み込まれ、書き出され、そのメタデータ(所有者など)が 変更された時刻を取得したいのであれば、perlfuncに説明がある -M, -A, -C といったファイルテスト演算子を使います。 これらはファイルの年齢(あなたのプログラムの実行開始に対するもの)を 浮動小数点数で表現された日数で取得します。 epochからの経過秒数による“生の”時間を得るためには、 stat関数を呼び出して、その値をlocaltime()、gmtime()、POSIX::strftime()を 使って人が読めるような形へ変換します。
例を挙げましょう:
$write_secs = (stat($file))[9];
printf "file %s updated at %s\n", $file,
scalar localtime($write_secs);
もっと読みやすいものがお好みなら、File::statモジュールを使います (これは5.004以降の標準配布キットに含まれています)。
# エラーチェックは読者の練習のために残しています。
use File::stat;
use Time::localtime;
$date_string = ctime(stat($file)->mtime);
print "file $file updated at $date_string\n";
POSIX::strftime()アプローチは理論的にはロカール非依存で、 使う価値があります。詳しくはperlolcaleを参照してください。
perlでファイルのタイムスタンプを設定するには?¶
You use the utime() function documented in "utime" in perlfunc. By way of example, here's a little program that copies the read and write times from its first argument to all the rest of them.
"utime" in perlfuncで説明されているutime()という関数を使います。 例として、引数の最初のファイルのread and write 時刻を読んで、 残りのファイルにその時刻を設定する小さなプログラムを挙げましょう。
if (@ARGV < 2) {
die "usage: cptimes timestamp_file other_files ...\n";
}
$timestamp = shift;
($atime, $mtime) = (stat($timestamp))[8,9];
utime $atime, $mtime, @ARGV;
例によって、エラーチェックは読者の練習として残してあります。
現時点では、Windows 95/NT への移植ではutimeは正しく動作しないと いうことに注意してください。バグが報告されています。これらのプラ ットフォームではutime()を使う前に注意してチェックしてください。
一度に二つ以上のファイルに出力するには?¶
これをやる必要があるのが一回だけなら、以下のようにしてできます:
for $fh (FH1, FH2, FH3) { print $fh "whatever\n" }
一つのファイルハンドルを幾つかの出力ファイルハンドルに接続するには、 multiplexingを注意深く行うtee(1) を持っているのならそれを使うのが最も簡単です。
open (FH, "| tee file1 file2 file3");
あるいは:
# make STDOUT go to three files, plus original STDOUT
open (STDOUT, "| tee file1 file2 file3") or die "Teeing off: $!\n";
print "whatever\n" or die "Writing: $!\n";
close(STDOUT) or die "Closing: $!\n";
そういったものがなければ、自分で multiplexing print 関数を書くか、 あるいは tee プログラムを自分で書くか http://www.perl.com/CPAN/authors/id/TOMC/scripts/tct.gz, に置かれている Tom ChristiansenによってPerlで書かれたものを 使うかする必要があるでしょう。
ファイル全体を一度に読みこむにはどうすればいいですか?¶
The customary Perl approach for processing all the lines in a file is to do so one line at a time:
Perl においてファイルの全ての行を処理するための慣例的な手法は 1 行ずつ読みこむことです:
open (INPUT, $file) || die "can't open $file: $!";
while (<INPUT>) {
chomp;
# do something with $_
}
close(INPUT) || die "can't close $file: $!";
This is tremendously more efficient than reading the entire file into memory as an array of lines and then processing it one element at a time, which is often--if not almost always--the wrong approach. Whenever you see someone do this:
これはファイル全体を行の配列として読みこんでから1行ずつ処理するという (ほとんど常に、でないのなら)しばしば誤った手法よりも 遥かに効率的です。 それでもファイル全体を読み込みたいなら以下のようにします:
@lines = <INPUT>;
you should think long and hard about why you need everything loaded at once. It's just not a scalable solution. You might also find it more fun to use the standard DB_File module's $DB_RECNO bindings, which allow you to tie an array to a file so that accessing an element the array actually accesses the corresponding line in the file.
本当に全てを一度に読み込む必要があるのかを良く考えるべきです。 これは単にスケールの問題ではありません。DB_File モジュールの $DB_RECNO バインディングの方がより好ましいと考えるかもしれません。 これは配列とファイルを結び付けるので、配列にアクセスすると、実際には ファイルの対応する行にアクセスすることになります。
On very rare occasion, you may have an algorithm that demands that the entire file be in memory at once as one scalar. The simplest solution to that is
非常にまれな場合に、ファイル全体を一度にひとつのスカラとして メモリに読みこむ必要があるアルゴリズムを使うかもしれません。 これを解決する最も単純な方法は以下のとおりです:
$var = `cat $file`;
Being in scalar context, you get the whole thing. In list context, you'd get a list of all the lines:
スカラコンテキストとして、全てを得ることが出来ます。 以下のようにすればリストコンテキストで全ての行のリストを得ることが出来ます:
@lines = `cat $file`;
This tiny but expedient solution is neat, clean, and portable to all systems on which decent tools have been installed. For those who prefer not to use the toolbox, you can of course read the file manually, although this makes for more complicated code.
この単純だけれども便利な解法はすっきりしていて、明快で、ツールが インストールされている全てのシステムにおいて移植性があります。 このツールボックスを使いたくない人のために、もちろんファイルを手動で 読み込むこともできますが、これはやや煩わしいコードになります。
{
local(*INPUT, $/);
open (INPUT, $file) || die "can't open $file: $!";
$var = <INPUT>;
}
That temporarily undefs your record separator, and will automatically close the file at block exit. If the file is already open, just use this:
これは一時的にレコードセパレータを未定義にし、ブロックを出るときに 自動的にファイルをクローズします。ファイルが既にオープンしているなら、 単に以下のようにします:
$var = do { local $/; <INPUT> };
どうすればファイルをパラグラフ毎に読み出すことができますか?¶
$/
という変数を使います(詳しくはperlvarを参照してください)。 たとえば ("abc\n\n\n\ndef"
で三つではなく二つのパラグラフを受 け取る、つまり空のパラグラフを除去するには""
を設定します。空 のパラグラフを受け付けるには"\n\n"
を設定します。
Note that a blank line must have no blanks in it. Thus "fred\n \nstuff\n\n"
is one paragraph, but "fred\n\nstuff\n\n"
is two.
空行はブランクを持っていてはいけないということに注意しましょう。 このため、"fred\n\nstuff\n\n"
は一つのパラグラフですが "fred\n\nstuff\n\n"
は二つのパラグラフです。
どうすればファイルからキャラクターを一つだけ読み出せますか?キーボードからは?¶
ほとんどのファイルハンドルに対してはgetc()
という組み込み関数を 使うことができますが、これはターミナルデバイスに対してはうまくいきません。 STDIN に対しては、CPAN にある Term::ReadKey を使うか "getc" in perlfuncにあるサンプルコードを使います。
あなたの使っているシステムがPOSIXをサポートしているのなら、echo を オフにしているのを気をつけながら以下のようなコードでできます。
以下のようなコードが使えます。
#!/usr/bin/perl -w
use strict;
$| = 1;
for (1..4) {
my $got;
print "gimme: ";
$got = getone();
print "--> $got\n";
}
exit;
BEGIN {
use POSIX qw(:termios_h);
my ($term, $oterm, $echo, $noecho, $fd_stdin);
$fd_stdin = fileno(STDIN);
$term = POSIX::Termios->new();
$term->getattr($fd_stdin);
$oterm = $term->getlflag();
$echo = ECHO | ECHOK | ICANON;
$noecho = $oterm & ~$echo;
sub cbreak {
$term->setlflag($noecho);
$term->setcc(VTIME, 1);
$term->setattr($fd_stdin, TCSANOW);
}
sub cooked {
$term->setlflag($oterm);
$term->setcc(VTIME, 0);
$term->setattr($fd_stdin, TCSANOW);
}
sub getone {
my $key = '';
cbreak();
sysread(STDIN, $key, 1);
cooked();
return $key;
}
}
END { cooked() }
CPAN にある Term::ReadKey モジュールならもっと簡単に使えます。 最新のバージョンでは non-portable システムのサポートも含まれています:
use Term::ReadKey;
open(TTY, "</dev/tty");
print "Gimme a char: ";
ReadMode "raw";
$key = ReadKey 0, *TTY;
ReadMode "normal";
printf "\nYou said %s, char number %03d\n",
$key, ord $key;
DOS システムに対しては、Dan Carson <dbc@tc.fluke.COM>が 以下のように報告しています。
PC を“raw”モードにするには、msdos.c(Perlのソースファイル)と Ralf Brown の 割り込みリスト(よくネットにあります)から取り出したマジックナンバーを使って ioctl を行います。
$old_ioctl = ioctl(STDIN,0,0); # デバイス情報を取得
$old_ioctl &= 0xff;
ioctl(STDIN,1,$old_ioctl | 32); # bit5をセットして書き戻す
それからキャラクター一つを読み込む:
sysread(STDIN,$c,1); # キャラクター一つを読み込む
PC を "cooked" モードに戻す:
ioctl(STDIN,1,$old_ioctl); # cooked modeに戻す
これで $c にキャラクターが入っています。ord($c) == 0
なら、特殊なキーが 叩かれたことを表す 2 バイトのコードを受け取りました。 もう 1 バイトを sysread(STDIN,$c,1)
を使って読み込み、以下のテーブルに従って 組み合わせたものがあなたの求めているものです。
# PC の2バイトキーコード = ^@ と以下のいずれか
# 十六進 キー
# ------ ----
# 0F SHF TAB
# 10-19 ALT QWERTYUIOP
# 1E-26 ALT ASDFGHJKL
# 2C-32 ALT ZXCVBNM
# 3B-44 F1-F10
# 47-49 HOME,UP,PgUp
# 4B LEFT
# 4D RIGHT
# 4F-53 END,DOWN,PgDn,Ins,Del
# 54-5D SHF F1-F10
# 5E-67 CTR F1-F10
# 68-71 ALT F1-F10
# 73-77 CTR LEFT,RIGHT,END,PgDn,HOME
# 78-83 ALT 1234567890-=
# 84 CTR PgUp
This is all trial and error I did a long time ago, I hope I'm reading the file that worked.
これはかなり昔に私が試行錯誤したもので、きちんと動くだろうと期待しています。
ファイルハンドルがあったときにそれがキャラクターを待っているかどうかを 知るには?¶
あなたがすべき第一のことは、CPAN にある Term::ReadKey を入手することです。 今では閉鎖的な独占システム (オープンなシステムではない、POSIX でもなく、UNIX でもないような…)に 対する限定的なサポートさえあります。
comp.unix.* の Frequently Asked Questions(しばしば尋ねられる質問)リストで、 同じようなものをチェックすべきでしょう: この答えは本質的に同じものです。 つまり、非常にシステム依存なものです。 以下に挙げたのは、BSDシステムで動作する解決策の一つです。
sub key_ready {
my($rin, $nfd);
vec($rin, fileno(STDIN), 1) = 1;
return $nfd = select($rin,undef,undef,0);
}
どの位のキャラクターが待っているのかを知りたいのであれば、 FIONREAD ioctl 呼び出しを使うことができます。 h2phツールは C のインクルードファイルを Perl に変換するもので、その結果は require
によって呼び出すことが可能です。 FIONREAD は sys/ioctl.ph にある関数として定義されます。
$size = pack("L", 0);
ioctl(FH, FIONREAD(), $size) or die "Couldn't call ioctl: $!\n";
$size = unpack("L", $size);
h2ph がインストールされていないか、うまく動作しなかったのであれば、 以下のようにできます。
% grep FIONREAD /usr/include/*/*
/usr/include/asm/ioctls.h:#define FIONREAD 0x541B
Or write a small C program using the editor of champions:
あるいはエディターの王様を使って小さな C プログラムを書きます:
% cat > fionread.c
#include <sys/ioctl.h>
main() {
printf("%#08x\n", FIONREAD);
}
^D
% cc -o fionread fionread.c
% ./fionread
0x4004667f
And then hard-code it, leaving porting as an exercise to your successor.
その後でその値をハードコードしてやって、あなたの仕事を引き継ぐ人のための 練習として移植をさぼります。
$FIONREAD = 0x4004667f; # XXX: opsys に依存
$size = pack("L", 0);
ioctl(FH, $FIONREAD, $size) or die "Couldn't call ioctl: $!\n";
$size = unpack("L", $size);
FIONREAD requires a filehandle connected to a stream, meaning sockets, pipes, and tty devices work, but not files.
FIONREAD はストリームに接続されたファイルハンドルを要求します。 これはソケット、パイプ、あるいは tty デバイスではうまく動作しますが、 ファイルに対してはうまく行きません。
How do I do a tail -f
in perl?¶
(perlで tail -f
をするには?)
まず最初に以下のものを試してみてください
seek(GWFILE, 0, 1);
seek(GWFILE, 0, 1)
という文はカレントの位置を変更しませんが、 そのファイルハンドルにおける end-fo-file 状態を解除します。 このため、次に <GWFILE>とすると Perl は再度何かを読もうとするのです。
このやり方がうまくいかない(これはあなたの使っている stdio が実装している 機能に依存しています)のなら、以下のようにする必要があるでしょう:
for (;;) {
for ($curpos = tell(GWFILE); <GWFILE>; $curpos = tell(GWFILE)) {
# 何かを探し、それをファイルに押し込む
}
# しばらく眠る
seek(GWFILE, $curpos, 0); # かつていた場所にシークする
}
これでもうまく行かなければ、POSIX モジュールを試してみてください。 POSIX はファイルハンドルに対する end-of-file 状態をクリアすることのできる cleaererr()というメソッドを定義しています。 やり方: 終端に至るまでファイルを読み込む。 clearerr()。何かを読み込む。洗って、濯いで、くり返す(lather, rince, repeat)。
CPANにはFile::Tailモジュールがあります。
Perl でファイルハンドルの dup() をするには?¶
"open" in perlfuncを見れば、それを行うための open() の呼び出し方が 何通りもあることに気がつくでしょう。例を挙げます:
open(LOG, ">>/tmp/logfile");
open(STDERR, ">&LOG");
あるいは、リテラルの数値ディスクリプターを使います:
$fd = $ENV{MHCONTEXTFD};
open(MHCONTEXT, "<&=$fd"); # fdopen(3S) のようなもの
Note that "<&STDIN" makes a copy, but "<&=STDIN" make an alias. That means if you close an aliased handle, all aliases become inaccessible. This is not true with a copied one.
"<&STDIN"はコピーを作成し、"<&=STDIN" が エイリアスを作成するということに注意してください。 これはつまり、あなたがエイリアスが作成されたファイルハンドルを クローズすると、エイリアスはすべてアクセスできなくなります。 これはコピーの場合にはそうはなりません。
エラーチェックはいつものように読者の練習のために残してあります。
数値によるファイルディスクリプターをクローズするには?¶
Perl の close() 関数は、先の例にあった MHCNOTEXT のように数値ディスクリプター (numeric descriptor)を使って dup したものでさえも含めて、 Perl 自身がオープンしたものに対して使うことができますから、 その必要はほとんどないはずです。 しかし本当にそうする必要があるのなら、以下のようにできるでしょう:
require 'sys/syscall.ph';
$rc = syscall(&SYS_close, $fd + 0); # 強制的に数値にしなければならない
die "can't sysclose $fd: $!" unless $rc == -1;
Or, just use the fdopen(3S) feature of open():
あるいは、単に open() の fdopen(3S) 機能を使います:
{
local *F;
open F, "<&=$fd" or die "Cannot reopen fd=$fd: $!";
close F;
}
なぜ DOS のパスで "C:\temp\foo" が使えないのでしょうか? なぜ `C:\temp\foo.exe` はうまくいかないの?¶
おーっと! ファイル名にタブや改ページを入れてしまいましたね! "like\this"のようにダブルクォートで括られた文字列の中では、 バックスラッシュはエスケープキャラクターであるということを思い出してください。 エスケープキャラクター全てのリストは "Quote and Quote-like Operators" in perlopにあります。 当然のことでしょうが、あなたの使っているDOSのファイルシステムでは "c:(tab)emp(formfeed)oo" とか "c:(tab)emp(formfeed)oo.exe" なんて名前のファイルはできませんよね。
Either single-quote your strings, or (preferably) use forward slashes. Since all DOS and Windows versions since something like MS-DOS 2.0 or so have treated /
and \
the same in a path, you might as well use the one that doesn't clash with Perl--or the POSIX shell, ANSI C and C++, awk, Tcl, Java, or Python, just to mention a few.
文字列を括るのにシングルクォートを使うか、もしくは(こちらが好ましい) スラッシュを使ってください。 全てのDOSおよびWindowsは、MS-DOS 2.0以降、パス中にある/
と \
を 同じに扱いますから、あなたはPerlを壊すことなく使えます。 もしくは POSIXシェル、ANSI CとC++、awk、tcl、Java、Python を考慮してください。 POSIXパスはより移植性に富んでいます。
なぜ glob("*.*")で全てのファイルを得られないのでしょうか?¶
非 UNIX システムに対する移植であっても、Perl の glob 関数は UNIX の標準的な グロブの振る舞いに従うからです。 全ての(隠し属性でない)ファイルを得るには glob("*")
とする必要があります。 これは glob() の移植性を高めます。 あなたの使っている Perl が独自のグロブ関数をサポートしているかもしれません。 詳しくはドキュメントを参照してください。
Why does Perl let me delete read-only files? Why does -i
clobber protected files? Isn't this a bug in Perl?¶
(なぜ Perl は読みとり専用ファイルを削除してしまうのでしょうか? なぜ-i
clobberはファイルをプロテクトするのでしょうか? これはPerlのバグじゃないんですか?)
http://www.perl.com/CPAN/doc/FMTEYEWTK/file-dir-perms にある "Far More Than You Ever Wanted To Know" が 教育的、かつ懇切丁寧にこの問題を説明しています。
簡単なまとめ: あなたの使っているファイルシステムがどのように動作しているのかを 考えてください。 ファイルに対するパーミッションはそのファイルにあるデータに何が できるかということを表しています。 ディレクトリに対するパーミッションは、そのディレクトリにあるファイルリストに 対して何ができるのかということを表しています。 ファイルを削除したとき、そのファイルに対する名前がディレクトリから 取り除かれます(したがってこの操作はファイルに対するパーミッションでは なく、ディレクトリに対するパーミッションに依存しているのです)。 ファイルに対して書き込みを行おうとすると、ファイルに対するパーミッションが それができるかどうかを決定します。
あるファイルからランダムに行を選択するには?¶
以下に示すのは らくだ本にあったアルゴリズムです:
srand;
rand($.) < 1 && ($line = $_) while <>;
This has a significant advantage in space over reading the whole file in. A simple proof by induction is available upon request if you doubt the algorithm's correctness.
これは、ファイル全体を読み込んで処理するやり方に比べて使用する空間の 大きさにおいて明らかなアドバンテージがあります。 あなたがアルゴリズムの正確さに自信がない場合には 帰納法による単純な証明がつかえるでしょう。
行の配列を出力したときになぜ余計なスペースがつくのでしょうか?¶
print "@lines\n";
とした場合、@line
の要素を繋ぎあわせ、その間に空白を置きます。 @lines
が ("little", "fluffy", "clouds")
だったとすると、 先のやり方で出力した場合以下のようになります:
little fluffy clouds
しかし、@lines
の各要素がテキストの行であって終端に改行がある ("little\n", "fluffy\n", "clouds\n")
のようなものだっとすると その結果は以下のようになります:
little
fluffy
clouds
配列が行から構成されているのなら、出力するには単に以下のようにします:
print @lines;
AUTHOR AND COPYRIGHT¶
Copyright (c) 1997-1999 Tom Christiansen and Nathan Torkington. All rights reserved.
When included as an integrated part of the Standard Distribution of Perl or of its documentation (printed or otherwise), this works is covered under Perl's Artistic License. For separate distributions of all or part of this FAQ outside of that, see perlfaq.
Irrespective of its distribution, all code examples here are in the public domain. You are permitted and encouraged to use this code and any derivatives thereof in your own programs for fun or for profit as you see fit. A simple comment in the code giving credit to the FAQ would be courteous but is not required.
POD ERRORS¶
Hey! The above document had some coding errors, which are explained below:
- Around line 46:
-
alternative text 'perlvar/$' contains non-escaped | or /