Web入稿用のファイルリネームが面倒だからスクリプトを組んだ話

夏コミの新刊をギリギリで入稿して印刷所からの返信を震えて待ってます。ここなつているです。

今回の記事はなんと技術メモ(?)です。といっても備忘録や単なる日記に近く、誰かがパッと見て役に立つような記事にはならないと思います。

技術メモ、ひょっとしたら書くかもな~くらいに思っていたのですが本当に書く日が来るとは思いませんでした。

あ、ちなみに冒頭でサラッと書きましたがC94にサークル参加します!一日目東フ18a「うたうアルミナ。」です!よろしくお願いします!(宣伝)

やりたいこと

僕は毎回ポプルスさんに印刷をお願いしているのですが、

Web入稿するときにファイル名を連番、かつ「003」から開始しないといけないという決まりがあります。

(ストレート値引きとか快速本を利用するときだけだったかな?)

が、CLIP STUDIO PAINT EXで原稿のPSDファイルを出力するとき、当然「001」からの連番になりますし、

出力ダイアログで作品名の入力を促されるのでそれを入れると出力ファイルにもその作品名が必ずくっついてきます。

例えばこんな感じに。

うすいほん_001.psd
うすいほん_002.psd
うすいほん_003.psd
...

で、これを以下のようにリネームしないといけないわけなんですが……

003.psd
004.psd
005.psd
...

こんなもん20ページや30ページ分の作業やってられるか!!!!

ってなるので、Powershellを使って一括変換したいと思います。

とにもかくにもどんなのを作ったのか見せろ

書いたスクリプトはこちら。

######
# Path: 変換したいファイルがあるフォルダのパス
# Title: CLIP STUDIOで出力時に指定した作品名
######

Param(
    [parameter(mandatory=$true)][string]$Path,
    [parameter(mandatory=$true)][string]$Title
)

New-Variable parsed

Get-ChildItem -Path $Path | ForEach-Object {
    # 作品名を取り除く
    $titleWithUnderbar = $Title + "_"
    $titleRemovedStr = $_.BaseName -replace $titleWithUnderbar,""

    # 数値でないものが混じっている場合は何もしない
    if ([int]::TryParse($titleRemovedStr , [ref]$parsed)) {
        # 文字列を3始まりにするために+2する
        $incremented = $parsed + 2

        # ページ数の0埋めと拡張子の付与
        $newName = $incremented.ToString().PadLeft(3,"0") + ".psd"

        # ファイル名をリネームする
        Rename-Item $_.FullName -NewName $newName
    }
}

※このスクリプトの動作は一切保証できませんし何かあっても僕は責任取りません。必ずバックアップを取ってから使用してください。

※そもそもPowershellって何?という人は使わない方が賢明かと思われます。もしくは興味があれば勉強してみてください。

スクリプトのコメントにもありますが、やっていることは

  1. 作品名とアンダーバーが邪魔なので除去した文字列を作る(拡張子も数値変換時に邪魔なので除去)
  2. その文字列を数値に変換して+2する
  3. 数値を再び文字列に戻したついでに拡張子もつける
  4. できあがった文字列でファイルをリネームする

ですね。

ちなみに、うっかりhyoushi.psdとかを既にフォルダに含めてしまっている場合に備えて、

数値に変換できない(というか、形式が作品名_番号.psdに一致しない)ファイル名のものは処理対象から外しています。

これを以下のように実行してやると

PS > .\script.ps1 -Path .\Documents\shinkan -Title "うすいほん"

一発で番号のみ、かつ003からの連番になります!やったね!

書くときつまづいた点

で、せっかくなのでこれを書くときにハマったポイントを書いておきます。技術メモっぽいですね。


◆ファイルオブジェクトから拡張子を取り除いた文字列を取得するならBaseNameで一発

早速Powershellクソ初心者であることがバレバレのTipsが来たぞ。

最初、.NETのメソッドを使って拡張子除去しようとしてたんですけど、

ファイルオブジェクトをそのまま突っ込んでしまってPowershellにキレられました。(キレられたのそこじゃなかったかもしれないけど)

しかもその.NETのメソッドというのが

[System.IO.Path]::GetFileNameWithoutExtension($filePath)

と長ったらしいったらありゃしない。

Get-ChildItemでファイルオブジェクトを取得しているんだから、$_.BaseNameを参照すれば一発だったんですよ。はあ。

ちなみに$_.FullNameで逆にフルパスが取得できたりしますね。便利~。


◆ref変数は事前に宣言しておく必要がある

もし目的のファイル以外に何か悪さしたら大変だ、あと無駄なエラーは見たくない、と思って単なる[int]での型変換を避け、

TryParseくんを導入したらまたPowershellにキレられました。

「[ref] は、存在しない変数に適用できません。」

正直最初見たときはスクリプト言語で変数を事前宣言しておかなきゃいけないとは何事だ!?と思いましたが、

.NETを使う以上避けられない仕様とかなんかそんなところなのでしょう。

というわけで以下の一行があるのです。

New-Variable parsed

ちなみに、$parsed=$nullとかでも大丈夫です。あと、変数のスコープには気をつけましょう。


◆数値変換すると3桁目までの0埋めが消えてしまう

そりゃそうなんですよ。数値型は一桁の数値でも三桁として扱うなんて情報持つことができませんからね。(16進数とかだと話は別みたいですけど)

で、これを数値に変換しちゃったあとに戻す術というのがわからなかったんですが、

一度そのまま文字列に戻してからstring型に用意されているPadLeftで解決。

主に一覧出力とかでインデントを揃えて見やすくするために使われるメソッドですが、

こういう使い方もできるんですね。


◆Rename-Itemは元ファイル指定はパス指定じゃないとダメだけど変換後の名前指定はパスで指定できない

これが今回挙げた中で一番厄介な問題ではないでしょうか。

Powershellでのファイル管理に慣れ親しんだ人には常識かもしれませんが、

Rename-Itemは元ファイルの指定(第一引数)はパス指定で、

変換後の名前(第二引数または-NewNameで指定)はファイル名単体の文字列で指定しなければならないのです。

僕の場合、元ファイルを名前で指定というかGet-ChildItemで取得しているファイルオブジェクトをそのまま指定したら

「そんな項目存在しない」とキレられてエラーになってしまい、

しかもスクリプトではなくワンライナーで単体動作確認していたときには動いていたのでわけがわかりませんでした。

これは、入力が相対パスであると認識されておりスクリプト実行時は実行ディレクトリの直下に対象ファイルがなかったためにエラー、

動作確認時にはファイルがあるディレクトリで直接実行していたのでエラーにならなかった、というわけだったのでした。

変換後の名前指定では逆にパスを受け付けないというのも厄介ですね。

Linuxでファイルリネームするときはmvを使うのが一般的かと思われるので、こういうのあると混乱しますね……

(実際、名前の変換にRename-ItemよりもMove-Itemを推奨しているブログも見かけました)


とまあ、今回はこんなところでした。たった10行程度のスクリプトを書くだけでこんなに色々出ると思ってなかったので知識不足で情けない限りです。

せっかくだから最後にもう一回宣伝

C94(2018年夏コミ)参加します!

1日目 東フ18a 「うたうアルミナ。」です!

入稿した原稿に不備がなければ新刊はひなビタ♪の百合本です!!

よければ遊びに来てくださいね!

おわり