

**最終的にやりたいこと**  
・対象フォルダ(サブフォルダ含む)内にある特定拡張子ファイルのみ、指定フォルダへ移動したい  
・同名ファイルが存在していた場合、上書きではなく連番付与したい

* * *

**試したこと**  
・[特定ファイルを指定フォルダに一括移動させるバッチファイル](https://www.lisz-works.com/entry/file-batch-
processing)で移動確認は出来たのですが、同名ファイルが存在していた場合、1ファイルに上書きされてしまいます

* * *

**Q**  
・同名ファイルが存在していた場合、上書きではなく連番付与する、条件追加が分かりません  
・同名ファイルが存在していた場合の条件分岐はどの段階で追加する？ list.txt作成する時？ それとも、ファイルを1行ずつ処理する時？


CC BY-SA 4.0
2018-10-03T12:46:58.047
48933
2019-02-13T12:02:40.520
7886
question
windows
Windowsバッチのファイル移動で、同名ファイルが存在していた場合、上書きではなく連番付与したい


> 同名ファイルが存在していた場合の条件分岐はどの段階で追加する？ list.txt作成する時？ それとも、ファイルを1行ずつ処理する時？

list.txt作成時に条件分岐を加えると、ファイル作成前に新規作成された場合上書きされますので、処理する時に追記するべきだと思います。

> 同名ファイルが存在していた場合、上書きではなく連番付与する、条件追加が分かりません

Powershellの方が連番の取得など細かい操作に向いているので、Powershellで実装例を作ってみました。

[code]

    # 変数を変更して実行すること
    Move-Mp3 -Path "D:\test\src" -Dest "D:\test\dst" -IsMove $false
    
    <#
    .SYNOPSIS
     指定フォルダ以下のファイルを移動またはコピーし、移動先に同名ファイルが存在する場合は連番を付加する
    
    .DESCRIPTION
     指定された移動元フォルダから特定拡張子のファイルを再帰的に取得して、指定された出力先に移動またはコピーする。
     出力先に同名ファイルが存在する場合は連番を付加して上書きを回避する。
     移動元フォルダの構成は再現せず、出力先フォルダ直下に直接移動またはコピーを行う。
    
    .PARAMETER Path
     移動元フォルダのパス。
    
    .PARAMETER Dest
     移動またはコピーする出力先フォルダのパス。
    
    .PARAMETER Filter
     ファイル名や拡張子などのフィルタ。
     省略時は"*.mp3"
    
    .PARAMETER IsMove
     $trueの場合は移動、$falseの場合はコピーを行う。
     省略時は移動する
    
    .NOTES
     出力先フォルダが存在しない場合はエラーとなる。
    
    #>
    function Move-Mp3 {
        Param(
            [parameter(mandatory=$true)][String]$Path,
            [parameter(mandatory=$true)][String]$Dest,
            [String]$Filter = "*.mp3",
            [bool]$IsMove = $true
        )
    
        ls -Path $Path -Filter $Filter -Recurse | foreach {
          $src = $_.FullName
          $name = $_.Name
          $target = "${Dest}\${name}" 
          while (Test-Path $target) {
            $f = [System.IO.Path]::GetFileNameWithoutExtension($name)
            $x = [System.IO.Path]::GetExtension($name)
            $r = [regex]"^(.*?)([0-9]+)$" 
            $m = $r.Match($f)
            if($m.Success) {
              # 末尾の数値をカウントアップ
              $index = [int]$m.Groups[2].Value + 1
              $name = "{0}{1}{2}" -f $m.Groups[1].Value, $index, $x
            } else {
              # 末尾に数値がなければ拡張子の前に"-1"を追加
              $name = "{0}-1{1}" -f $f, $x
              $index++
            }
            $target = "${Dest}\${name}" 
          }
          if($IsMove) {
            move $src $target
            "$src を $target に移動しました。"
          } else {
            copy $src $target
            "$src を $target にコピーしました。"
          }
        }
    }
    
[/code]

当初はbatファイルで実装しようとしましたが、連番の抽出などが面倒で挫折しました。  
中途半端な実装例でもbatファイルを見たい場合は、履歴から修正前の回答をご参照ください。


CC BY-SA 4.0
2018-10-04T10:01:20.970
48965
2018-10-07T10:13:20.520
2018-10-07T10:13:20.520
9820
9820
48933
answer


Git Bashの使用をおすすめします。なぜなら、PowerShellよりもBashのユーザーのほうが多いのでより多くの知見が得られるためです。Git
BashのなかにはPerlも入っています。Perlがあれば一通りのことはできます。

例えば`D:\test`のなかに`A`と`B`というサブディレクトリがあるとしましょう。Git
Bashでは`D:\test`は`/d/test`という表記になるので`cd /d/test/`と入力します。

さて、`A`の中に、`aaa.txt`, `aaa.bin`, `bbb.txt`, `ccc.txt`,
`ccc.out`というファイルを用意して`B`に`.txt`のファイルだけ移動させることを考えます。`B`には`bbb.txt`,
`bbb_1.txt`, `bbb_2.txt`, `ccc.txt`を用意しておきます。

次のPerlスクリプトファイル`move.pl`を用意します。

[code]

    use warnings;
    use File::Copy 'move';
    
    $srcdir = "./A"; # 移動元ディレクトリ
    $dstdir = "./B"; # 移動先ディレクトリ
    $ext = "txt"; # 拡張子
    
    opendir(FROM, $srcdir);
    foreach(readdir(FROM)) {
        next if $_ !~ /^(.*)\.${ext}$/;
        $original = $1; # 元々のファイル名(拡張子無し)
        if ( !( -f "${dstdir}/${original}.${ext}" ) ) {
            $filename = "${original}.${ext}";
        } else {
            $i = 1;
            while ( -f "${dstdir}/${original}_${i}.${ext}" ) {
                $i++;
            }
        $filename = "${original}_${i}.${ext}";
        }
        print "${srcdir}/${original}.${ext}を${dstdir}/${filename}に移動\n";
        move("${srcdir}/${original}.${ext}", "${dstdir}/${filename}");
    }
    closedir(FROM);
    
[/code]

このスクリプトをGit Bashの`/d/test/`内で実行します。実行コマンドは次のとおりです。

[code]

    perl move.pl
    
[/code]

実行すると次のような表示が出ます。

[code]

    ./A/aaa.txtを./B/aaa.txtに移動
    ./A/bbb.txtを./B/bbb_3.txtに移動
    ./A/ccc.txtを./B/ccc_1.txtに移動
    
[/code]

実際、`A`の中が`aaa.bin`, `ccc.out`だけになり、`B`のなかに`aaa.txt`, `bbb.txt`, `bbb_1.txt`,
`bbb_2.txt`, `bbb_3.txt`, `ccc.txt`, `ccc_1.txt`が入っている状態となります。


CC BY-SA 4.0
2019-01-13T08:02:03.627
51942
2019-01-13T08:02:03.627
31654
48933
answer