NAME

perlfaq5 - ファイルとフォーマット

DESCRIPTION

このセクションでは、入出力と“f”に関する事柄: ファイルハンドル (filehandle)、フラッシング(flushing)、フォーマット(format)、 フッター(footer)を扱います。

出力ファイルハンドルを flush/unbufferするには? なぜ私はこれをやらなければならないの?

(brian d foy によって寄贈されました)

http://perl.plover.com/FAQs/Buffering.html にある Mark Jason Dominus の "Suffering From Buffering" を読みたいかもしれません。

通常 Perl は出力をバッファリングするので、ちょっと出力する毎に システムコールを呼び出したりはしません。 出力回数を節約することで、負荷の高いシステムコールの回数を減らします。 例えば、このコード片では、プログラムが1行の処理する毎に、プログラムの 実行状況を示すためにドットを表示します。 Perl は 行毎にドットを表示せず、出力をバッファリングするので、 長い間待った後に 50 文字のドットが一気に表示されます:

        # long wait, then row of dots all at once
        while( <> ) {
                print ".";
                print "\n" unless ++$count % 50;

                #... expensive line processing operations
                }

これを回避するには、出力ファイルハンドル (この場合は STDOUT) の バッファリングを留める必要があります。 特殊変数 $| を真の値にセットします (記憶法: パイプをホットな状態にしておくために使う。):

        $|++;

        # dot shown immediately
        while( <> ) {
                print ".";
                print "\n" unless ++$count % 50;

                #... expensive line processing operations
                }

$| はファイルハンドル単位の特殊変数の一つで、ファイルハンドル毎に この値のコピーを保持しています。 例えば、標準出力と標準エラーをまとめたいなら、それぞれのバッファリングを 解除する必要があります(但し、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;
                }

$| 特殊変数に加えて、binmode を使ってファイルハンドルに :unix 層をつけることでバッファリングしないようにできます:

        binmode( STDOUT, ":unix" );

        while( 1 ) {
                sleep 1;
                print ".";
                print "\n" unless ++$count % 50;
                }

出力層に関するさらなる情報については、perlfuncbinmode および open のエントリと、PerlIO モジュールの文書を参照してください。

IO::Handle かその派生クラスを使っているなら、ファイルハンドルの設定を 変更するために autoflush メソッドを呼び出せます:

        use IO::Handle;
        open my( $io_fh ), ">", "output.txt";
        $io_fh->autoflush(1);

IO::Handle オブジェクトには flush メソッドもあります。 いつでも自動バッファリングなしにバッファをフラッシュできます

        $io_fh->flush;

ファイルの一行を変更する/ファイルのある行を削除する/ファイルの中程で一行挿入する/ファイルの先頭に追加するには?

(brian d foy によって寄贈されました)

テキストファイルの行を挿入、変更、削除するための基本的な考え方は、 ファイルを変更したい地点まで読み込んで表示し、変更を行い、ファイルの 残りを読み込んで表示するというものです。 (特にレコード入力セパレータ $/ は変更可能なので) Perl は行に対する ランダムアクセスは提供していませんが、Tie::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;

基本形の中に、行の追加、修正、削除という必要な作業を追加します。

先頭に追加するには、既存の行を表示するループに入る前に追加行を表示します。

        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;

既にある行を変更するには、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;

特定の行だけ変更するには、入力行番号 $. が便利です。 まず、変更したい行まで読み込んで表示します。 次に、変更したい行を 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 $_;
                }

行を読み飛ばすには、ループ制御を使います。 この例の next はコメント行を読み飛ばし、last は、__END____DATA__ のどちらかに出会った後は全ての処理を停止します。

        while( <$in> )
                {
                next if /^\s+#/;             # skip comment lines
                last if /^__(END|DATA)__$/;  # stop at end of code marker
                print $out $_;
                }

同じようなことは、next を使って表示したくない行を飛ばすことで行えます。

        while( <$in> )
                {
                next unless $. % 5;
                print $out $_;
                }

もし、なんらかの変わった理由により、一行ずつ処理するのではなく、本当に ファイル全体を一度に読み込みたい場合、(メモリがあれば!)以下のようにして できます:

        open my $in,  '<',  $file      or die "Can't read old file: $!"
        open my $out, '>', "$file.new" or die "Can't write new file: $!";

        my @lines = do { local $/; <$in> }; # slurp!

                # do your magic here

        print $out @lines;

File::SlurpTie::File のようなモジュールもこの助けになります。 しかし、もし可能なら、ファイル全体を一度に読み込むのは避けてください。 Perl は OS から確保したメモリを、プロセスが終わるまで返しません。

ファイルをその場で変更するために、Perl の一行野郎も使えます。 以下の例では、inFile.txt にある全ての 'Fred' を 'Barney' に変更し、 ファイルを新しい内容で上書きします。 -p スイッチ付きの場合、-e で指定したコードを while ループで包み、 -i はその場編集を有効にします。 現在行は $_ に入っています。 -p があると、Perl はループの最後に $_ の値を自動的に表示します。 さらなる詳細については perlrun を参照してください。

        perl -pi -e 's/Fred/Barney/' inFile.txt

inFile.txt をバックアップするには、-i で追加する拡張子を指定します:

        perl -pi.bak -e 's/Fred/Barney/' inFile.txt

5 行目だけを変更するには、入力行番号 $. をチェックするテストを追加し、 テストに通過した場合にのみ操作を行います:

        perl -pi -e 's/Fred/Barney/ if $. == 5' inFile.txt

特定の行の前に追加するには、Perl が $_ を表示する前に行を追加します:

        perl -pi -e 'print "Put before third line\n" if $. == 3' inFile.txt

現在行はループの終わりに表示されるので、ファイルの先頭に行を 追加することもできます:

        perl -pi -e 'print "Put before first line\n" if $. == 1' inFile.txt

既にファイルにある行の後に行を追加するには、-n スイッチを使います。 これは -p と同様ですが、ループの終わりに $_ を表示しないので、 自分自身で表示する必要があります。 この場合、まず $_ を表示し、それから追加したい行を表示します。

        perl -ni -e 'print; print "Put after fifth line\n" if $. == 5' inFile.txt

行を削除するには、必要なものだけを表示します:

        perl -ni -e 'print unless /d/' inFile.txt

                ... or ...

        perl -pi -e 'next unless /d/' inFile.txt

あるファイルの行数を数えるには?

(brian d foy によって寄贈されました)

概念的には、ファイルの行数を数える一番易しい方法は単にそれを読み込んで それを数えます:

        my $count = 0;
        while( <$fh> ) { $count++; }

しかし、Perl には最後に読み込んだファイルハンドルの現在の行番号である $. 変数が既にあるので、自分自身で数える必要はありません:

        1 while( <$fh> );
        my $count = $.;

$. を使いたいなら、以下のように単純な一行野郎にまで短くできます:

        % perl -lne '} print $.; {'    file

        % perl -lne 'END { print $. }' file

しかしこれらはやや非効率です。 これがあなたにとって十分に速くないなら、単にデータの塊を読み込んで改行の 数を数えるかもしれません:

        my $lines = 0;
        open my($fh), '<:raw', $filename or die "Can't open $filename: $!";
        while( sysread $fh, $buffer, 4096 ) {
                $lines += ( $buffer =~ tr/\n// );
                }
        close FILE;

しかし、これは行の末尾が改行でない場合は動作しません。 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 FILE;

シェルに出るのを気にしないなら、wc コマンドが、プロセス間の オーバーヘッドがあっても普通は最速です。 しかし、汚染されていないファイル名を使うように気をつけてください:

        #!perl -T
        
        $ENV{PATH} = undef;
        
        my $lines;
        if( $filename =~ /^([0-9a-z_.]+)\z/ ) {
                $lines = `/usr/bin/wc -l $1`
                chomp $lines;
                }

ファイルの最後の N 行を削除するには?

(brian d foy によって寄贈されました)

概念的に最も簡単な解決法は、ファイルの行数を数えて、最初から行数分 (マイナス最後の N) だけ新しいファイルに出力します。

ほとんどの場合、実際の質問は、ファイルに対して 1 パスで最後の N 行を 削除する方法、または多くのコピーなしに行う方法です。 簡単な概念は、ファイルに何百万行もある場合は現実性がありません。

一つの裏技は 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 );

File::ReadBackwards モジュールは、入力レコードセパレータを 正規表現で設定できるという利点もあります。

行に対して tie された配列を使ってアクセスできる Tie::File モジュールも 使えます。 最後の添え字を設定して splice を使うなど、、ファイルを修正するために 通常の配列操作が使えます。

プログラム内から Perl の -i オプションを使うには?

-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

このブロックはカレントディレクトリの全ての the .c ファイルを修正し、 各ファイルの元データのバックアップを新しい .c.orig ファイルに残します。

ファイルをコピーするには?

(brian d foy によって寄贈されました)

File::Copy モジュールをつかいましょう。 これは Perl に同梱されていて、ファイルシステム間で真のコピーが行われ、 移植性の面での細工がされています。

        use File::Copy;

        copy( $original, $new_copy ) or die "Copy failed: $!";

File::Copy が使えない場合、作業を自分自身でする必要があります: 元ファイルを開き、宛て先ファイルを開き、元ファイルから読んだものを宛て先 ファイルに書き込みます。 権限、所有者、グループも新しいファイルにコピーすることを忘れないように しなければなりません。

一時ファイルの名前を作り出すには?

もしファイル名を知る必要がないなら、open() でファイル名の部分に undef を指定します。 Perl 5.8 以降では、open() 関数は無名一時ファイルを作成します。

        open my $tmp, '+>', undef or die $!;

さもなければ、File::Temp モジュールが使えます。

        use File::Temp qw/ tempfile tempdir /;

        $dir = tempdir( CLEANUP => 1 );
        ($fh, $filename) = tempfile( DIR => $dir );

        # or if you don't need to know the filename

        $fh = tempfile( DIR => $dir );

File::Temp は Perl 5.6.1 から標準モジュールです。 十分にモダンな Perl がインストールされていないなら、 IO::File モジュールにあるクラスメソッド new_tmpfile を使って 読み書きのためにオープンされたファイルハンドルを取得します。 ファイルがどんな名前なのかを知る必要がない場合はこれを使ってください。

        use IO::File;
        $fh = IO::File->new_tmpfile()
        or die "Unable to make new temporary file: $!";

一時ファイルの作成を手作業で行いたいのなら、 プロセスIDやその時点での時刻(あるいはこれら両方)を使用してください。 一つのプロセスで複数の一時ファイルを使用するのであれば、 カウンターを使用しましょう:

        BEGIN {
        use Fcntl;
        my $temp_dir = -d '/tmp' ? '/tmp' : $ENV{TMPDIR} || $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;
                        # 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 ();
                        }
                }

        }

固定長レコードのファイルを操作するには?

最も効率的なやり方はpack()unpack() を使うものです。 これは文字列が大量にあるときにはsubstr() を 使うよりも高速です。 速度低下もほとんどありません。

以下に挙げたのは幾つかの固定フォーマットをした入力行に対して 分解を行ったり書き戻しをするコード片の例で、出力は 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";
        }

それぞれの行のフィールドを簡単に扱うためにハッシュスライスを使いました。 キーを配列に保管するということは、これらをグループやループで簡単に扱えると いうことを意味します。 これはまた、グローバル変数とシンボリックリファレンスの使用でプログラムが 汚染されることを防ぎます。

ファイルハンドルをサブルーチンに局所化するには? サブルーチンにファイルハンドルを渡すには? ファイルハンドルの配列を作るには?

perl5.6 から、open() にファイルハンドルやディレクトリハンドルとして未定義の スカラ変数を渡すと、これをリファレンスとして自動有効化します。 その後これらのリファレンスはその他のスカラと同じように扱え、名前付き ハンドルを指定する場所で使えます。

        open my    $fh, $file_name;

        open local $fh, $file_name;

        print $fh "Hello World!\n";

        process_file( $fh );

お好みなら、これらのファイルハンドルを配列やハッシュに保管することもできます。 これらを直接アクセスすると、これらは単なるスカラではないので、 ファイルハンドルを中かっこで囲むことで print に少し助けを与える必要が あります。 Perl はファイルハンドルリファレンスが単純なスカラの場合にのみファイル ハンドルリファレンスと認識します。

        my @fhs = ( $fh1, $fh2, $fh3 );

        for( $i = 0; $i <= $#fhs; $i++ ) {
                print {$fhs[$i]} "just another Perl answer, \n";
                }

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!" }

大量の無名ハンドルを作りたいのであれば、 Symbol, FileHandle, IO::Handle といったモジュールを参照してください。

ファイルハンドルを間接的に扱うには?

間接ファイルハンドルは、あるファイルハンドルを期待している場所に置かれた シンボル以外のなにかを使っています。 以下に間接ファイルハンドルの例を挙げます:

        $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

あるいは、FileHandleモジュールやIOモジュールのnewメソッドを使い、 それをスカラー変数に格納してからそれを普通のファイルハンドルと 同じように扱います。

        use IO::Handle;                     # 5.004 or higher
        $fh = IO::Handle->new();

上記のように作成して、後は普通のファイルハンドルと同じように使います。 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);

上の例では、ファイルハンドルを使う前にそれをスカラー変数に代入していました。 これは式でもなく、ハッシュや配列の添え字でもなく単純スカラ変数だけが printprintf、ダイヤモンド演算子と一緒に使えるからです。 単純スカラ変数以外のものをファイルハンドルとして使うのは不正であり、 コンパイル時にエラーとなります:

        @fd = (*STDIN, *STDOUT, *STDERR);
        print $fd[1] "Type it: ";                           # WRONG
        $got = <$fd[0]>                                     # WRONG
        print $fd[2] "What was that: $got";                 # WRONG

printprintfの場合には、ブロックを使ってその中にファイルハンドルを 含む式を置くことによって対処することができます:

        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";

このアプローチはprintprintfをオブジェクトメソッドの呼び出しのように 扱うものですが、これはダイヤモンド演算子には使えません。 なぜなら、ダイヤモンド演算子はカンマなしの引数を取る関数ではなくて本当の 演算子だからです。 さて、上記の例のように型グロブをあなたの作った構造に格納したとしましょう。 readlineという名前の組み込み関数を使って<> が行うように レコードを読み込むことができます。 @fdを例にあったように初期化してやることでうまく動作します。 しかし、readline()は型グロブを要求するからです。 これはオブジェクトや文字列では動作しません。 このことはバグとも言えるもので現時点では修正されていません。

        $got = readline($fd[0]);

間接ファイルハンドルの妙な点はそれが文字列であるか、型グロブであるか、 オブジェクトであるか、はたまた別の何者であるかには関係しないということに 注意してください。 これは基本的な演算子の構文なのです。 オブジェクトをいじくりまわして遊ぶことはここでは何の助けにもなりません。

これを行うための組み込みの方法はありません。 しかしperlformにはこれを可能にするための、大胆不敵なハッカー向けの いくつかのテクニックがあります。

文字列に対して write() するには?

"Accessing Formatting Internals" in perlformswrite() 関数を参照してください。

ファイルハンドルを文字列としてオープンするには?

(Peter J. Holzer, hjp-usenet2@hjp.at によって寄贈されました)

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"

Perl のより古いバージョンでは、IO::String モジュールが似たような機能を 提供します。

出力する数字にカンマを付加するには?

(brian d foy と Benjamin Goldberg によって寄贈されました)

数値を区切るために Number::Format が使えます。 これは代わりにピリオド(または実際のところは使いたいもの何でも)を 挿入したい人のために、ロケール情報を扱います。

このサブルーチンは数値にカンマを追加します:

        sub commify {
                local $_  = shift;
                1 while s/^([-+]?\d+)(\d{3})/$1,$2/;
                return $_;
                }

Benjamin Goldberg による正規表現は数値にカンマを追加します:

        s/(^[-+]?\d+?(?=(?>(?:\d{3})+)(?!\d))|\G\d{3}(?=\d))/$1,/g;

コメント付きで見ればより簡単になります:

        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;

ファイル名の中にあるチルダ (~) を変換するには?

perlfuncで説明されている <> (glob()) を使います。 5.6 より古いバージョンの Perl では、チルダを展開するシェルが既に インストールされていることを要求します。 最近のバージョンの Perl は内部にこの機能を持っています。 Glob::KGlob モジュール(CPANで入手可能)はより移植性のある glob 機能を提供します。

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;

ファイルを読み書きモードでオープンしたときに内容をクリアしてしまうのはなぜ?

ファイルを切り詰めて、その後で読み書きアクセスを提供するようなものを 使おうとしたからです。

        open(FH, "+> /path/name");              # WRONG (almost always)

おっと。 ファイルがなかったときに失敗するような以下のやり方を使うべきでしょう。

        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 $!;

ファイルを更新のためにオープンするが、ファイルが事前に存在してはいけない場合:

        sysopen(FH, $path, O_RDWR|O_EXCL|O_CREAT)           || die $!;
        sysopen(FH, $path, O_RDWR|O_EXCL|O_CREAT, 0666)     || die $!;

ブロッキングなしでフィルをオープンし、必要があれば作成するには:

        sysopen(FH, "/foo/somefile", O_WRONLY|O_NDELAY|O_CREAT)
            or die "can't open /foo/somefile: $!":

ファイルの作成や削除はNFS越しの場合にはアトミックな操作ではないことに 注意してください。 つまり、二つのプロセスが同じファイルの作成や削除に成功するかもしれないのです! したがってO_EXCLはあなたが期待しているようには排他的ではないのです。

もしあれば 新しい perlopentut (5.6 対応) も参照して下さい。

なぜ <*> を使ったときに "Argument list too long" (引数リストが長すぎる)となることがあるの?

<*> 演算子はグロブ操作(globbing operation)を行います。 v5.6.0 以前の Perl では、glob() はcsh(1)を起動して、 実際のグロブを行います。 しかし、cshは127を越えるアイテムを扱うことができないので、 そういったエラーメッセージ Argument list too longとなるのです。 cshをcshとしてインストールしている人はこの問題に直面することはないでしょうが、 ユーザーがびっくりすることがあるかもしれません。

これに対処するには、Perl v5.6.0 以降にアップグレードするか グロブをreaddir()とパターンを使って 自分自身で実行するか、あるいはGlob::KGlobのようなモジュールを使って、 シェルによるグロブを行わないようにします。

glob()に leak/bug はあるの?

(brian d foy によって寄贈されました)

Perl 5.6.0 から、glob 外部リソースに頼ることなく内部で実装されています。 当然として、最近の perl では glob ではメモリの問題はありません。

名前の先頭に">"があったり末尾に空白があるようなファイルをオープンするには?

(Brian McCauley によって寄贈されました)

Perl の open() 関数の特殊な 2 引数形式はファイル名の末尾の空白を無視し、 先頭の文字 (あるいは末尾の "|") からモードを推論します。 古いバージョンの Perl ではこれが open() の唯一の形式なので、古いコードや 本には普及しています。

2 引数形式を使う特有の理由がない限り、ファイル名のどの文字も特別なものとして 扱わない 3 引数形式の open() を使うべきです。

        open FILE, "<", "  file  ";  # filename is "   file   "
        open FILE, ">", ">file";     # filename is ">file"

信頼性のあるファイルのリネームをするには?

あなたの使っているオペレーティングシステムが、適切な mv(1) あるいは それと機能的に等価なプログラムをサポートしているのなら以下のような やり方が使えるでしょう:

        rename($old, $new) or system("mv", $old, $new);

File::Copy モジュールを使うのが、より移植性があるかもしれません。 新しい名前で新しいファイルにコピーして(ここで戻り値をチェック)、 古いものを削除するだけです。 ただし、これはパーミッション、タイムスタンプ、inode情報といった メタ情報が保存されないので、rename() と同じにはなりません。

ファイルをロックするには?

Perlに組み込みの flock関数(詳しくはperlfuncを参照)は、flock(2)が あればそれを、なければfcntl(2)を呼び出します(5.004以降の場合)。 そして、これら二つのシステムコールのいずれもない場合にはlockf(3)を 呼び出します。 一部のシステムでは、ネイティブなロッキングとは異なった使い方を するかもしれません。 以下に、Perlのflock()に関する罠(gotchas)を挙げておきます:

  1. 三つのシステムコールがどれもなければ(もしくは等価なものがなければ)、 致命的エラーを生成します。

  2. lockf(3)は共有ロックをサポートしません。そして、書き込み用に(もしくは 追加モードか読み書きモード)オープンされているファイルハンドルを要求します。

  3. flock()の一部のバージョンはネットワーク越し(NFSファイルシステムなど)に ファイルをロックすることはできません。 このため、Perlをビルドするときにfcntl(2)を使うように強制する必要が あるかもしれません。しかしこれでも最善かどうかは疑わしいです。 perlfuncのflock()のエントリと、そのためのPerlをビルドする 情報のある、ソース配布中にあるINSTALLというファイルを参照してください。

    二つの潜在的に明らかでないけれども、伝統的な flock の手法があります。 一つはロックが与えられるまで無限に待ち、そしてそのロックは めったに忠告されません。このような自由度の高いロックはより柔軟ですが、 得るものはより少ないです。 つまり、 flock() でロックされたファイルは flock() を使っていない プログラムによって修正される可能性があるからです。 赤信号で止まる車同士ならうまくいきますが、片方が信号を守らないなら うまくいかないということです。 詳細については perlport man ページ、使っているバージョン独自のドキュメント、 システム独自のローカルな man ページを参照してください。 移植性のあるプログラムのためには、伝統的な振る舞いを仮定するのが最善です。 (そうしないなら、あなたは自分のシステムの癖(「仕様」とも呼ばれます)に 合わせて書くことをいつでも気にしないようにするべきです。 あなたの仕事をやり遂げる時に、盲目的に移植性を考慮するべきではありません。)

    ファイルのロッキングに関する詳細は<perlopentut/"FileLocking">を 参照してください(5.6 対応)。

なぜ単に open(FH, ">file.lock")とできないの?

以下のようなことを してはいけません:

        sleep(3) while -e "file.lock";  # PLEASE DO NOT USE
        open(LCK, "> file.lock");               # THIS BROKEN CODE

これは古典的な競合状態です: あなたはここで、 一つのステップでやらなければならないことを二つのステップでやっています。 つまりこれが、コンピューターのハードウェアがアトミックな test-and-set の命令を備えている理由です。 理論的には、これを動作するにさせるには:

        sysopen(FH, "file.lock", O_WRONLY|O_EXCL|O_CREAT)
                or die "can't open  file.lock: $!";

残念なことに、NFSを通じた場合にはファイルの作成(と削除)は アトミックではありません。 このため、この例はネットワークを通した場合にはうまく動きません (少なくとも失敗する可能性があります)。 link()を含め、様々なやり方が既に提案されていますが、これらは busy-waitを 伴うものでありあまり望ましいものではありません。

まだロックができません。ただ単にファイルにある数値をインクリメントしたいだけなんだけど。どうすればいいの?

これまでただの一人もあなたに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 ページヒットカウンターです:

        $hits = int( (time() - 850_000_000) / rand(1_000) );

カウントがあなたの友達を感心させないのなら、このコードもそうでしょうね :-)

ファイルの末尾にちょっとしたテキストを追加したいだけなんです。それでもロックが必要なの?

もし flock() が正しく実装されているシステムを使っていて、 "perldoc -f flock" にある追加コードの例を使っているなら、たとえ使っている OS が追加モードを正しく実装していない(もしそのようなシステムがあれば) 場合でも、全てうまくいきます。 従って、実行する OS を flock() が実装されているものに制限してもよいなら (これは実際のところそれほど大きな制限ではありません)、そうするべきです。

正しく追加を実装している(つまり Win32 ではない)システムを 利用しているとわかっている場合のみ、以前の答えのコードから seek() を 省略できます。

追加モードを正しく実装している OS とファイルシステム(例えば最近の Unix の ローカルファイルシステム)でのみ実行されることがわかっていて、 ファイルをブロックバッファモードのままに維持していて、 手動でのバッファのフラッシュの間にバッファがいっぱいになるほどの 書き込みを行わない場合は、それぞれのバッファは他からの出力が混ざることなく、 ファイルの末尾にひとつの塊で書き込まれることがほとんど保証されます write(2) システムコールの単なるラッパである syswrite() を使うことも できます。

未だに、理論的にはシステムレベルの write() 操作が終了前にシグナルが割り込む 可能性が少しあります。 STDIO の実装によっては、バッファが空からスタートしても、複数のシステム レベルの write() を呼び出すものがある可能性があります。 この確率を 0 にしているシステムもあるかもしれませんし、 システムの STDIO ではなく :perlio を使うときには関係ありません。

バイナリファイルをランダムに更新するには?

単にバイナリにパッチをあてたいというのなら、多くの場合は以下のように 単純にできます:

        perl -i -pe 's{window manager}{window mangler}g' /usr/bin/emacs

もし固定サイズのレコードを持っているのなら、以下のようにして行うことも できます:

        $RECSIZE = 220; # size of record, in bytes
        $recno   = 37;  # which record to update
        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に説明がある -A, -M, -C といったファイルテスト演算子を使います。 これらはファイルの年齢(あなたのプログラムの実行開始に対するもの)を 浮動小数点数で表現された日数で取得します。 プラットフォームによってはこれら全ては利用できないものもあります。 紀元からの経過秒数による“生の”時間を得るためには、 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以降の標準配布キットに含まれています)。

        # error checking left as an exercise for reader.
        use File::stat;
        use Time::localtime;
        $date_string = ctime(stat($file)->mtime);
        print "file $file updated at $date_string\n";

POSIX::strftime()アプローチは理論的にはロカール非依存で、 使う価値があります。詳しくはperllocaleを参照してください。

perl でファイルのタイムスタンプを設定するには?

"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;

例によって、エラーチェックは読者の練習として残してあります。

utime の perldoc には、既にある ファイルに対する touch(1) と同じ 効果のある例があります。

予測されたレベルの制度でファイルの時刻を保存する能力が制限された ファイルシステムもあります。 例えば、FAT と HPFS は 2 秒よりも細かい精度でファイルの作成時刻を 設定できません。 これはファイルシステムの制限であり、utime() での問題でありません。

複数のファイルを一度に表示するには?

一つのファイルハンドルを複数の出力ファイルハンドルに接続するには、 IO::Tee や Tie::FileHandle::Multiplex のモジュールが使えます。

これをする必要があるのが一回だけなら、以下のようにしてできます:

        for $fh (FH1, FH2, FH3) { print $fh "whatever\n" }

ファイル全体を一度に読みこむには?

これを 1 ステップで行うためには、File::Slurp モジュールが使えます。

        use File::Slurp;

        $all_of_it = read_file($filename); # entire file in scalar
        @all_lines = read_file($filename); # one line per element

Perl においてファイルの全ての行を処理するための慣例的な手法は 1 行ずつ読みこむことです:

        open (INPUT, $file)     || die "can't open $file: $!";
        while (<INPUT>) {
                chomp;
                # do something with $_
                }
        close(INPUT)            || die "can't close $file: $!";

これはファイル全体を行の配列として読みこんでから1行ずつ処理するという (ほとんど常に、でないのなら)しばしば誤った手法よりも 遥かに効率的です。 それでもファイル全体を読み込みたいなら以下のようにします:

        @lines = <INPUT>;

本当に全てを一度に読み込む必要があるのかを良く考えるべきです。 これは単にスケールの問題ではありません。 標準の Tie::File モジュールか DB_File モジュールの $DB_RECNO バインディングの方がより好ましいと考えるかもしれません。 これは配列とファイルを結び付けるので、配列にアクセスすると、実際には ファイルの対応する行にアクセスすることになります。

ファイルハンドルの内容全体を一つのスカラ変数に読み込むことができます。

        {
        local(*INPUT, $/);
        open (INPUT, $file)     || die "can't open $file: $!";
        $var = <INPUT>;
        }

これは一時的にレコードセパレータを未定義にし、ブロックを出るときに 自動的にファイルをクローズします。ファイルが既にオープンしているなら、 単に以下のようにします:

        $var = do { local $/; <INPUT> };

通常のファイルの場合は read 関数も使えます。

        read( INPUT, $var, -s INPUT );

3 番目の引数は INPUT ファイルハンドルのデータのバイトサイズをテストし、 そのバイト数だけバッファ $var に読み込みます。

ファイルをパラグラフ毎に読み込むには?

$/という変数を使います(詳しくはperlvarを参照してください)。 たとえば ("abc\n\n\n\ndef"で三つではなく二つのパラグラフを受 け取る、つまり空のパラグラフを除去するには""を設定します。空 のパラグラフを受け付けるには"\n\n"を設定します。

空行はブランクを持っていてはいけないということに注意しましょう。 このため、"fred\n \nstuff\n\n" は一つのパラグラフですが "fred\n\nstuff\n\n"は二つのパラグラフです。

ファイルから 1 文字だけ読み出すには? キーボードからは?

ほとんどのファイルハンドルに対しては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;

あるファイルハンドルが読み込み待ちの文字を待っているかどうかを知るには?

あなたがすべき第一のことは、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 にある関数として定義されます。

        require '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

あるいはエディターの王様を使って小さな C プログラムを書きます:

        % cat > fionread.c
        #include <sys/ioctl.h>
        main() {
            printf("%#08x\n", FIONREAD);
        }
        ^D
        % cc -o fionread fionread.c
        % ./fionread
        0x4004667f

その後でその値をハードコードしてやって、あなたの仕事を引き継ぐ人のための 練習として移植をさぼります。

        $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 はストリームに接続されたファイルハンドルを要求します。 これはソケット、パイプ、あるいは tty デバイスではうまく動作しますが、 ファイルに対してはうまく行きません

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)) {
            # search for some stuff and put it into files
          }
          # sleep for a while
          seek(GWFILE, $curpos, 0);  # seek to where we had been
        }

これでもうまく行かなければ、IO::Handleclearerr メソッドを 検討してください; これはハンドルのエラーと end-of-file 状態をリセットします。

CPANには File::Tail モジュールがあります。

Perl でファイルハンドルの dup() をするには?

"open" in perlfunc を見れば、それを行うための open() の呼び出し方が 何通りもあることに気がつくでしょう。例を挙げます:

        open(LOG, ">>/foo/logfile");
        open(STDERR, ">&LOG");

あるいは、リテラルの数値記述子を使います:

   $fd = $ENV{MHCONTEXTFD};
   open(MHCONTEXT, "<&=$fd");   # like fdopen(3S)

"<&STDIN" はコピーを作成し、"<&=STDIN" がエイリアスを作成するということに 注意してください。 これはつまり、あなたがエイリアスが作成されたファイルハンドルを クローズすると、エイリアスはすべてアクセスできなくなります。 これはコピーの場合にはそうはなりません。

エラーチェックはいつものように読者の練習のために残してあります。

数値によるファイル記述子をクローズするには?

もし、何らかの理由で、ファイルハンドルではなくファイル記述子を持っている場合 (おそらく POSIX::open を使ったのでしょう)、POSIX モジュールの close() 関数が使えます:

        use POSIX ();

        POSIX::close( $fd );

Perl の close() 関数は、先の例にあった MHCNOTEXT のように数値 記述子を使って dup したものでさえも含めて、 Perl 自身がオープンしたものに対して使うことができますから、 その必要はほとんどないはずです。 しかし本当にそうする必要があるのなら、以下のようにできるでしょう:

        require 'sys/syscall.ph';
        $rc = syscall(&SYS_close, $fd + 0);  # must force numeric
        die "can't sysclose $fd: $!" unless $rc == -1;

あるいは、単に open() の fdopen(3S) 機能を使います:

        {
        open my( $fh ), "<&=$fd" or die "Cannot reopen fd=$fd: $!";
        close $fh;
        }

なぜ 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" なんて名前のファイルはできませんよね。

文字列を括るのにシングルクォートを使うか、もしくは(こちらが好ましい) スラッシュを使ってください。 全てのDOSおよびWindowsは、MS-DOS 2.0以降、パス中にある/\を 同じに扱いますから、あなたはPerlを壊すことなく使えます。 もしくは POSIXシェル、ANSI CとC++、awk、tcl、Java、Python を考慮してください。 POSIXパスはより移植性に富んでいます。

なぜ glob("*.*") で全てのファイルを得られないの?

非 UNIX システムに対する移植であっても、Perl の glob 関数は UNIX の標準的な グロブの振る舞いに従うからです。 全ての(隠し属性でない)ファイルを得るには glob("*")とする必要があります。 これは glob() の移植性を高めます。 あなたの使っている Perl が独自のグロブ関数をサポートしているかもしれません。 詳しくはドキュメントを参照してください。

なぜ Perl は読みとり専用ファイルを削除してしまうの? なぜ-i clobberはファイルをプロテクトするの? これは Perl のバグじゃないの?

http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz の "Far More Than You Ever Wanted To Know" にあるfile-dir-perms の 記事が教育的、かつ懇切丁寧にこの問題を説明しています。

簡単なまとめ: あなたの使っているファイルシステムがどのように動作しているのかを 考えてください。 ファイルに対するパーミッションはそのファイルにあるデータに何が できるかということを表しています。 ディレクトリに対するパーミッションは、そのディレクトリにあるファイルリストに 対して何ができるのかということを表しています。 ファイルを削除したとき、そのファイルに対する名前がディレクトリから 取り除かれます(したがってこの操作はファイルに対するパーミッションでは なく、ディレクトリに対するパーミッションに依存しているのです)。 ファイルに対して書き込みを行おうとすると、ファイルに対するパーミッションが それができるかどうかを決定します。

あるファイルからランダムに行を選択するには?

ファイルをデータベースに読み込ませたり、ファイルの行数のインデックスを 予め作っておく以外では、これをするためには 2 種類の方法があります:

以下に示すのはらくだ本にあったリザーバサンプリングアルゴリズムです:

        srand;
        rand($.) < 1 && ($line = $_) while <>;

これは、ファイル全体を読み込んで処理するやり方に比べて使用する空間の 大きさにおいて明らかなアドバンテージがあります。 この手法の証明は Donald E. Knuth の The Art of Computer Programming, Volume 2, Section 3.4.2 に あります。

このアルゴリズムの関数を提供する File::Random モジュールが使えます:

        use File::Random qw/random_line/;
        my $line = random_line($filename);

もう一つの方法は Tie::File モジュールを使うことで、これはファイル全体を 配列として扱います。 単にランダムに配列にアクセスしてください。

行の配列を出力したときになぜ余計なスペースがつくの?

(brian d foy によって寄贈されました)

もし配列を表示したときに配列の要素の間に空白が付くなら、 おそらく配列をダブルクォートで展開しています:

        my @animals = qw(camel llama alpaca vicuna);
        print "animals are: @animals\n";

これは print ではなくダブルクォートが行っています。 ダブルクォートコンテキストで配列を展開するときはいつでも、 Perl は要素を空白(正確にはデフォルトが空白の $")を使って 結合します:

        animals are: camel llama alpaca vicuna

これは、配列を変数展開なしに表示するのとは異なります:

        my @animals = qw(camel llama alpaca vicuna);
        print "animals are: ", @animals, "\n";

今度は要素の間には空白はありません; @animals の要素は単に print へのリストの一部となっているからです:

        animals are: camelllamaalpacavicuna

@array の要素のそれぞれが改行で終わっているときにこのことに 気付いたかも知れません。 行毎に 1 要素を表示することを想定したけれども、最初の以外の行が インデントされることに気付きます:

        this is a line
         this is another line
         this is the third line

追加の空白は配列の展開による物です。配列の要素の間に何も表示したくない 場合、ダブルクォートの中で配列を使わないでください。 使わなくても表示できます:

        print @lines;

ディレクトリツリーを辿るには?

(brian d foy によって寄贈されました)

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 ...
                }

CPAN からダウンロードできる File::Find::Closures は、 File::Find と共に使える、たくさんの「すぐに使える」サブルーチンを 提供します。

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 );

CPAN からダウンロードできる File::Find::Rule モジュールは、 同じようなインターフェースを持ちますが、ファイルを辿ることもします:

        use File::Find::Rule;

        my @files = File::Find::Rule->file()
                                                         ->name( '*.pm' )
                                                         ->in( @INC );

ディレクトリツリーを削除するには?

(brian d foy によって寄贈されました)

もし、ディレクトリが空なら、Perl 組み込みの rmdir が使えます。 もしディレクトリが空(ファイルもサブディレクトリもない)ではないなら、 自力で空にする(多くの作業が必要です)か、助けてくれるモジュールを 使う必要があります。

Perl と共に配布されている File::Path モジュールには、大変な作業の全てを 代わりに処理してくれる remove_tree があります:

        use File::Path qw(remove_tree);

        remove_tree( @directories );

File::Path モジュールには、より古い rmtree サブルーチンへの レガシーなインターフェースもあります。

ディレクトリ全体をコピーするには?

(Shlomi Fish によって寄贈されました)

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.