PerlでUNCアクセス
2023/04/30
UNCパスの扱いが面倒
  • UNC...Universal Naming Convention...共有フォルダ上のファイル指定等(*1)に利用される
      \\サーバー名\パス\ファイル名
    表記のことです。

  • Perlスクリプトからバッククォート経由で dir /b 等によりUNCパス上のファイルリスト取得を試みると、「UNCパスはサポートされません」といったメッセージでエラー終了します。Windows 11以降は動きそうですが、Windows 10が多数派の状況では困ったことになります。

  • するともう少し安定した手段が欲しいわけですが、エクスプローラなら問題ないでしょう。つまりOLE経由でPerlからエクスプローラを...FileSystemObjectを使用します。

UNCパス上のファイルリスト取得
  • まずはUNCパス上のファイルリスト取得からコピーまで操作してみましょう。下記流れです。
    • [1]OLEでFileSystemObjectのオブジェクト取得
    • [2]指定UNCパスの存在を確認
    • [3]指定UNCパスのFolderオブジェクト取得
    • [4]FolderオブジェクトからFilesコレクション取得
    • [5]FilesコレクションからFileオブジェクト取得
    • [6]FileオブジェクトのNameプロパティ取得
    • [7]正規表現でファイル選択
    • [8]選択したファイルをUNCパスからカレントフォルダへコピー

  • コード例は下記となります(sample01.pl)。スクリプトの漢字コードはShift-JIS(cp932)です。
  • #!perl -w
    use Win32::OLE;
    use Encode;
    use strict;
    
    {
      # [1]OLEでFileSystemObjectのオブジェクト取得
        my $o_fso = Win32::OLE->new('Scripting.FileSystemObject');
        
        # 例として全角やスペースが混ざったUNCパスを指定
        # 先頭の\\はエスケープで\の1文字解釈となるため'\\'を先頭に追加
        my $p_unc = '\\'.'\\Server\MusicDb\domestic\サザンオールスターズ\KILLER STREET\disc1';
        
      # [2]指定UNCパスの存在を確認
        unless ($o_fso->FolderExists($p_unc)) { die "ERR: $p_unc は見つかりません。\n"; }
        
        print $o_fso->GetAbsolutePathName($p_unc), "\n";
      
      # [3]指定UNCパスのFolderオブジェクト取得
        my $o_folder = $o_fso->GetFolder($p_unc);
        
      # [4]FolderオブジェクトからFilesコレクション取得
        my $o_files_collection = $o_folder->{Files};
        
      # [5]FilesコレクションからFileオブジェクト取得
        print "\t#ext\t#file_name\n";
        foreach my $o_file (in $o_files_collection) { # コレクション処理がポイント
          # [6]FileオブジェクトのNameプロパティ取得
            my $file_name = $o_file->{Name}; # ファイル名
            my $ext_name  = $o_fso->GetExtensionName($file_name); # 拡張子
            print "\t", $ext_name, "\t", $file_name, "\n";
        }
      
      # [7]正規表現でファイル選択
        # Folderオブジェクトはワイルドカード対応無しのため正規表現で抜き出す
        # 正規表現使用時はutf8(Perl内部文字列)へ変換必要
        my $match_utf8 = decode('cp932', '.*魔法.*\.mp3$'); # 条件(正規表現)
        my $match_o_file;
        
        foreach my $o_file (in $o_files_collection) {
            my $file_name = $o_file->{Name};                   # ファイル名(cp932)
            my $file_name_utf8  = decode('cp932', $file_name); # ファイル名(utf8)
            if ($file_name_utf8 =~ /$match_utf8/i) {
                print "\tmatched\t", $file_name, "\n"; # 表示はcp932側
                $match_o_file = $o_file;               # 選択したFileオブジェクト確保
            }
        }
        
      # [8]選択したファイルをUNCパスからカレントフォルダへコピー
        print "\tcopying the file ...\n";
        $match_o_file->Copy('.\\', 1); # カレントフォルダの.\は、.\\にする必要あり
    }

  • ステップ[5]のコレクション要素取得でinメソッドを使うのがポイントです。これはWin32::OLEが持つメソッドです。下記のように書くとわかりやすいかもしれません。
    • foreach my $o_file (Win32::OLE::in($o_files_collection)) {
        ...
      }

  • またエクスプローラとの漢字データやり取りはcp932なので、スクリプトは基本cp932で記述し、必要な場合...正規表現を使用する場合にdecodeでUTF-8にするのが使いやすいと考えています。そのためスクリプトに use utf8; を入れていません。

  • 実行結果は下記となります。ファイルリスト表示後、'.*魔法.*\.mp3$'にマッチした「05_夢と魔法の国.mp3」がスクリプトのカレントフォルダに置かれます。尚11番目のファイルは文字化けではなく、本当にそのファイル名です。
  • C:>sample0.pl
    \\Server\MusicDb\domestic\サザンオールスターズ\KILLER STREET\disc1
            #ext    #file_name
            mp3     01_からっぽのブルース.mp3
            mp3     02_セイシェル〜海の聖者〜.mp3
            mp3     03_彩〜Aja〜.mp3
            mp3     04_JUMP.mp3
            mp3     05_夢と魔法の国.mp3
            mp3     06_神の島遥か国.mp3
            mp3     07_涙の海で抱かれたい〜SEA_OF_LOV.mp3
            mp3     08_山はありし日のまま.mp3
            mp3     09_ロックンロール・スーパーマン〜.mp3
            mp3     10_BOHBO_No.5.mp3
            mp3     11_殺しの接吻〜Kiss_Me_Good−Byeヤ.mp3
            mp3     12_LONELY_WOMAN.mp3
            mp3     13_キラーストリート.mp3
            mp3     14_夢に消えたジュリア.mp3
            mp3     15_限りなき永遠の愛.mp3
            matched 05_夢と魔法の国.mp3
            copying the file ...

クラスにした方が良さそうです
  • できることはわかりました。ただファイルリストの取得だけで毎回これだけコーディングするのは面倒です。ですがローカル/UNCともに適用できるので便利ではあります。

  • すると漢字コードの処理も含めて隠蔽するのが良さそうなので、クラスを作ろうと思います。

  • ある程度クラスができたら、またレポートします。
Notes
  • Windowsネットワーク上のリソースです。Sambaも含む。
Copyright(C) 2023 Altmo
本HPについて