- 名前
- VERSION
- 説明
- 出力ファイルハンドルを flush/unbufferするには? なぜ私はこれをやらなければならないの?
- ファイルの一行を変更する/ファイルのある行を削除する/ファイルの中程で一行挿入する/ファイルの先頭に追加するには?
- あるファイルの行数を数えるには?
- ファイルの最後の N 行を削除するには?
- How can I use Perl's
-i
option from within a program? - ファイルをコピーするには?
- 一時ファイルの名前を作り出すには?
- 固定長レコードのファイルを操作するには?
- ファイルハンドルをサブルーチンに局所化するには? サブルーチンにファイルハンドルを渡すには? ファイルハンドルの配列を作るには?
- ファイルハンドルを間接的に扱うには?
- write()と一緒に使うフッターのフォーマットのセットアップをする方法は?
- 文字列に対して write() するには?
- ファイルハンドルを文字列としてオープンするには?
- 出力する数字にカンマを付加するには?
- ファイル名の中にあるチルダ (~) を変換するには?
- ファイルを読み書きモードでオープンしたときに内容をクリアしてしまうのはなぜ?
- なぜ <*> を使ったときに "Argument list too long" (引数リストが長すぎる)となることがあるの?
- 名前の先頭に">"があったり末尾に空白があるようなファイルをオープンするには?
- 信頼性のあるファイルのリネームをするには?
- ファイルをロックするには?
- なぜ単に open(FH, ">file.lock")とできないの?
- まだロックができません。ただ単にファイルにある数値をインクリメントしたいだけなんだけど。どうすればいいの?
- ファイルの末尾にちょっとしたテキストを追加したいだけなんです。それでもロックが必要なの?
- バイナリファイルをランダムに更新するには?
- perl でファイルのタイムスタンプを取得するには?
- perl でファイルのタイムスタンプを設定するには?
- 複数のファイルを一度に表示するには?
- ファイル全体を一度に読みこむには?
- ファイルをパラグラフ毎に読み込むには?
- ファイルから 1 文字だけ読み出すには? キーボードからは?
- あるファイルハンドルが読み込み待ちの文字を待っているかどうかを知るには?
- 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
名前¶
perlfaq5 - Files and Formats
perlfaq5 - ファイルとフォーマット
VERSION¶
version 5.021009
説明¶
This section deals with I/O and the "f" issues: filehandles, flushing, formats, and footers.
このセクションでは、入出力と“f”に関する事柄: ファイルハンドル (filehandle)、フラッシング(flushing)、フォーマット(format)、 フッター(footer)を扱います。
出力ファイルハンドルを flush/unbufferするには? なぜ私はこれをやらなければならないの?¶
(contributed by brian d foy)
(brian d foy によって寄贈されました)
You might like to read Mark Jason Dominus's "Suffering From Buffering" at http://perl.plover.com/FAQs/Buffering.html .
http://perl.plover.com/FAQs/Buffering.html にある Mark Jason Dominus の "Suffering From Buffering" を読みたいかもしれません。
Perl normally buffers output so it doesn't make a system call for every bit of output. By saving up output, it makes fewer expensive system calls. For instance, in this little bit of code, you want to print a dot to the screen for every line you process to watch the progress of your program. Instead of seeing a dot for every line, Perl buffers the output and you have a long wait before you see a row of 50 dots all at once:
通常 Perl は出力をバッファリングするので、ちょっと出力する毎に システムコールを呼び出したりはしません。 出力回数を節約することで、負荷の高いシステムコールの回数を減らします。 例えば、このコード片では、プログラムが1行の処理する毎に、プログラムの 実行状況を示すためにドットを表示します。 Perl は 行毎にドットを表示せず、出力をバッファリングするので、 長い間待った後に 50 文字のドットが一気に表示されます:
# long wait, then row of dots all at once
while( <> ) {
print ".";
print "\n" unless ++$count % 50;
#... expensive line processing operations
}
To get around this, you have to unbuffer the output filehandle, in this case, STDOUT
. You can set the special variable $|
to a true value (mnemonic: making your filehandles "piping hot"):
これを回避するには、出力ファイルハンドル (この場合は STDOUT
) の バッファリングを留める必要があります。 特殊変数 $|
を真の値にセットします (記憶法: パイプをホットな状態にしておくために使う):
$|++;
# dot shown immediately
while( <> ) {
print ".";
print "\n" unless ++$count % 50;
#... expensive line processing operations
}
The $|
is one of the per-filehandle special variables, so each filehandle has its own copy of its value. If you want to merge standard output and standard error for instance, you have to unbuffer each (although STDERR might be unbuffered by default):
$|
はファイルハンドル単位の特殊変数の一つで、ファイルハンドル毎に この値のコピーを保持しています。 例えば、標準出力と標準エラーをまとめたいなら、それぞれのバッファリングを 解除する必要があります(但し、STDERR はデフォルトでは バッファリングしていないはずです):
{
my $previous_default = select(STDOUT); # save previous default
$|++; # autoflush STDOUT
select(STDERR);
$|++; # autoflush STDERR, to be sure
select($previous_default); # restore previous default
}
# now should alternate . and +
while( 1 ) {
sleep 1;
print STDOUT ".";
print STDERR "+";
print STDOUT "\n" unless ++$count % 25;
}
Besides the $|
special variable, you can use binmode
to give your filehandle a :unix
layer, which is unbuffered:
$|
特殊変数に加えて、binmode
を使ってファイルハンドルに :unix
層をつけることでバッファリングしないようにできます:
binmode( STDOUT, ":unix" );
while( 1 ) {
sleep 1;
print ".";
print "\n" unless ++$count % 50;
}
For more information on output layers, see the entries for binmode
and open in perlfunc, and the PerlIO module documentation.
出力層に関するさらなる情報については、perlfunc の binmode
および open のエントリと、PerlIO モジュールの文書を参照してください。
If you are using IO::Handle or one of its subclasses, you can call the autoflush
method to change the settings of the filehandle:
IO::Handle かその派生クラスを使っているなら、ファイルハンドルの設定を 変更するために autoflush
メソッドを呼び出せます:
use IO::Handle;
open my( $io_fh ), ">", "output.txt";
$io_fh->autoflush(1);
The IO::Handle objects also have a flush
method. You can flush the buffer any time you want without auto-buffering
IO::Handle オブジェクトには flush
メソッドもあります。 いつでも自動バッファリングなしにバッファをフラッシュできます
$io_fh->flush;
ファイルの一行を変更する/ファイルのある行を削除する/ファイルの中程で一行挿入する/ファイルの先頭に追加するには?¶
(contributed by brian d foy)
(brian d foy によって寄贈されました)
The basic idea of inserting, changing, or deleting a line from a text file involves reading and printing the file to the point you want to make the change, making the change, then reading and printing the rest of the file. Perl doesn't provide random access to lines (especially since the record input separator, $/
, is mutable), although modules such as Tie::File can fake it.
テキストファイルの行を挿入、変更、削除するための基本的な考え方は、 ファイルを変更したい地点まで読み込んで表示し、変更を行い、ファイルの 残りを読み込んで表示するというものです。 (特にレコード入力セパレータ $/
は変更可能なので) Perl は行に対する ランダムアクセスは提供していませんが、Tie::File はそれを見せかけます。
A Perl program to do these tasks takes the basic form of opening a file, printing its lines, then closing the file:
この作業をする Perl プログラムは、ファイルを開いて、一行毎に表示し、 ファイルを閉じるという基本的な形をとります:
open my $in, '<', $file or die "Can't read old file: $!";
open my $out, '>', "$file.new" or die "Can't write new file: $!";
while( <$in> ) {
print $out $_;
}
close $out;
Within that basic form, add the parts that you need to insert, change, or delete lines.
基本形の中に、行の追加、修正、削除という必要な作業を追加します。
To prepend lines to the beginning, print those lines before you enter the loop that prints the existing lines.
先頭に追加するには、既存の行を表示するループに入る前に追加行を表示します。
open my $in, '<', $file or die "Can't read old file: $!";
open my $out, '>', "$file.new" or die "Can't write new file: $!";
print $out "# Add this line to the top\n"; # <--- HERE'S THE MAGIC
while( <$in> ) {
print $out $_;
}
close $out;
To change existing lines, insert the code to modify the lines inside the while
loop. In this case, the code finds all lowercased versions of "perl" and uppercases them. The happens for every line, so be sure that you're supposed to do that on every line!
既にある行を変更するには、while
の中に行を変更するコードを追加します。 今回の場合、コードは小文字の "perl" を探してそれを大文字にします。 これは全ての行に対して起こるので、全ての行に対してこれを行いたいということを 確認してください!
open my $in, '<', $file or die "Can't read old file: $!";
open my $out, '>', "$file.new" or die "Can't write new file: $!";
print $out "# Add this line to the top\n";
while( <$in> ) {
s/\b(perl)\b/Perl/g;
print $out $_;
}
close $out;
To change only a particular line, the input line number, $.
, is useful. First read and print the lines up to the one you want to change. Next, read the single line you want to change, change it, and print it. After that, read the rest of the lines and print those:
特定の行だけ変更するには、入力行番号 $.
が便利です。 まず、変更したい行まで読み込んで表示します。 次に、変更したい行を 1 行読んで、変更し、表示します。 その後、残りの行を読み込んで表示します:
while( <$in> ) { # print the lines before the change
print $out $_;
last if $. == 4; # line number before change
}
my $line = <$in>;
$line =~ s/\b(perl)\b/Perl/g;
print $out $line;
while( <$in> ) { # print the rest of the lines
print $out $_;
}
To skip lines, use the looping controls. The next
in this example skips comment lines, and the last
stops all processing once it encounters either __END__
or __DATA__
.
行を読み飛ばすには、ループ制御を使います。 この例の next
はコメント行を読み飛ばし、last
は、__END__
か __DATA__
のどちらかに出会った後は全ての処理を停止します。
while( <$in> ) {
next if /^\s+#/; # skip comment lines
last if /^__(END|DATA)__$/; # stop at end of code marker
print $out $_;
}
Do the same sort of thing to delete a particular line by using next
to skip the lines you don't want to show up in the output. This example skips every fifth line:
同じようなことは、next
を使って表示したくない行を飛ばすことで行えます。 この例は 5 行毎に飛ばします:
while( <$in> ) {
next unless $. % 5;
print $out $_;
}
If, for some odd reason, you really want to see the whole file at once rather than processing line-by-line, you can slurp it in (as long as you can fit the whole thing in memory!):
もし、なんらかの変わった理由により、一行ずつ処理するのではなく、本当に ファイル全体を一度に読み込みたい場合、(メモリがあれば!)以下のようにして できます:
open my $in, '<', $file or die "Can't read old file: $!"
open my $out, '>', "$file.new" or die "Can't write new file: $!";
my $content = do { local $/; <$in> }; # slurp!
# do your magic here
print $out $content;
Modules such as Path::Tiny and Tie::File can help with that too. If you can, however, avoid reading the entire file at once. Perl won't give that memory back to the operating system until the process finishes.
Path::Tiny や Tie::File のようなモジュールもこの助けになります。 しかし、もし可能なら、ファイル全体を一度に読み込むのは避けてください。 Perl は OS から確保したメモリを、プロセスが終わるまで返しません。
You can also use Perl one-liners to modify a file in-place. The following changes all 'Fred' to 'Barney' in inFile.txt, overwriting the file with the new contents. With the -p
switch, Perl wraps a while
loop around the code you specify with -e
, and -i
turns on in-place editing. The current line is in $_
. With -p
, Perl automatically prints the value of $_
at the end of the loop. See perlrun for more details.
ファイルをその場で変更するために、Perl の一行野郎も使えます。 以下の例では、inFile.txt にある全ての 'Fred' を 'Barney' に変更し、 ファイルを新しい内容で上書きします。 -p
スイッチ付きの場合、-e
で指定したコードを while
ループで包み、 -i
はその場編集を有効にします。 現在行は $_
に入っています。 -p
があると、Perl はループの最後に $_
の値を自動的に表示します。 さらなる詳細については perlrun を参照してください。
perl -pi -e 's/Fred/Barney/' inFile.txt
To make a backup of inFile.txt
, give -i
a file extension to add:
inFile.txt
をバックアップするには、-i
で追加する拡張子を指定します:
perl -pi.bak -e 's/Fred/Barney/' inFile.txt
To change only the fifth line, you can add a test checking $.
, the input line number, then only perform the operation when the test passes:
5 行目だけを変更するには、入力行番号 $.
をチェックするテストを追加し、 テストに通過した場合にのみ操作を行います:
perl -pi -e 's/Fred/Barney/ if $. == 5' inFile.txt
To add lines before a certain line, you can add a line (or lines!) before Perl prints $_
:
特定の行の前に追加するには、Perl が $_
を表示する前に行を追加します:
perl -pi -e 'print "Put before third line\n" if $. == 3' inFile.txt
You can even add a line to the beginning of a file, since the current line prints at the end of the loop:
現在行はループの終わりに表示されるので、ファイルの先頭に行を 追加することもできます:
perl -pi -e 'print "Put before first line\n" if $. == 1' inFile.txt
To insert a line after one already in the file, use the -n
switch. It's just like -p
except that it doesn't print $_
at the end of the loop, so you have to do that yourself. In this case, print $_
first, then print the line that you want to add.
既にファイルにある行の後に行を追加するには、-n
スイッチを使います。 これは -p
と同様ですが、ループの終わりに $_
を表示しないので、 自分自身で表示する必要があります。 この場合、まず $_
を表示し、それから追加したい行を表示します。
perl -ni -e 'print; print "Put after fifth line\n" if $. == 5' inFile.txt
To delete lines, only print the ones that you want.
行を削除するには、必要なものだけを表示します:
perl -ni -e 'print if /d/' inFile.txt
あるファイルの行数を数えるには?¶
(contributed by brian d foy)
(brian d foy によって寄贈されました)
Conceptually, the easiest way to count the lines in a file is to simply read them and count them:
概念的には、ファイルの行数を数える一番易しい方法は単にそれを読み込んで それを数えます:
my $count = 0;
while( <$fh> ) { $count++; }
You don't really have to count them yourself, though, since Perl already does that with the $.
variable, which is the current line number from the last filehandle read:
しかし、Perl には最後に読み込んだファイルハンドルの現在の行番号である $.
変数が既にあるので、自分自身で数える必要はありません:
1 while( <$fh> );
my $count = $.;
If you want to use $.
, you can reduce it to a simple one-liner, like one of these:
$.
を使いたいなら、以下のように単純な一行野郎にまで短くできます:
% perl -lne '} print $.; {' file
% perl -lne 'END { print $. }' file
Those can be rather inefficient though. If they aren't fast enough for you, you might just read chunks of data and count the number of newlines:
しかしこれらはやや非効率です。 これがあなたにとって十分に速くないなら、単にデータの塊を読み込んで改行の 数を数えるかもしれません:
my $lines = 0;
open my($fh), '<:raw', $filename or die "Can't open $filename: $!";
while( sysread $fh, $buffer, 4096 ) {
$lines += ( $buffer =~ tr/\n// );
}
close $fh;
However, that doesn't work if the line ending isn't a newline. You might change that tr///
to a s///
so you can count the number of times the input record separator, $/
, shows up:
しかし、これは行の末尾が改行でない場合は動作しません。 tr///
を s///
に変えて、入力レコードセパレータ $/
が 現れた数を数えるかもしれません:
my $lines = 0;
open my($fh), '<:raw', $filename or die "Can't open $filename: $!";
while( sysread $fh, $buffer, 4096 ) {
$lines += ( $buffer =~ s|$/||g; );
}
close $fh;
If you don't mind shelling out, the wc
command is usually the fastest, even with the extra interprocess overhead. Ensure that you have an untainted filename though:
シェルに出るのを気にしないなら、wc
コマンドが、プロセス間の オーバーヘッドがあっても普通は最速です。 しかし、汚染されていないファイル名を使うように気をつけてください:
#!perl -T
$ENV{PATH} = undef;
my $lines;
if( $filename =~ /^([0-9a-z_.]+)\z/ ) {
$lines = `/usr/bin/wc -l $1`
chomp $lines;
}
ファイルの最後の N 行を削除するには?¶
(contributed by brian d foy)
(brian d foy によって寄贈されました)
The easiest conceptual solution is to count the lines in the file then start at the beginning and print the number of lines (minus the last N) to a new file.
概念的に最も簡単な解決法は、ファイルの行数を数えて、最初から行数分 (マイナス最後の N) だけ新しいファイルに出力します。
Most often, the real question is how you can delete the last N lines without making more than one pass over the file, or how to do it without a lot of copying. The easy concept is the hard reality when you might have millions of lines in your file.
ほとんどの場合、実際の質問は、ファイルに対して 1 パスで最後の N 行を 削除する方法、または多くのコピーなしに行う方法です。 簡単な概念は、ファイルに何百万行もある場合は現実性がありません。
One trick is to use File::ReadBackwards, which starts at the end of the file. That module provides an object that wraps the real filehandle to make it easy for you to move around the file. Once you get to the spot you need, you can get the actual filehandle and work with it as normal. In this case, you get the file position at the end of the last line you want to keep and truncate the file to that point:
一つの裏技は File::ReadBackwards を使って、ファイルの最後から 始めることです。 このモジュールは、ファイルを動き回りやすくするために実際の ファイルハンドルをラップしたオブジェクトを提供します。 必要な場所が分かれば、実際のファイルハンドルを取得して、いつも通りに 処理します。 この場合、保持したい最後の行の最後のファイル位置を取得して、 その位置でファイルを切り詰めます:
use File::ReadBackwards;
my $filename = 'test.txt';
my $Lines_to_truncate = 2;
my $bw = File::ReadBackwards->new( $filename )
or die "Could not read backwards in [$filename]: $!";
my $lines_from_end = 0;
until( $bw->eof or $lines_from_end == $Lines_to_truncate ) {
print "Got: ", $bw->readline;
$lines_from_end++;
}
truncate( $filename, $bw->tell );
The File::ReadBackwards module also has the advantage of setting the input record separator to a regular expression.
File::ReadBackwards モジュールは、入力レコードセパレータを 正規表現で設定できるという利点もあります。
You can also use the Tie::File module which lets you access the lines through a tied array. You can use normal array operations to modify your file, including setting the last index and using splice
.
行に対して tie された配列を使ってアクセスできる Tie::File モジュールも 使えます。 最後の添え字を設定して splice
を使うなど、、ファイルを修正するために 通常の配列操作が使えます。
How can I use Perl's -i
option from within a program? ¶
(プログラム内から Perl の -i
オプションを使うには?)
-i
sets the value of Perl's $^I
variable, which in turn affects the behavior of <>
; see perlrun for more details. By modifying the appropriate variables directly, you can get the same behavior within a larger program. For example:
-i
は Perl の $^I
変数の値をセットし、これにより <>
の 振る舞いに影響を与えます; 更なる詳細については perlrun を 参照してください。 適切な変数を直接修正することによって、より大きなプログラムの中で同じ効果が 得られます。 例えば:
# ...
{
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; # Correct typos, preserving case
print;
close ARGV if eof; # Reset $.
}
}
# $^I and @ARGV return to their old values here
This block modifies all the .c
files in the current directory, leaving a backup of the original data from each file in a new .c.orig
file.
このブロックはカレントディレクトリの全ての the .c
ファイルを修正し、 各ファイルの元データのバックアップを新しい .c.orig
ファイルに残します。
ファイルをコピーするには?¶
(contributed by brian d foy)
(brian d foy によって寄贈されました)
Use the File::Copy module. It comes with Perl and can do a true copy across file systems, and it does its magic in a portable fashion.
File::Copy モジュールをつかいましょう。 これは Perl に同梱されていて、ファイルシステム間で真のコピーが行われ、 移植性の面での細工がされています。
use File::Copy;
copy( $original, $new_copy ) or die "Copy failed: $!";
If you can't use File::Copy, you'll have to do the work yourself: open the original file, open the destination file, then print to the destination file as you read the original. You also have to remember to copy the permissions, owner, and group to the new file.
File::Copy が使えない場合、作業を自分自身でする必要があります: 元ファイルを開き、宛て先ファイルを開き、元ファイルから読んだものを宛て先 ファイルに書き込みます。 権限、所有者、グループも新しいファイルにコピーすることを忘れないように しなければなりません。
一時ファイルの名前を作り出すには?¶
If you don't need to know the name of the file, you can use open()
with undef
in place of the file name. In Perl 5.8 or later, the open()
function creates an anonymous temporary file:
もしファイル名を知る必要がないなら、open()
でファイル名の部分に undef
を指定します。 Perl 5.8 以降では、open()
関数は無名一時ファイルを作成します。
open my $tmp, '+>', undef or die $!;
Otherwise, you can use the File::Temp module.
さもなければ、File::Temp モジュールが使えます。
use File::Temp qw/ tempfile tempdir /;
my $dir = tempdir( CLEANUP => 1 );
($fh, $filename) = tempfile( DIR => $dir );
# or if you don't need to know the filename
my $fh = tempfile( DIR => $dir );
The File::Temp has been a standard module since Perl 5.6.1. If you don't have a modern enough Perl installed, 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:
File::Temp は Perl 5.6.1 以降標準モジュールになっています。 十分にモダンな Perl がインストールされていないなら、 IO::File モジュールにあるクラスメソッド new_tmpfile
を使って 読み書きのためにオープンされたファイルハンドルを取得します。 ファイルがどんな名前なのかを知る必要がない場合はこれを使ってください。
use IO::File;
my $fh = IO::File->new_tmpfile()
or die "Unable to make new temporary file: $!";
If you're committed to creating a temporary file by hand, use the process ID and/or the current time-value. If you need to have many temporary files in one process, use a counter:
一時ファイルの作成を手作業で行いたいのなら、 プロセスIDやその時点での時刻(あるいはこれら両方)を使用してください。 一つのプロセスで複数の一時ファイルを使用するのであれば、 カウンターを使用しましょう:
BEGIN {
use Fcntl;
use File::Spec;
my $temp_dir = File::Spec->tmpdir();
my $file_base = sprintf "%d-%d-0000", $$, time;
my $base_name = File::Spec->catfile($temp_dir, $file_base);
sub temp_file {
my $fh;
my $count = 0;
until( defined(fileno($fh)) || $count++ > 100 ) {
$base_name =~ s/-(\d+)$/"-" . (1 + $1)/e;
# O_EXCL is required for security reasons.
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 taking many, many strings. It is slower for just a few.
最も効率的なやり方はpack() と unpack() を使うものです。 これは文字列が大量にあるときにはsubstr() を 使うよりも高速です。 速度低下もほとんどありません。
Here is a sample chunk of code to break up and put back together again some fixed-format input lines, in this case from the output of a normal, Berkeley-style ps:
以下に挙げたのは幾つかの固定フォーマットをした入力行に対して 分解を行ったり書き戻しをするコード片の例で、出力は Berkeley形式のpsに準じています。
# sample input line:
# 15158 p5 T 0:00 perl /home/tchrist/scripts/now-what
my $PS_T = 'A6 A4 A7 A5 A*';
open my $ps, '-|', 'ps';
print scalar <$ps>;
my @fields = qw( pid tt stat time command );
while (<$ps>) {
my %process;
@process{@fields} = unpack($PS_T, $_);
for my $field ( @fields ) {
print "$field: <$process{$field}>\n";
}
print 'line=', pack($PS_T, @process{@fields} ), "\n";
}
We've used a hash slice in order to easily handle the fields of each row. Storing the keys in an array makes it easy to operate on them as a group or loop over them with for
. It also avoids polluting the program with global variables and using symbolic references.
それぞれの行のフィールドを簡単に扱うためにハッシュスライスを使いました。 キーを配列に保管するということで、これらをグループや for
によるループで 簡単に扱えます。 これはまた、グローバル変数とシンボリックリファレンスの使用でプログラムが 汚染されることを防ぎます。
ファイルハンドルをサブルーチンに局所化するには? サブルーチンにファイルハンドルを渡すには? ファイルハンドルの配列を作るには?¶
As of perl5.6, open() autovivifies file and directory handles as references if you pass it an uninitialized scalar variable. You can then pass these references just like any other scalar, and use them in the place of named handles.
perl5.6 から、open() にファイルハンドルやディレクトリハンドルとして未定義の スカラ変数を渡すと、これをリファレンスとして自動有効化します。 その後これらのリファレンスはその他のスカラと同じように扱え、名前付き ハンドルを指定する場所で使えます。
open my $fh, $file_name;
open local $fh, $file_name;
print $fh "Hello World!\n";
process_file( $fh );
If you like, you can store these filehandles in an array or a hash. If you access them directly, they aren't simple scalars and you need to give print
a little help by placing the filehandle reference in braces. Perl can only figure it out on its own when the filehandle reference is a simple scalar.
お好みなら、これらのファイルハンドルを配列やハッシュに保管することもできます。 これらを直接アクセスすると、これらは単なるスカラではないので、 ファイルハンドルを中かっこで囲むことで print
に少し助けを与える必要が あります。 Perl はファイルハンドルリファレンスが単純なスカラの場合にのみファイル ハンドルリファレンスと認識します。
my @fhs = ( $fh1, $fh2, $fh3 );
for( $i = 0; $i <= $#fhs; $i++ ) {
print {$fhs[$i]} "just another Perl answer, \n";
}
Before perl5.6, you had to deal with various typeglob idioms which you may see in older code.
perl5.6 より前では、古いコードにあるような、様々な型グロブの慣用法を 扱わなければなりません。
open FILE, "> $filename";
process_typeglob( *FILE );
process_reference( \*FILE );
sub process_typeglob { local *FH = shift; print FH "Typeglob!" }
sub process_reference { local $fh = shift; print $fh "Reference!" }
If you want to create many anonymous handles, you should check out the Symbol or IO::Handle modules.
大量の無名ハンドルを作りたいのであれば、 Symbol, FileHandle, IO::Handle といったモジュールを参照してください。
ファイルハンドルを間接的に扱うには?¶
An indirect filehandle is the use of something other than a symbol in a place that a filehandle is expected. Here are ways to get indirect filehandles:
間接ファイルハンドルは、あるファイルハンドルを期待している場所に置かれた シンボル以外のなにかを使っています。 以下に間接ファイルハンドルの例を挙げます:
$fh = SOME_FH; # bareword is strict-subs hostile
$fh = "SOME_FH"; # strict-refs hostile; same package only
$fh = *SOME_FH; # typeglob
$fh = \*SOME_FH; # ref to typeglob (bless-able)
$fh = *SOME_FH{IO}; # blessed IO::Handle from *SOME_FH typeglob
Or, you can use the new
method from one of the IO::* modules to create an anonymous filehandle and store that in a scalar variable.
あるいは、FileHandle モジュールや IO::* モジュールの new
メソッドを使い、 それをスカラー変数に格納します。
use IO::Handle; # 5.004 or higher
my $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 <FH>
diamond operator will accept either a named filehandle or a scalar variable containing one:
上記のように作成して、後は普通のファイルハンドルと同じように使います。 Perlがファイルハンドルを期待しているところではどこでも 間接ファイルハンドルを使うことができるでしょう。 間接ファイルハンドルは、ファイルハンドルを保持しているスカラー変数に すぎません。print
, open
, seek
のような関数や、 ダイヤモンド演算子 <FH>
はファイルハンドルもファイルハンドルを 保持しているスカラー変数の両方とも受け付けます。
($ifh, $ofh, $efh) = (*STDIN, *STDOUT, *STDERR);
print $ofh "Type it: ";
my $got = <$ifh>
print $efh "What was that: $got";
If you're passing a filehandle to a function, you can write the function in two ways:
ファイルハンドルを関数に渡した場合、渡される関数は二種類の書き方ができます:
sub accept_fh {
my $fh = shift;
print $fh "Sending to indirect filehandle\n";
}
Or it can localize a typeglob and use the filehandle directly:
型グロブを局所化して、ファイルハンドルを直接使うことも可能です:
sub accept_fh {
local *FH = shift;
print FH "Sending to localized filehandle\n";
}
Both styles work with either objects or typeglobs of real filehandles. (They might also work with strings under some circumstances, but this is risky.)
両方の形式とも、オブジェクトでも実際のファイルハンドルの型グロブでも 動作します。 (ある場合においては文字列でも可能ですが、これはちょっとリスクがあります。)
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 variable as a filehandle is illegal and won't even compile:
上の例では、ファイルハンドルを使う前にそれをスカラー変数に代入していました。 これは式でもなく、ハッシュや配列の添え字でもなく単純スカラ変数だけが print
やprintf
、ダイヤモンド演算子と一緒に使えるからです。 単純スカラ変数以外のものをファイルハンドルとして使うのは不正であり、 コンパイル時にエラーとなります:
my @fd = (*STDIN, *STDOUT, *STDERR);
print $fd[1] "Type it: "; # WRONG
my $got = <$fd[0]> # WRONG
print $fd[2] "What was that: $got"; # WRONG
With print
and printf
, you get around this by using a block and an expression where you would place the filehandle:
print
やprintf
の場合には、ブロックを使ってその中にファイルハンドルを 含む式を置くことによって対処することができます:
print { $fd[1] } "funny stuff\n";
printf { $fd[1] } "Pity the poor %x.\n", 3_735_928_559;
# Pity the poor deadbeef.
That block is a proper block like any other, so you can put more complicated code there. This sends the message out to one of two places:
これらのブロックは妥当なものですから、より複雑なコードをその中に 入れこむことができます。 以下の例はメッセージを二ヶ所のどちらかひとつに送り出します:
my $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 read a record just as <>
does. Given the initialization shown above for @fd, this would work, but only because readline() requires 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()と一緒に使うフッターのフォーマットのセットアップをする方法は?¶
There's no builtin way to do this, but perlform has a couple of techniques to make it possible for the intrepid hacker.
これを行うための組み込みの方法はありません; しかし perlform にはこれを 可能にするための、大胆不敵なハッカー向けのいくつかのテクニックがあります。
文字列に対して write() するには?¶
(contributed by brian d foy)
(brian d foy によって寄贈されました)
If you want to write
into a string, you just have to <open> a filehandle to a string, which Perl has been able to do since Perl 5.6:
文字列に write
したいなら、単に文字列へのファイルハンドルを open
する必要があります; これは Perl 5.6 から可能です:
open FH, '>', \my $string;
write( FH );
Since you want to be a good programmer, you probably want to use a lexical filehandle, even though formats are designed to work with bareword filehandles since the default format names take the filehandle name. However, you can control this with some Perl special per-filehandle variables: $^
, which names the top-of-page format, and $~
which shows the line format. You have to change the default filehandle to set these variables:
良いプログラマになりたいために、デフォルトフォーマット名がファイルハンドル名を とるためにフォーマットは裸の単語のファイルハンドルで動作するように 設計されていても、おそらくレキシカルファイルハンドルを使いたいでしょう。 しかし、これはいくつかのファイルハンドル単位 Perl 特殊変数で 制御できます; $^
は先頭ページのフォーマットの名前で、 $~
は行フォーマットです。 これらの変数を設定するにはデフォルトファイルハンドルを変更する必要があります:
open my($fh), '>', \my $string;
{ # set per-filehandle variables
my $old_fh = select( $fh );
$~ = 'ANIMAL';
$^ = 'ANIMAL_TOP';
select( $old_fh );
}
format ANIMAL_TOP =
ID Type Name
.
format ANIMAL =
@## @<<< @<<<<<<<<<<<<<<
$id, $type, $name
.
Although write can work with lexical or package variables, whatever variables you use have to scope in the format. That most likely means you'll want to localize some package variables:
write はレキシカル変数やパッケージ変数でも動作しますが、使う変数は フォーマットのスコープでなければなりません。 これはおそらくいくつかのパッケージ変数を ローカル化したいだろうということです:
{
local( $id, $type, $name ) = qw( 12 cat Buster );
write( $fh );
}
print $string;
There are also some tricks that you can play with formline
and the accumulator variable $^A
, but you lose a lot of the value of formats since formline
won't handle paging and so on. You end up reimplementing formats when you use them.
formline
および加算器変数 $^A
に関するいくつかのトリックもありますが、 formline
はページングなどが扱えないので、フォーマットの多くの値を 失います。 これらを使うと、最終的にフォーマットを再実装することに鳴ります。
ファイルハンドルを文字列としてオープンするには?¶
(contributed by Peter J. Holzer, [email protected])
(Peter J. Holzer, [email protected] によって寄贈されました)
Since Perl 5.8.0 a file handle referring to a string can be created by calling open with a reference to that string instead of the filename. This file handle can then be used to read from or write to the string:
Perl 5.8.0 から、ファイル名の代わりに文字列へのリファレンスで open を呼び出すことによって、文字列を参照するファイルハンドルを作れます。 このファイルハンドルは文字列を読み書きするために使えます:
open(my $fh, '>', \$string) or die "Could not open string for writing";
print $fh "foo\n";
print $fh "bar\n"; # $string now contains "foo\nbar\n"
open(my $fh, '<', \$string) or die "Could not open string for reading";
my $x = <$fh>; # $x now contains "foo\n"
With older versions of Perl, the IO::String module provides similar functionality.
Perl のより古いバージョンでは、IO::String モジュールが似たような機能を 提供します。
出力する数字にカンマを付加するには?¶
(contributed by brian d foy and Benjamin Goldberg)
(brian d foy と Benjamin Goldberg によって寄贈されました)
You can use Number::Format to separate places in a number. It handles locale information for those of you who want to insert full stops instead (or anything else that they want to use, really).
数値を区切るために Number::Format が使えます。 これは代わりにピリオド(または実際のところは使いたいもの何でも)を 挿入したい人のために、ロケール情報を扱います。
This subroutine will add commas to your number:
このサブルーチンは数値にカンマを追加します:
sub commify {
local $_ = shift;
1 while s/^([-+]?\d+)(\d{3})/$1,$2/;
return $_;
}
This regex from Benjamin Goldberg will add commas to numbers:
Benjamin Goldberg による正規表現は数値にカンマを追加します:
s/(^[-+]?\d+?(?=(?>(?:\d{3})+)(?!\d))|\G\d{3}(?=\d))/$1,/g;
It is easier to see with comments:
コメント付きで見ればより簡単になります:
s/(
^[-+]? # beginning of number.
\d+? # first digits before first comma
(?= # followed by, (but not included in the match) :
(?>(?:\d{3})+) # some positive multiple of three digits.
(?!\d) # an *exact* multiple, not x * 3 + 1 or whatever.
)
| # or:
\G\d{3} # after the last group, get three digits
(?=\d) # but they have to have more digits after them.
)/$1,/xg;
ファイル名の中にあるチルダ (~) を変換するには?¶
Use the <> (glob()
) operator, documented in perlfunc. Versions of Perl older than 5.6 require that you have a shell installed that groks tildes. Later versions of Perl have this feature built in. The File::KGlob module (available from CPAN) gives more portable glob functionality.
perlfuncで説明されている <> (glob()
) を使います。 5.6 より古いバージョンの Perl では、チルダを展開するシェルが既に インストールされていることを要求します。 最近のバージョンの Perl は内部にこの機能を持っています。 File::KGlob モジュール(CPANで入手可能)はより移植性のある glob 機能を提供します。
Within Perl, you may use this directly:
Perl を使えば、これを以下のように直接的に行えます。
$filename =~ s{
^ ~ # find a leading tilde
( # save this in $1
[^/] # a non-slash character
* # repeated 0 or more times (0 means me)
)
}{
$1
? (getpwnam($1))[7]
: ( $ENV{HOME} || $ENV{LOGDIR} )
}ex;
ファイルを読み書きモードでオープンしたときに内容をクリアしてしまうのはなぜ?¶
Because you're using something like this, which truncates the file then gives you read-write access:
ファイルを切り詰めて、その後で読み書きアクセスを提供するようなものを 使おうとしたからです。
open my $fh, '+>', '/path/name'; # WRONG (almost always)
Whoops. You should instead use this, which will fail if the file doesn't exist:
おっと。 ファイルがなかったときに失敗するような以下のやり方を使うべきでしょう:
open my $fh, '+<', '/path/name'; # open for update
Using ">" always clobbers or creates. Using "<" never does either. The "+" doesn't change this.
">"を使うと常に切り詰めか作成が行われます。 "<"を使った場合にはどちらも行いません。 "+"はこれらを変更することはありません。
Here are examples of many kinds of file opens. Those using sysopen
all assume that you've pulled in the constants from Fcntl:
ファイルをオープンする多くのやり方の例を以下に挙げます。 sysopen
を使っているものはすべて Fcntl から定数を引いているものと 仮定します:
use Fcntl;
To open file for reading:
ファイルを読み込みのためにオープンするには:
open my $fh, '<', $path or die $!;
sysopen my $fh, $path, O_RDONLY or die $!;
To open file for writing, create new file if needed or else truncate old file:
ファイルを書き出しのためにオープンし、ファイルがなければ新しく作り あれば古いファイルを切り詰めるには:
open my $fh, '>', $path or die $!;
sysopen my $fh, $path, O_WRONLY|O_TRUNC|O_CREAT or die $!;
sysopen my $fh, $path, O_WRONLY|O_TRUNC|O_CREAT, 0666 or die $!;
To open file for writing, create new file, file must not exist:
ファイルを書き出しのためにオープンし、ファイルを新たに作成するが 慈善に存在していてはいけない場合:
sysopen my $fh, $path, O_WRONLY|O_EXCL|O_CREAT or die $!;
sysopen my $fh, $path, O_WRONLY|O_EXCL|O_CREAT, 0666 or die $!;
To open file for appending, create if necessary:
ファイルを追加のためにオープンし、必要があればファイルを作成するには:
open my $fh, '>>' $path or die $!;
sysopen my $fh, $path, O_WRONLY|O_APPEND|O_CREAT or die $!;
sysopen my $fh, $path, O_WRONLY|O_APPEND|O_CREAT, 0666 or die $!;
To open file for appending, file must exist:
ファイルを追加のためにオープンするが、ファイルが事前になければいけない場合:
sysopen my $fh, $path, O_WRONLY|O_APPEND or die $!;
To open file for update, file must exist:
ファイルを更新のためにオープンするが、ファイルが事前になければいけない場合:
open my $fh, '+<', $path or die $!;
sysopen my $fh, $path, O_RDWR or die $!;
To open file for update, create file if necessary:
ファイルを更新のためにオープンし、必要があればファイルを作成する場合:
sysopen my $fh, $path, O_RDWR|O_CREAT or die $!;
sysopen my $fh, $path, O_RDWR|O_CREAT, 0666 or die $!;
To open file for update, file must not exist:
ファイルを更新のためにオープンするが、ファイルが事前に存在してはいけない場合:
sysopen my $fh, $path, O_RDWR|O_EXCL|O_CREAT or die $!;
sysopen my $fh, $path, O_RDWR|O_EXCL|O_CREAT, 0666 or die $!;
To open a file without blocking, creating if necessary:
ブロッキングなしでフィルをオープンし、必要があれば作成するには:
sysopen my $fh, '/foo/somefile', O_WRONLY|O_NDELAY|O_CREAT
or die "can't open /foo/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 perlopentut.
perlopentut も参照して下さい。
なぜ <*> を使ったときに "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.
<*> 演算子は(上述の)グロブ操作を行います。 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 File::Glob, one that doesn't use the shell to do globbing.
これに対処するには、Perl v5.6.0 以降にアップグレードするか グロブを readdir() とパターンを使って 自分自身で実行するか、あるいは File::Glob のようなモジュールを使って、 シェルによるグロブを行わないようにします。
名前の先頭に">"があったり末尾に空白があるようなファイルをオープンするには?¶
(contributed by Brian McCauley)
(Brian McCauley によって寄贈されました)
The special two-argument form of Perl's open() function ignores trailing blanks in filenames and infers the mode from certain leading characters (or a trailing "|"). In older versions of Perl this was the only version of open() and so it is prevalent in old code and books.
Perl の open() 関数の特殊な 2 引数形式はファイル名の末尾の空白を無視し、 先頭の文字 (あるいは末尾の "|") からモードを推論します。 古いバージョンの Perl ではこれが open() の唯一の形式なので、古いコードや 本には普及しています。
Unless you have a particular reason to use the two-argument form you should use the three-argument form of open() which does not treat any characters in the filename as special.
2 引数形式を使う特有の理由がない限り、ファイル名のどの文字も特別なものとして 扱わない 3 引数形式の open() を使うべきです。
open my $fh, "<", " file "; # filename is " file "
open my $fh, ">", ">file"; # filename is ">file"
信頼性のあるファイルのリネームをするには?¶
If your operating system supports a proper mv(1) utility or its functional equivalent, this works:
あなたの使っているオペレーティングシステムが、適切な mv(1) あるいは それと機能的に等価なプログラムをサポートしているのなら以下のような やり方が使えるでしょう:
rename($old, $new) or system("mv", $old, $new);
It may be more portable to use the File::Copy module instead. You just copy to the new file to the new name (checking return values), then delete the old one. This isn't really the same semantically as a rename()
, which preserves meta-information like permissions, timestamps, inode info, etc.
File::Copy モジュールを使うのが、より移植性があるかもしれません。 新しい名前で新しいファイルにコピーして(ここで戻り値をチェック)、 古いものを削除するだけです。 ただし、これはパーミッション、タイムスタンプ、inode情報といった メタ情報が保存されないので、rename() と同じにはなりません。
ファイルをロックするには?¶
Perl's builtin flock() function (see perlfunc for details) will call flock(2) if that exists, fcntl(2) if it doesn't (on perl version 5.004 and later), and lockf(3) if neither of the two previous system calls exists. On some systems, it may even use a different form of native locking. Here are some gotchas with Perl's flock():
Perl に組み込みの flock() 関数(詳しくは perlfunc を参照)は、flock(2) が あればそれを、なければ fcntl(2) を呼び出します(5.004 以降の場合); そして、 これら二つのシステムコールのいずれもない場合にはlockf(3)を 呼び出します。 一部のシステムでは、ネイティブなロッキングとは異なった使い方を するかもしれません。 以下に、Perl の flock() に関する罠(gotchas)を挙げておきます:
-
Produces a fatal error if none of the three system calls (or their close equivalent) exists.
三つのシステムコールがどれもなければ(もしくは等価なものがなければ)、 致命的エラーを生成します。
-
lockf(3) does not provide shared locking, and requires that the filehandle be open for writing (or appending, or read/writing).
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 ページを参照してください。 移植性のあるプログラムのためには、伝統的な振る舞いを仮定するのが最善です。 (そうしないなら、あなたは自分のシステムの癖(「仕様」とも呼ばれます)に 合わせて書くことをいつでも気にしないようにするべきです。 あなたの仕事をやり遂げる時に、盲目的に移植性を考慮するべきではありません。)
For more information on file locking, see also "File Locking" in perlopentut if you have it (new for 5.6).
ファイルのロッキングに関する詳細は<perlopentut/"FileLocking">を 参照してください(5.6 対応)。
なぜ単に open(FH, ">file.lock")とできないの?¶
A common bit of code NOT TO USE is this:
以下のようなことを してはいけません:
sleep(3) while -e 'file.lock'; # PLEASE DO NOT USE
open my $lock, '>', 'file.lock'; # THIS BROKEN CODE
This is a classic race condition: you take two steps to do something which must be done in one. That's why computer hardware provides an atomic test-and-set instruction. In theory, this "ought" to work:
これは古典的な競合状態です: あなたはここで、 一つのステップでやらなければならないことを二つのステップでやっています。 つまりこれが、コンピューターのハードウェアがアトミックな test-and-set の命令を備えている理由です。 理論的には、これを動作するにさせるには:
sysopen my $fh, "file.lock", O_WRONLY|O_EXCL|O_CREAT
or die "can't open file.lock: $!";
except that lamentably, file creation (and deletion) is not atomic over NFS, so this won't work (at least, not every time) over the net. Various schemes involving link() have been suggested, but these tend to involve busy-wait, which is also less than desirable.
残念なことに、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ページのヒットカウンターは役たたずなんだと いうことを言わなかったんですか? ヒットカウンターはヒットした数は数えず、時間を浪費し、さらに言えば 作者のうぬぼれを叩くのに役立つだけです。 乱数を取り出したほうがよっぽどましです。
Anyway, this is what you can do if you can't help yourself.
まあいずれにしろ、以下のようにしてやります:
use Fcntl qw(:DEFAULT :flock);
sysopen my $fh, "numfile", O_RDWR|O_CREAT or die "can't open numfile: $!";
flock $fh, LOCK_EX or die "can't flock numfile: $!";
my $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: $!";
Here's a much better web-page hit counter:
以下の例はもっと良い web ページヒットカウンターです:
$hits = int( (time() - 850_000_000) / rand(1_000) );
If the count doesn't impress your friends, then the code might. :-)
カウントがあなたの友達を感心させないのなら、このコードもそうでしょうね。:-)
ファイルの末尾にちょっとしたテキストを追加したいだけなんです。それでもロックが必要なの?¶
If you are on a system that correctly implements flock
and you use the example appending code from "perldoc -f flock" everything will be OK even if the OS you are on doesn't implement append mode correctly (if such a system exists). So if you are happy to restrict yourself to OSs that implement flock
(and that's not really much of a restriction) then that is what you should do.
もし flock()
が正しく実装されているシステムを使っていて、 "perldoc -f flock" にある追加コードの例を使っているなら、たとえ使っている OS が追加モードを正しく実装していない(もしそのようなシステムがあれば) 場合でも、全てうまくいきます。 従って、実行する OS を flock()
が実装されているものに制限してもよいなら (これは実際のところそれほど大きな制限ではありません)、そうするべきです。
If you know you are only going to use a system that does correctly implement appending (i.e. not Win32) then you can omit the seek
from the code in the previous answer.
正しく追加を実装している(つまり Win32 ではない)システムを 利用しているとわかっている場合のみ、以前の答えのコードから seek()
を 省略できます。
If you know you are only writing code to run on an OS and filesystem that does implement append mode correctly (a local filesystem on a modern Unix for example), and you keep the file in block-buffered mode and you write less than one buffer-full of output between each manual flushing of the buffer then each bufferload is almost guaranteed to be written to the end of the file in one chunk without getting intermingled with anyone else's output. You can also use the syswrite
function which is simply a wrapper around your system's write(2)
system call.
追加モードを正しく実装している OS とファイルシステム(例えば最近の Unix の ローカルファイルシステム)でのみ実行されることがわかっていて、 ファイルをブロックバッファモードのままに維持していて、 手動でのバッファのフラッシュの間にバッファがいっぱいになるほどの 書き込みを行わない場合は、それぞれのバッファは他からの出力が混ざることなく、 ファイルの末尾にひとつの塊で書き込まれることがほとんど保証されます。 write(2)
システムコールの単なるラッパである syswrite()
を使うことも できます。
There is still a small theoretical chance that a signal will interrupt the system-level write()
operation before completion. There is also a possibility that some STDIO implementations may call multiple system level write()
s even if the buffer was empty to start. There may be some systems where this probability is reduced to zero, and this is not a concern when using :perlio
instead of your system's STDIO.
未だに、理論的にはシステムレベルの write()
操作が終了前にシグナルが割り込む 可能性が少しあります。 STDIO の実装によっては、バッファが空からスタートしても、複数のシステム レベルの write()
を呼び出すものがある可能性があります。 この確率を 0 にしているシステムもあるかもしれませんし、 システムの STDIO ではなく :perlio
を使うときには関係ありません。
バイナリファイルをランダムに更新するには?¶
If you're just trying to patch a binary, in many cases something as simple as this works:
単にバイナリにパッチをあてたいというのなら、多くの場合は以下のように 単純にできます:
perl -i -pe 's{window manager}{window mangler}g' /usr/bin/emacs
However, if you have fixed sized records, then you might do something more like this:
もし固定サイズのレコードを持っているのなら、以下のようにして行うことも できます:
my $RECSIZE = 220; # size of record, in bytes
my $recno = 37; # which record to update
open my $fh, '+<', 'somewhere' or die "can't update somewhere: $!";
seek $fh, $recno * $RECSIZE, 0;
read $fh, $record, $RECSIZE == $RECSIZE or die "can't read record $recno: $!";
# munge the record
seek $fh, -$RECSIZE, 1;
print $fh $record;
close $fh;
Locking and error checking are left as an exercise for the reader. Don't forget them or you'll be quite sorry.
ロックとエラーチェックは読者の練習として残してあります。 これらを行うことを忘れてはいけません; さもなくばとても 後悔することになるでしょう。
perl でファイルのタイムスタンプを取得するには?¶
If you want to retrieve the time at which the file was last read, written, or had its meta-data (owner, etc) changed, you use the -A, -M, or -C file test operations as documented in perlfunc. These retrieve the age of the file (measured against the start-time of your program) in days as a floating point number. Some platforms may not have all of these times. See perlport for details. To retrieve the "raw" time in seconds since the epoch, you would call the stat function, then use localtime()
, gmtime()
, or POSIX::strftime()
to convert this into human-readable form.
そのファイルが最後に読み込まれ、書き出され、そのメタデータ(所有者など)が 変更された時刻を取得したいのであれば、perlfunc に説明がある -A, -M, -C といったファイルテスト演算子を使います。 これらはファイルの年齢(あなたのプログラムの実行開始に対するもの)を 浮動小数点数で表現された日数で取得します。 プラットフォームによってはこれら全ては利用できないものもあります。 詳しくは perlport を参照してください。 紀元からの経過秒数による「生の」時間を得るためには、stat 関数を呼び出して、 その値を localtime()
、gmtime()
、POSIX::strftime()
を使って人が 読めるような形へ変換します。
Here's an example:
例を挙げましょう:
my $write_secs = (stat($file))[9];
printf "file %s updated at %s\n", $file,
scalar localtime($write_secs);
If you prefer something more legible, use the File::stat module (part of the standard distribution in version 5.004 and later):
もっと読みやすいものがお好みなら、File::statモジュールを使います (これは5.004以降の標準配布キットに含まれています)。
# error checking left as an exercise for reader.
use File::stat;
use Time::localtime;
my $date_string = ctime(stat($file)->mtime);
print "file $file updated at $date_string\n";
The POSIX::strftime() approach has the benefit of being, in theory, independent of the current locale. See perllocale for details.
POSIX::strftime()アプローチは理論的にはロカール非依存で、 使う価値があります。詳しくはperllocaleを参照してください。
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";
}
my $timestamp = shift;
my($atime, $mtime) = (stat($timestamp))[8,9];
utime $atime, $mtime, @ARGV;
Error checking is, as usual, left as an exercise for the reader.
例によって、エラーチェックは読者の練習として残してあります。
The perldoc for utime also has an example that has the same effect as touch(1) on files that already exist.
utime の perldoc には、既にある ファイルに対する touch(1) と同じ 効果のある例があります。
Certain file systems have a limited ability to store the times on a file at the expected level of precision. For example, the FAT and HPFS filesystem are unable to create dates on files with a finer granularity than two seconds. This is a limitation of the filesystems, not of utime().
予測されたレベルの制度でファイルの時刻を保存する能力が制限された ファイルシステムもあります。 例えば、FAT と HPFS は 2 秒よりも細かい精度でファイルの作成時刻を 設定できません。 これはファイルシステムの制限であり、utime() での問題でありません。
複数のファイルを一度に表示するには?¶
To connect one filehandle to several output filehandles, you can use the IO::Tee or Tie::FileHandle::Multiplex modules.
一つのファイルハンドルを複数の出力ファイルハンドルに接続するには、 IO::Tee や Tie::FileHandle::Multiplex のモジュールが使えます。
If you only have to do this once, you can print individually to each filehandle.
これをする必要があるのが一回だけなら、以下のようにしてできます:
for my $fh ($fh1, $fh2, $fh3) { print $fh "whatever\n" }
ファイル全体を一度に読みこむには?¶
The customary Perl approach for processing all the lines in a file is to do so one line at a time:
Perl においてファイルの全ての行を処理するための慣例的な手法は 1 行ずつ読みこむことです:
open my $input, '<', $file or die "can't open $file: $!";
while (<$input>) {
chomp;
# do something with $_
}
close $input or 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行ずつ処理するという (ほとんど常に、でないのなら)しばしば誤った手法よりも 遥かに効率的です。 それでもファイル全体を読み込みたいなら以下のようにします:
my @lines = <INPUT>;
You should think long and hard about why you need everything loaded at once. It's just not a scalable solution.
本当に全てを一度に読み込む必要があるのかを良く考えるべきです。 これは単にスケールの問題ではありません。
If you "mmap" the file with the File::Map module from CPAN, you can virtually load the entire file into a string without actually storing it in memory:
CPAN にある File::Map モジュールでファイルを "mmap" した場合、 実際にメモリに保管することなく仮想的にファイル全体を文字列に読み込めます:
use File::Map qw(map_file);
map_file my $string, $filename;
Once mapped, you can treat $string
as you would any other string. Since you don't necessarily have to load the data, mmap-ing can be very fast and may not increase your memory footprint.
一度マッピングされると、$string
を他の文字列と同じように扱えます。 データを読み込む必要性がないので、mmap によってとても速くなり メモリ消費を増やさないようにもできます。
You might also find it more fun to use the standard Tie::File module, or the DB_File module's $DB_RECNO
bindings, which allow you to tie an array to a file so that accessing an element of the array actually accesses the corresponding line in the file.
標準の Tie::File モジュールか DB_File モジュールの $DB_RECNO
バインディングの方がより好ましいと考えるかもしれません; これは配列とファイルを結び付けるので、配列にアクセスすると、実際には ファイルの対応する行にアクセスすることになります。
If you want to load the entire file, you can use the Path::Tiny module to do it in one simple and efficient step:
ファイル全体を読み込みたいなら、これを単純で効果的な 1 ステップで行うための、 Path::Tiny モジュールが使えます。
use Path::Tiny;
my $all_of_it = path($filename)->slurp; # entire file in scalar
my @all_lines = path($filename)->lines; # one line per element
Or you can read the entire file contents into a scalar like this:
または以下のようにファイルハンドルの内容全体を一つのスカラ変数に 読み込むことができます:
my $var;
{
local $/;
open my $fh, '<', $file or die "can't open $file: $!";
$var = <$fh>;
}
That temporarily undefs your record separator, and will automatically close the file at block exit. If the file is already open, just use this:
これは一時的にレコードセパレータを未定義にし、ブロックを出るときに 自動的にファイルをクローズします。ファイルが既にオープンしているなら、 単に以下のようにします:
my $var = do { local $/; <$fh> };
You can also use a localized @ARGV
to eliminate the open
:
open
を削除するためにローカル化された @ARGV
も使えます:
my $var = do { local( @ARGV, $/ ) = $file; <> };
For ordinary files you can also use the read
function.
通常のファイルの場合は read
関数も使えます。
read( $fh, $var, -s $fh );
That third argument tests the byte size of the data on the $fh
filehandle and reads that many bytes into the buffer $var
.
3 番目の引数は $fh
ファイルハンドルのデータのバイトサイズをテストし、 そのバイト数だけバッファ $var
に読み込みます。
ファイルをパラグラフ毎に読み込むには?¶
Use the $/
variable (see perlvar for details). You can either set it to ""
to eliminate empty paragraphs ("abc\n\n\n\ndef"
, for instance, gets treated as two paragraphs and not three), or "\n\n"
to accept empty paragraphs.
$/
という変数を使います(詳しくは 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"
は二つのパラグラフです。
ファイルから 1 文字だけ読み出すには? キーボードからは?¶
You can use the builtin getc()
function for most filehandles, but it won't (easily) work on a terminal device. For STDIN, either use the Term::ReadKey module from CPAN or use the sample code in "getc" in perlfunc.
ほとんどのファイルハンドルに対してはgetc()
という組み込み関数を 使うことができますが、これはターミナルデバイスに対してはうまくいきません。 STDIN に対しては、CPAN にある Term::ReadKey を使うか "getc" in perlfuncにあるサンプルコードを使います。
If your system supports the portable operating system programming interface (POSIX), you can use the following code, which you'll note turns off echo processing as well.
あなたの使っているシステムがPOSIXをサポートしているのなら、echo を オフにしているのを気をつけながら以下のようなコードでできます。
#!/usr/bin/perl -w
use strict;
$| = 1;
for (1..4) {
print "gimme: ";
my $got = getone();
print "--> $got\n";
}
exit;
BEGIN {
use POSIX qw(:termios_h);
my ($term, $oterm, $echo, $noecho, $fd_stdin);
my $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() }
The Term::ReadKey module from CPAN may be easier to use. Recent versions include also support for non-portable systems as well.
CPAN にある Term::ReadKey モジュールならもっと簡単に使えます。 最新のバージョンでは non-portable システムのサポートも含まれています:
use Term::ReadKey;
open my $tty, '<', '/dev/tty';
print "Gimme a char: ";
ReadMode "raw";
my $key = ReadKey 0, $tty;
ReadMode "normal";
printf "\nYou said %s, char number %03d\n",
$key, ord $key;
あるファイルハンドルが読み込み待ちの文字を待っているかどうかを知るには?¶
The very first thing you should do is look into getting the Term::ReadKey extension from CPAN. As we mentioned earlier, it now even has limited support for non-portable (read: not open systems, closed, proprietary, not POSIX, not Unix, etc.) systems.
あなたがすべき第一のことは、CPAN にある Term::ReadKey を入手することです。 今では閉鎖的な独占システム (オープンなシステムではない、POSIX でもなく、UNIX でもないような…)に 対する限定的なサポートさえあります。
You should also check out the Frequently Asked Questions list in comp.unix.* for things like this: the answer is essentially the same. It's very system-dependent. Here's one solution that works on BSD systems:
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);
}
If you want to find out how many characters are waiting, there's also the FIONREAD ioctl call to be looked at. The h2ph tool that comes with Perl tries to convert C include files to Perl code, which can be require
d. FIONREAD ends up defined as a function in the sys/ioctl.ph file:
どの位のキャラクターが待っているのかを知りたいのであれば、 FIONREAD ioctl 呼び出しを使うことができます。 h2phツールは C のインクルードファイルを Perl に変換するもので、その結果は require
によって呼び出すことが可能です。 FIONREAD は sys/ioctl.ph にある関数として定義されます。
require 'sys/ioctl.ph';
$size = pack("L", 0);
ioctl(FH, FIONREAD(), $size) or die "Couldn't call ioctl: $!\n";
$size = unpack("L", $size);
If h2ph wasn't installed or doesn't work for you, you can grep the include files by hand:
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 dependent
$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 that sockets, pipes, and tty devices work, but not files.
FIONREAD はストリームに接続されたファイルハンドルを要求します; これはソケット、パイプ、あるいは tty デバイスではうまく動作しますが、 ファイルに対しては うまく行きません。
How do I do a tail -f
in perl? ¶
(perlで tail -f
をするには?)
First try
まず最初に以下のものを試してみてください
seek($gw_fh, 0, 1);
The statement seek($gw_fh, 0, 1)
doesn't change the current position, but it does clear the end-of-file condition on the handle, so that the next <$gw_fh>
makes Perl try again to read something.
seek($gw_fh, 0, 1)
という文はカレントの位置を変更しませんが、 そのファイルハンドルにおける end-fo-file 状態を解除します; このため、次に <$gw_fh>
とすると Perl は再度何かを読もうとするのです。
If that doesn't work (it relies on features of your stdio implementation), then you need something more like this:
このやり方がうまくいかない(これはあなたの使っている stdio が実装している 機能に依存しています)のなら、以下のようにする必要があるでしょう:
for (;;) {
for ($curpos = tell($gw_fh); <$gw_fh>; $curpos =tell($gw_fh)) {
# search for some stuff and put it into files
}
# sleep for a while
seek($gw_fh, $curpos, 0); # seek to where we had been
}
If this still doesn't work, look into the clearerr
method from IO::Handle, which resets the error and end-of-file states on the handle.
これでもうまく行かなければ、IO::Handle の clearerr
メソッドを 検討してください; これはハンドルのエラーと end-of-file 状態をリセットします。
There's also a File::Tail module from CPAN.
CPAN には File::Tail モジュールがあります。
Perl でファイルハンドルの dup() をするには?¶
If you check "open" in perlfunc, you'll see that several of the ways to call open() should do the trick. For example:
"open" in perlfunc を見れば、それを行うための open() の呼び出し方が 何通りもあることに気がつくでしょう。 例えば:
open my $log, '>>', '/foo/logfile';
open STDERR, '>&', $log;
Or even with a literal numeric descriptor:
あるいは、リテラルの数値記述子を使います:
my $fd = $ENV{MHCONTEXTFD};
open $mhcontext, "<&=$fd"; # like fdopen(3S)
Note that "<&STDIN" makes a copy, but "<&=STDIN" makes an alias. That means if you close an aliased handle, all aliases become inaccessible. This is not true with a copied one.
"<&STDIN" はコピーを作成し、"<&=STDIN" がエイリアスを作成するということに 注意してください。 これはつまり、あなたがエイリアスが作成されたファイルハンドルを クローズすると、エイリアスはすべてアクセスできなくなります。 これはコピーの場合にはそうはなりません。
Error checking, as always, has been left as an exercise for the reader.
エラーチェックはいつものように読者の練習のために残してあります。
数値によるファイル記述子をクローズするには?¶
If, for some reason, you have a file descriptor instead of a filehandle (perhaps you used POSIX::open
), you can use the close()
function from the POSIX module:
もし、何らかの理由で、ファイルハンドルではなくファイル記述子を持っている場合 (おそらく POSIX::open
を使ったのでしょう)、POSIX モジュールの close()
関数が使えます:
use POSIX ();
POSIX::close( $fd );
This should rarely be necessary, as the Perl close()
function is to be used for things that Perl opened itself, even if it was a dup of a numeric descriptor as with MHCONTEXT
above. But if you really have to, you may be able to do this:
Perl の close()
関数は、先の例にあった MHCNOTEXT
のように数値 記述子を使って dup したものでさえも含めて、 Perl 自身がオープンしたものに対して使うことができますから、 その必要はほとんどないはずです。 しかし本当にそうする必要があるのなら、以下のようにできるでしょう:
require 'sys/syscall.ph';
my $rc = syscall(SYS_close(), $fd + 0); # must force numeric
die "can't sysclose $fd: $!" unless $rc == -1;
Or, just use the fdopen(3S) feature of open()
:
あるいは、単に open()
の fdopen(3S) 機能を使います:
{
open my $fh, "<&=$fd" or die "Cannot reopen fd=$fd: $!";
close $fh;
}
なぜ DOS のパスで "C:\temp\foo" が使えないの? なぜ `C:\temp\foo.exe` はうまくいかないの?¶
Whoops! You just put a tab and a formfeed into that filename! Remember that within double quoted strings ("like\this"), the backslash is an escape character. The full list of these is in "Quote and Quote-like Operators" in perlop. Unsurprisingly, you don't have a file called "c:(tab)emp(formfeed)oo" or "c:(tab)emp(formfeed)oo.exe" on your legacy DOS filesystem.
おーっと! ファイル名にタブや改ページを入れてしまいましたね! "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. POSIX paths are more portable, too.
文字列を括るのにシングルクォートを使うか、もしくは(こちらが好ましい) スラッシュを使ってください。 全ての DOS および Windows は、MS-DOS 2.0 以降、パス中にある /
と \
を 同じに扱いますから、Perl -- もしくは POSIX シェル、ANSI C と C++, awk, tcl, Java, Python などでも壊すことなく使えます。 POSIX パスもより移植性に富んでいます。
なぜ glob("*.*") で全てのファイルを得られないの?¶
Because even on non-Unix ports, Perl's glob function follows standard Unix globbing semantics. You'll need glob("*")
to get all (non-hidden) files. This makes glob() portable even to legacy systems. Your port may include proprietary globbing functions as well. Check its documentation for details.
非 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 のバグじゃないの?)
This is elaborately and painstakingly described in the file-dir-perms article in the "Far More Than You Ever Wanted To Know" collection in http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz .
Know" collection in http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz の "Far More Than You Ever Wanted To Know" にある file-dir-perms の 記事が教育的、かつ懇切丁寧にこの問題を説明しています。
The executive summary: learn how your filesystem works. The permissions on a file say what can happen to the data in that file. The permissions on a directory say what can happen to the list of files in that directory. If you delete a file, you're removing its name from the directory (so the operation depends on the permissions of the directory, not of the file). If you try to write to the file, the permissions of the file govern whether you're allowed to.
簡単なまとめ: あなたの使っているファイルシステムがどのように動作しているのかを 考えてください。 ファイルに対するパーミッションはそのファイルにあるデータに何が できるかということを表しています。 ディレクトリに対するパーミッションは、そのディレクトリにあるファイルリストに 対して何ができるのかということを表しています。 ファイルを削除したとき、そのファイルに対する名前がディレクトリから 取り除かれます(したがってこの操作はファイルに対するパーミッションでは なく、ディレクトリに対するパーミッションに依存しているのです)。 ファイルに対して書き込みを行おうとすると、ファイルに対するパーミッションが それができるかどうかを決定します。
あるファイルからランダムに行を選択するには?¶
Short of loading the file into a database or pre-indexing the lines in the file, there are a couple of things that you can do.
ファイルをデータベースに読み込ませたり、ファイルの行数のインデックスを 予め作っておく以外では、これをするためには 2 種類の方法があります:
Here's a reservoir-sampling algorithm from the Camel Book:
以下に示すのはらくだ本にあったリザーバサンプリングアルゴリズムです:
srand;
rand($.) < 1 && ($line = $_) while <>;
This has a significant advantage in space over reading the whole file in. You can find a proof of this method in The Art of Computer Programming, Volume 2, Section 3.4.2, by Donald E. Knuth.
これは、ファイル全体を読み込んで処理するやり方に比べて使用する空間の 大きさにおいて明らかなアドバンテージがあります。 この手法の証明は Donald E. Knuth の The Art of Computer Programming, Volume 2, Section 3.4.2 に あります。
You can use the File::Random module which provides a function for that algorithm:
このアルゴリズムの関数を提供する File::Random モジュールが使えます:
use File::Random qw/random_line/;
my $line = random_line($filename);
Another way is to use the Tie::File module, which treats the entire file as an array. Simply access a random array element.
もう一つの方法は Tie::File モジュールを使うことで、これはファイル全体を 配列として扱います。 単にランダムに配列にアクセスしてください。
行の配列を出力したときになぜ余計なスペースがつくの?¶
(contributed by brian d foy)
(brian d foy によって寄贈されました)
If you are seeing spaces between the elements of your array when you print the array, you are probably interpolating the array in double quotes:
もし配列を表示したときに配列の要素の間に空白が付くなら、 おそらく配列をダブルクォートで展開しています:
my @animals = qw(camel llama alpaca vicuna);
print "animals are: @animals\n";
It's the double quotes, not the print
, doing this. Whenever you interpolate an array in a double quote context, Perl joins the elements with spaces (or whatever is in $"
, which is a space by default):
これは print
ではなくダブルクォートが行っています。 ダブルクォートコンテキストで配列を展開するときはいつでも、 Perl は要素を空白(正確にはデフォルトが空白の $"
)を使って 結合します:
animals are: camel llama alpaca vicuna
This is different than printing the array without the interpolation:
これは、配列を変数展開なしに表示するのとは異なります:
my @animals = qw(camel llama alpaca vicuna);
print "animals are: ", @animals, "\n";
Now the output doesn't have the spaces between the elements because the elements of @animals
simply become part of the list to print
:
今度は要素の間には空白はありません; @animals
の要素は単に print
へのリストの一部となっているからです:
animals are: camelllamaalpacavicuna
You might notice this when each of the elements of @array
end with a newline. You expect to print one element per line, but notice that every line after the first is indented:
@array
の要素のそれぞれが改行で終わっているときにこのことに 気付いたかも知れません。 行毎に 1 要素を表示することを想定したけれども、最初の以外の行が インデントされることに気付きます:
this is a line
this is another line
this is the third line
That extra space comes from the interpolation of the array. If you don't want to put anything between your array elements, don't use the array in double quotes. You can send it to print without them:
追加の空白は配列の展開による物です。配列の要素の間に何も表示したくない 場合、ダブルクォートの中で配列を使わないでください。 使わなくても表示できます:
print @lines;
ディレクトリツリーを辿るには?¶
(contributed by brian d foy)
(brian d foy によって寄贈されました)
The File::Find module, which comes with Perl, does all of the hard work to traverse a directory structure. It comes with Perl. You simply call the find
subroutine with a callback subroutine and the directories you want to traverse:
Perl と共に配布されている File::Find モジュールが、ディレクトリ構造を 辿るための大変な作業の全てを行います。 これは Perl と共に配布されています。 単に、コールバックサブルーチンと、辿りたいディレクトリを指定して find
サブルーチンを呼び出します:
use File::Find;
find( \&wanted, @directories );
sub wanted {
# full path in $File::Find::name
# just filename in $_
... do whatever you want to do ...
}
The File::Find::Closures, which you can download from CPAN, provides many ready-to-use subroutines that you can use with File::Find.
CPAN からダウンロードできる File::Find::Closures は、 File::Find と共に使える、たくさんの「すぐに使える」サブルーチンを 提供します。
The File::Finder, which you can download from CPAN, can help you create the callback subroutine using something closer to the syntax of the find
command-line utility:
CPAN からダウンロードできる File::Finder は、 find
コマンドラインユーティリティの文法により近い形の コールバックサブルーチンを作ることを手助けします:
use File::Find;
use File::Finder;
my $deep_dirs = File::Finder->depth->type('d')->ls->exec('rmdir','{}');
find( $deep_dirs->as_options, @places );
The File::Find::Rule module, which you can download from CPAN, has a similar interface, but does the traversal for you too:
CPAN からダウンロードできる File::Find::Rule モジュールは、 同じようなインターフェースを持ちますが、ファイルを辿ることもします:
use File::Find::Rule;
my @files = File::Find::Rule->file()
->name( '*.pm' )
->in( @INC );
ディレクトリツリーを削除するには?¶
(contributed by brian d foy)
(brian d foy によって寄贈されました)
If you have an empty directory, you can use Perl's built-in rmdir
. If the directory is not empty (so, no files or subdirectories), you either have to empty it yourself (a lot of work) or use a module to help you.
もし、ディレクトリが空なら、Perl 組み込みの rmdir
が使えます。 もしディレクトリが空(ファイルもサブディレクトリもない)ではないなら、 自力で空にする(多くの作業が必要です)か、助けてくれるモジュールを 使う必要があります。
The File::Path module, which comes with Perl, has a remove_tree
which can take care of all of the hard work for you:
Perl と共に配布されている File::Path モジュールには、大変な作業の全てを 代わりに処理してくれる remove_tree
があります:
use File::Path qw(remove_tree);
remove_tree( @directories );
The File::Path module also has a legacy interface to the older rmtree
subroutine.
File::Path モジュールには、より古い rmtree
サブルーチンへの レガシーなインターフェースもあります。
ディレクトリ全体をコピーするには?¶
(contributed by Shlomi Fish)
(Shlomi Fish によって寄贈されました)
To do the equivalent of cp -R
(i.e. copy an entire directory tree recursively) in portable Perl, you'll either need to write something yourself or find a good CPAN module such as File::Copy::Recursive.
cp -R
と等価なこと (つまり、ディレクトリツリー全体を再帰的に コピーする) を移植性のある Perl でするには、自分で書くか、 File::Copy::Recursive のようなよい CPAN モジュールを見つける必要が あります。
AUTHOR AND COPYRIGHT¶
Copyright (c) 1997-2010 Tom Christiansen, Nathan Torkington, and other authors as noted. All rights reserved.
This documentation is free; you can redistribute it and/or modify it under the same terms as Perl itself.
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.