2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

perlの-iオプションを<>ループ外から使うときの挙動

2
Posted at

概要

結論から言えば、-iオプションは<>ループ外から使おうとしても狙い通りの挙動にならない。よってワンライナーでは原則として-n-pとともに使うべきである。

例えば、以下は期待した挙動にならない。(perl 5.18.2で確認。以下同様)

## 3行目を削除して出力したい
$ perl -i.orig -e '@f = <>; $f[2] = ""; print @f' sample.txt 

この場合、出力結果は標準出力に出てしまい、sample.txtは空ファイルになる。

以下も同様である。

## 各行に"> "という接頭辞をつけたい
$ perl -i.orig -e 'print "> $_" for <>' sample.txt 

なぜそうなるのか?

perlrun, perlvarによれば、グローバル変数$ARGVには現在<>で読み込んでいるファイル名が、グローバルファイルハンドルARGVOUTには-iオプションの効果による出力先ファイルハンドルがセットされるらしい。

そこで、以下のスクリプトを用意する。

sub p {
    my ($dev, $ino) = stat(*ARGVOUT);
    print STDERR "ARGV: $ARGV, ARGVOUT: $ino, ^I: $^I\n";
}

print STDERR "-- before loop\n";
p();
print STDERR "-- enter loop\n";
while(<>) { p() }
print STDERR "-- after loop\n";
p();

で、実行。

$ perl -i.orig process.pl sample.txt sample2.txt 
-- before loop
ARGV: , ARGVOUT: , ^I: .orig
-- enter loop
ARGV: sample.txt, ARGVOUT: 2759153, ^I: .orig
ARGV: sample.txt, ARGVOUT: 2759153, ^I: .orig
ARGV: sample.txt, ARGVOUT: 2759153, ^I: .orig
ARGV: sample2.txt, ARGVOUT: 2759159, ^I: .orig
ARGV: sample2.txt, ARGVOUT: 2759159, ^I: .orig
ARGV: sample2.txt, ARGVOUT: 2759159, ^I: .orig
-- after loop
ARGV: sample2.txt, ARGVOUT: , ^I: .orig

上の結果から、以下の事実が読み取れる。

  • ARGVOUT<>の読み込み中にのみセットされており、<>ループの前と後では存在しない。この時、printは通常通りSTDOUTに出る。
  • <>の読み込み中、ARGVOUT$ARGVの値に応じて適切に変化する。これにより、複数ファイルを一度に処理できる。

先の例では、

$ perl -i.orig -e 'print "> $_" for <>' sample.txt 

<>を限界まで読み込んでリストに保持してからループを回している。そのため、ループ中は既にARGVOUTは存在しない。

この場合、forwhileにすればうまく動く。

$ perl -i.orig -e 'print "> $_" while <>' sample.txt 
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?