Batchファイルでショートカット(.lnk)を解析する方法

備忘録

はじめに

バッチスクリプトで音声ファイルを一括処理していると、ショートカット(.lnk)ファイルもまとめて処理したいと感じる場面が出てくる。
たとえば、作業用フォルダに .mp3.ogg と並んで .lnk が混在しているケースだ。

しかし、バッチファイルは .lnk を実体ファイルとして解釈できない。そのまま *.lnk を処理対象に含めると、ツール側でエラーが発生しやすい。

この記事では、opus コマンドを用いた音声変換バッチを例に、.lnk を正しく展開して処理対象に含める方法を解説する。
古い手法に頼らず、現在でも安定して動作する実装に焦点を当てる。


スポンサーリンク
スポンサーリンク

なぜ .lnk はそのまま扱えないのか

.lnk ファイルは実体ファイルではない。
中身は「リンク先のパス」「起動オプション」「作業ディレクトリ」などを格納したバイナリ形式のショートカット情報である。

そのため、

  • dirfor %%f in (*.lnk) で列挙はできる
  • しかし、そのパスをそのままエンコーダや変換ツールに渡すと失敗する

という挙動になる。

重要なのは、.lnk を一度「リンク先の実ファイルパス」に変換する工程が必要という点だ。


.lnk を処理可能なパスに変換する

方法1:WScript.Shell を使ってターゲットパスを取得する(推奨)

現在でも最も安定している方法は、WScript.ShellCreateShortcut を使う手法だ。
PowerShell を使わず、純粋なバッチから呼び出せる点が強みになる。

以下は、.lnk を含む音声ファイルを再帰的に処理し、opus で変換する例だ。

修正後のバッチスクリプト例

@echo off
>nul 2>&1 chcp 65001

for /f "delims=" %%a in ('dir /b /s /a:-d *.mp3 *.ogg *.m4a *.wav *.flac *.wv *.lnk') do (
    call :process "%%~a"
)
goto :eof

:process
set "fp=%~1"
set "nm=%~n1"
set "xt=%~x1"

:: .lnk の場合はリンク先を解決
if /i "%xt%"==".lnk" (
    for /f "delims=" %%A in ('
        mshta.exe "javascript:
        var sh=new ActiveXObject('WScript.Shell');
        var lnk=sh.CreateShortcut('%fp:\=\\%');
        var fso=new ActiveXObject('Scripting.FileSystemObject');
        if(fso.FileExists(lnk.TargetPath))
            WScript.Echo(lnk.TargetPath);
        close();"
    ') do (
        set "fp=%%~fA"
        set "nm=%%~nA"
    )
)

:: opus 変換処理
opus ^
  -y -i "%fp%" ^
  -af dynaudnorm=p=0.65:m=2:f=200:g=15:s=30 ^
  -c:a libopus -b:a 128k -vn ^
  "F:\JDownloader\Musik Alben\xoutput\%nm%_dyn.ogg"

goto :eof

この実装のポイント

  • 拡張子が .lnk の場合のみ処理を分岐
  • mshta.exe から JScript を実行し、WScript.Shell.CreateShortcut() を利用
  • TargetPath の存在確認を行い、不正なリンクを回避
  • 実体ファイルのパスに差し替えたうえで opus を実行

この方法は Windows 10 / 11 の標準構成で動作し、.lnk の仕様変更の影響も受けにくい。


方法2:type コマンドを使う簡易的な方法(非推奨)

一見すると簡単に見えるのが、typefind を組み合わせる方法だ。

for /f "delims=" %%a in ('type "sample.lnk" ^| find ":\"') do set TARGET=%%a
echo %TARGET%

.lnk 内に含まれるパス文字列を拾い出すという発想になる。

ただし、この方法には明確な問題がある。

  • .lnk はバイナリ形式であり、構造が保証されていない
  • パスが複数含まれる場合、誤検出の可能性がある
  • UNC パスや相対パスでは失敗しやすい

検証用途なら使えるが、実運用には向かない


まとめ

バッチスクリプトで .lnk を処理対象に含める場合、リンク先の実体パスを取得する工程が不可欠となる。

選択肢は大きく2つある。

  • WScript.Shell を使って正式にリンク先を解決する方法
    • 動作が安定している
    • Windows 標準機能のみで完結する
    • 実運用向き
  • type コマンドで文字列を抜き出す方法
    • 実装は簡単
    • 再現性が低く、環境依存が大きい

音声変換やメディア処理のように失敗が許されないバッチでは、前者一択だ。

.lnk を「ただのファイル」と誤解しないこと。
一段階展開する、そのひと手間がスクリプト全体の安定性を大きく引き上げる。

実際の運用環境に合わせて、ぜひ組み込んでほしい。

コメント