@mtujikawa (辻川 光也)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

外部アプリによるリネームの追跡

解決したいこと

パワーシェルスクリプトで、ファイル名のリネームを行おうとしています。
外部プログラムと、pshの両方で複数の処理を行っています。
複数の処理を行いたいので、以下のように変数で処理結果を受け渡ししていくシナリオを考えていました。

$fn = もとのファイル名
$fn = 処理1 
$fn = 処理2
…
$fn = 最終ファイル名

外部プログラムでリネームした際、リネーム後のファイル名を受け取ろうと以下の文を書きました。

$fn = $args[0]
$fn = Start-Process -FilePath "rename.exe" -ArgumentList $fn -Wait

これでリネーム結果を受け取ろうとしましたが、そもそもその外部アプリが標準出力をしないので(Null)、

$fn = null

となってしまいます。

どうにかリネーム後のファイル名を取得し、新たに $fn に代入したいのですが、なにか良い方法はないでしょうか?

よろしくお願いいたします。

ちなみに外部アプリは Ashley という、書庫ファイルの中のフォルダー名を拾ってファイル名にしてくれる小さいアプリです。
他にも UnifyZip でゴミファイルの除去+rar→zip変換もしています。
これら処理の後にさらに自作でファイル名を置換しています。

これら複数の処理を一つのps1にまとめて、D&Dで処理させる予定です。

環境

Windows10 (64bit)
PSVersion: 5.1.19041.906

0 likes

3Answer

外部コマンドは独自のコマンドなんですよね。

例えば、外部コマンドがリネーム後のファイル名を標準出力に出力していれば(それ以外の情報は出力していないならば)、以下のように書けると思います。

$fn = $args[0]
$fn = rename.exe $fn
0Like

Comments

  1. @mtujikawa

    Questioner

    回答ありがとうございます。現在使用を検討しているコマンドは結果の標準出力に対応していないようで、Nullが返ってきてしまいます。
    ですので、別のアプローチが必要だと考えています。
    なにかアイデアございましたらご教示ください。
  2. 使用しているコマンドは修正できないということですね。

    あとは泥臭い方法ですが、呼び出す前後のフォルダ内のファイル名一覧を取得して、変わっているファイル名を調べるぐらいでしょうか。
    (他のプログラムなどに、ファイルを追加・削除・リネームなどをされないことが前提ですが)

まず、公式ドキュメントには「-PassThru」パラメーターをつけなきゃ「Start-Process」コマンドは何も返さないと書いてある気がします。(7.1も5.1も同じはず)

で、多分この修正をしても直らないような気もするので、私が一番に思いつく方法を書いておくと、
初めにlsコマンド(場合によっては-Nameや-Recurseオプション付き)でファイル名やパスの配列取得して、処理が終わった後にもう一回同じことやってCompare-Objectで比較するとかですかね。

分かりにくいかもしれませんが、何かあれば返信ください。

0Like

Comments

  1. @mtujikawa

    Questioner

    ご提案ありがとうございます。この方向で検討してみました。

自己レスです。
なんとか納得の行くものが作れたので、ここに残します。

Get-ChildItem で差分を取るしか無いのではないか? というご意見をもとに作成しました。

rename.ps1

# 処理対象のファイルがいるパスへ移動
$cp = Split-Path -Parent $args[0]
cd $cp

foreach($arg in $args){

$fn = $arg

# ファイル名フルパスに空白スペースが入っている場合の対策で"で囲む
$fn = "`"" + ${fn} + "`""

# 名称変更前のファイルリストをフルパスで取得
$d1 = Get-ChildItem * | ForEach-Object {$_.FullName}

# Ashelyでファイル名を書庫内フォルダ名にリネーム
Start-Process -FilePath "C:\Program Files (x86)\Ashley\Ashley.exe" -ArgumentList "/verysilent $fn" -PassThru -Wait -nonewwindow

# 名称変更後のファイルリストをフルパスで取得
$d2 = Get-ChildItem * | ForEach-Object {$_.FullName}

# 差分を取得して変更後のファイル名を取得
$diff = compare-object $d2 $d1 | ?{$_.SideIndicator -eq '<='}

# 差分があったら$fnに入れる。
if ($diff -ne $null) {
    $fn = $diff.InputObject

# 再び"で囲む
    $fn = "`"" + ${fn} + "`""

# $fnに対して処理2
# $fnに対して処理3

$fn = 最終ファイル名

}

こんな感じです。あまりスマートなやり方ではありませんが、求めるものはできました。

あとは Get-ChildItem で -include を使い、対象アイテムを絞るのも良いかと思います。私の場合には対象ファイルが rar書庫だったので下記のようになります。

rename2.ps1
$d1 = Get-ChildItem * -include *.rar | ForEach-Object {$_.FullName}
# $fnに対してリネーム処理
$d2 = Get-ChildItem * -include *.rar | ForEach-Object {$_.FullName}
$diff = compare-object $d2 $d1 | ?{$_.SideIndicator -eq '<='}
if ($diff -ne $null) {
    $fn = $diff.InputObject
}

差異がないとき、$diff が null になってしまうので、その処理は大切です。

渡す引数がフルパスでないとだめなプログラム、ファイル名だけでOKなプログラムや Rename-Item 、空白スペースの処理などなど課題が満載でした。

まだまだ改善の余地はあると思います。
ご指摘等ございましたらよろしくお願いいたします。

0Like

Your answer might help someone💌