ルモーリン
ホーム更新サービス雑談ランドナーコースガイド鉄ゲタ自転車Linuxリンク連絡先

山括弧に配列を置くとグロブする

きっかけ

このツイート/ブログを見て。

山括弧の仕様

おおよそこんな感じ。
以下、サイトから引用。
  1. スカラーコンテキストで山括弧の中のファイルハンドルを評価すると、 そのファイルから、次の行を読み込むことになります (改行があればそれも含まれます)。
  2. 山括弧の中の文字列がファイルハンドルでもファイルハンドル名、型グロブ、 型グロブリファレンスのいずれかが入った単純スカラ変数でもなければ、 グロブを行なうファイル名のパターンと解釈され、コンテキストによって ファイル名のリストか、そのリストの次のファイル名が返されます。
  3. 1 段階だけダブルクォート展開が行なわれます
  4. 引数を空白で分割して、それぞれを分割された パターンとして扱います
  5. 空でない中かっこが glob で使われている唯一の ワイルドカード文字列の場合、ファイル名とはマッチングせず、 可能性のある文字列が返されます。
1、2は知っていたけれど3~5は知らなかったなあ、きちんとドキュメントにあたって良かった。

グロブはファイル名マッチングだけじゃない

Linuxのシェルで試すと一目瞭然です。 この記事を書くまで知らなかったです。
echo abc_{def,ghi}_jkl
 ↓
abc_def_jkl abc_ghi_jkl

山括弧に配列を指定した場合

まとめるとスペース区切りでワイルドカードを含まない文字列が返ってきます。 可能性として偶然ファイル名マッチングに一致したファイルがあれば、そのファイル名も返ってきます。
  1. <@soc> ←最初
  2. <"@soc"> ←ダブルクォート展開
  3. <'<!DOCTYPE html> <html lang="ja"> … </body> </html>'> ←配列の要素をスペース区切りで並べる
  4. '<!DOCTYPE', 'html>', '<html', 'lang="ja">', …, '</body>', '</html>' ←スペースで分割
  5. '<!DOCTYPE' ←呼ばれる度に1個ずつ
  6. 中カッコがある→組み合わせを展開
  7. ワイルドカードがある→ファイル名マッチングで見つかったファイル名を1個ずつ
  8. ワイルドカードがない→その文字列

コード

うちのデスクトップ環境(Windows)にcurlがなく、LWP::UserAgentで代用しました。
#!/usr/bin/env perl -w

use utf8;
use strict;
use warnings;

use Data::Dumper;
use Encode::Argv;
use Encode::Locale;
use LWP::UserAgent;

use open IO => ":utf8";
binmode STDIN, ":encoding(console_in)";
binmode STDOUT, ":encoding(console_out)";

$| = 1;

my $url = $ARGV[0] // "https://www.lemorin.jp";
print "URL:'$url'\n";

# curlコマンドの代わり
my $ua = LWP::UserAgent->new;
my $res = $ua->get($url);
$res->is_success or die $res->status_line;
my $buf = $res->decoded_content;

my @soc = split /[\n\r]/, $buf;
print "入力の要素数:@{[scalar @soc]}個\n";

my @reproduction = reproduction(@soc);
print "再現の要素数:@{[scalar @reproduction]}個\n";

my @substitute = substitute(@soc);
print "代用の要素数:@{[scalar @substitute]}個\n";

print "比較\n";
if (@reproduction == @substitute) {
	for (my $i = 0; $i < @reproduction; $i++) {
		print "${i}個目:'$reproduction[$i]' ne '$substitute[$i]'\n" if $reproduction[$i] ne $substitute[$i];
	}
} else {
	print "要素数不一致\n";
}

print "グロブの展開を確認\n";

trial_glob(split //, "あいうえお");

trial_glob("abc{def,ghi}jkl");

trial_glob("abc def{ghi,jkl}mno pqr");

trial_glob("abc def{ghi,jkl}mno pqr*.txt");

trial_glob("abc", "def{ghi,jkl}mno", "pqr");

print "終了\n";

exit;

sub reproduction {
	my @soc = map {Encode::encode locale => $_} @_;

	my @result;
	while ($buf = <@soc>) {
		push @result, Encode::decode locale => $buf;
	}

	return @result;
}

sub substitute {
	my @soc = map {Encode::encode locale => $_} @_;

	my @result;
	while ($buf = glob "@soc") {
		push @result, Encode::decode locale => $buf;
	}

	return @result;
}

sub trial_glob {
	my @a = @_;

	print "―――glob前―――\n";
	for (@a) {
		print "$_\n";
		$_ = Encode::encode locale => $_;
	}

	my @b = <@a>;

	print "―――結果―――\n";
	for (@b) {
		my $c = Encode::decode locale => $_;
		print "$c\n";
	}
}

実行結果

例によってWindowsとLinuxで同じ事を確認…ん、Linuxでは再現/代用の要素数が221個になりました(Windowsより1個多い)。 グロブの動作がOSに影響されているかもしれません。
URL:'https://www.lemorin.jp'
入力の要素数:76個
再現の要素数:220個
代用の要素数:220個
比較
グロブの展開を確認
―――glob前―――
あ
い
う
え
お
―――結果―――
あ
い
う
え
お
―――glob前―――
abc{def,ghi}jkl
―――結果―――
abcdefjkl
abcghijkl
―――glob前―――
abc def{ghi,jkl}mno pqr
―――結果―――
abc
defghimno
defjklmno
pqr
―――glob前―――
abc def{ghi,jkl}mno pqr*.txt
―――結果―――
abc
defghimno
defjklmno
―――glob前―――
abc
def{ghi,jkl}mno
pqr
―――結果―――
abc
defghimno
defjklmno
pqr
終了