ファイル名に基づいて特定のディレクトリにファイルをコピーする

ファイル名に基づいて特定のディレクトリにファイルをコピーする

実際には、Batch や PowerShell を使用した経験はありませんが、従業員ディレクトリを設定しており、スクリプトを使用してドキュメントが前述のディレクトリに自動的にコピーされるソリューションを探しています。

  • コピーするドキュメントは現在すべて 1 つのフォルダーにあるので、ディレクトリのそれぞれのサブフォルダーにコピーする必要があります。
  • ドキュメントのファイル名は次のように構成されます。/ はXX割り当てXXXXで役割を果たす変数です。
    • ABCD_123456_1234567_12345_XX.pdf
      ABCD_123456_1234567_12345_XXXX.pdf
  • ターゲットディレクトリにもこれらが含まれており、現在約50個の異なるディレクトリがあります。

3 番目のアンダースコアとファイル拡張子の間にある変数のみを指すようにすることは可能ですか? これらは変化するため、3 桁または 5 桁の変数を追加して指定することは可能ですか?

  • すでに検索機能を使用しましたが、指定された例に正確に一致するものは見つかりませんでした。
    • C:\ABCD_123456_1234567_12345_99\\11/17/19/1991\Directory\99_ABCD\
      C:\ABCD_123456_1234567_12345_9999\\11/17/19/1991\Directory\9999_ABCD\
    • おそらく:
      C:\ABCD_123456_1234567_12345_999\\11/17/19/1991\ directory\999_ABCD\
      C:\ABCD_123456_1234567_12345_99999\\11/17/19/1991\Directory\99999_ABCD\

これを で設定しようとしましたが$FileName-split、うまくいきませんでしたが、30:27 のようにファイル名を完成させて実際にカウントすることができました。

  • C:\ABCD_123456_1234567_12345_99C:\ABCD_123456_1234567_12345_0099
  • すべてのフォルダーが存在し、ファイル名に変数が含まれている状態で、宛先フォルダーを解析するのに苦労していますが、ここまでは成功しました。
    $Directory = "C:\Folder\" 
    $FileNames = (Get-Item $Directory).GetFiles()
    
    foreach($FileName in $FileNames) {
      $Destination = "\\10.10.10.10\destinationfolder"
      { Move-Item -Path $FileName -Destination $Destination }
    }
    

一致によってファイルを移動する方法はありますか? それとも、チェックする部分が設定されるように、すべての宛先フォルダーをカウントして解析する必要がありますか?

これはそれを視覚化するかもしれない

答え1

これは PowerShell ソリューションになります:

$Destination = "\\10.10.10.10\destinationfolder"
Get-ChildItem "C:\Folder" | ForEach-Object {
    $Head = ($_.BaseName -split '_')[0]
    $Tail = ($_.BaseName -split '_')[-1]
    $DestinationPath = Join-Path $Destination ("{0}_{1}" -f $Head, $Tail)
    New-Item -Path $DestinationPath -ItemType Directory -ErrorAction SilentlyContinue
    Copy-Item $_.FullName $DestinationPath -Force
}

アップデート:

あなたのコメントに従って、これがあなたが望んでいることだと思います。ただし、各エンディングに一致するフォルダーが正確に1つあることを確認する必要があります。たとえば、と呼ばれるフォルダーとと呼ばabc_123れる別のフォルダーがある場合def_123、あなたのコメントの時点では、エンディングのみを考慮するため、最初のフォルダーが採用されます。

$Destination = "\\10.10.10.10\destinationfolder"
Get-ChildItem "C:\Folder" | ForEach-Object {
    $Tail = ($_.BaseName -split '_')[-1]
    $Filter = "*_{0}" -f $Tail
    $DestDir = Get-ChildItem $Destination -Filter $Filter -Directory | Select-Object -ExpandProperty FullName -First 1
    if ($DestDir) {
        Copy-Item $_.FullName $DestDir -Force
    } else {
        "No Directory was found that matched the Filter {0} in Directory {1}" -f $Filter, $Destination | Write-Host
    }
}

次のことを行います:

  • $Destinationまず変数に宛先フォルダを設定します
  • フォルダー内のすべてのアイテムを取得しますGet-ChildItem(これを再帰的に実行する必要がある場合は-Recurseスイッチを追加し、特定のファイルタイプのみが必要な場合はスイッチを追加するか-Filter *.txt、その他の拡張子を追加します)
  • 見つかったすべてのファイルをループしてForEach-Object
  • ベース名(拡張子のないファイル名)を分割し_、ベース名の最後の部分を取得して$Ending変数に保存します。
  • 終了変数をフォルダ名にフォーマットしてフォルダ名を計算します。-f
  • Join-Path宛先と計算されたフォルダを接続して保存するために使用します$Destinationpath
  • フォルダーが存在しない場合は作成しますNew-Item。既に存在する場合は、この行は既存のフォルダーを上書きしません。
  • アイテムを宛先フォルダにコピーしますCopy-Item

答え2

あなたの質問は理解できませんが、このようにファイルをコピーして貼り付けたい場合は

ABCD_123456_1234567_12345_1.txt copy into 1_ABCD
ABCD_123456_1234567_12345_2.txt copy into 2_ABCD
ABCD_123456_1234567_12345_3.txt copy into 3_ABCD
ABCD_123456_1234567_12345_9999.txt copy into 9999_ABCD

おそらくこのcmdコマンドが役に立つでしょう

echo D|for /l %x in (1,1,20) do @xcopy ABCD_123456_1234567_12345_%x.txt ABC_%x\ /Y /Q

同じ出力を持つ 2 番目のコマンド:

for /l %x in (1,1,20) do @xcopy ABCD_123456_1234567_12345_%x.txt ABC_%x\ /Y /Q

変更でき20ます9999 出力

このコマンドは、存在しない場合はフォルダーを作成し、ファイルを貼り付けます。フォルダー内にファイルが存在する場合は、ファイルを上書きします。

アップデート:

拡張子のないファイル名を検索するコマンド(そして結果をfilenamewithouttext.txtに追加します)

for /f %x in ('dir /b') do @echo %~nx>>filenamewithoutext.txt

出力ファイルを読み取り、変数を新しいファイルに保存する variables.txt

for /f "tokens=5 delims=_" %x in (filenamewithoutext.txt) do @echo %x>>variables.txt

変数を読み取り、ファイルを別のフォルダにコピーする

for /f %x in (variables.txt) do @xcopy ABCD_123456_1234567_12345_%x.txt ABC_%x\ /Y /Q

変更できABC_%xます%x_ABC

答え3

あなたの質問を読んで、これはうまくいくはずだと思いました:

$Source      = 'C:\Folder\'
$Destination = '\\10.10.10.10\destinationfolder\'

Get-ChildItem -Path $Source -FIlter *.pdf -File | ? { ($BaseName = $_.BaseName) -match '_' } | ForEach{
    $Head       = $BaseName.Split('_')[0]
    $Tail       = $BaseName.Split('_')[-1]       ### (Index of -1) -> Last array element
    $SubFolder  = "$Tail`_$Head"                 ### *** Escaped <underscore> required ***
    If ( Test-Path ( $FullPath = Join-Path $Destination $SubFolder ) ) {
        Copy-Item -Literal $_.FullName $FullPath
    } Else {
        '"{0}" not copied because the directory "{1}" does not exist' -f $_.Name , $FullPath | Write-Output
    }
}

ForEachオブジェクトString.Split() メソッド結合パステストパスコピーアイテム


デバッグ

私はこのコードのバリエーションを使用して文字列操作のトラブルシューティングを行いましたが、展開文字列でアンダースコアを使用する問題が明らかになりました。中間変数をすべてGridViewクイックスキャンの場合:

Get-ChildItem -Path $Source -FIlter *.pdf -File | ? BaseName -match '_' | ForEach{
    $Head       = $_.BaseName.Split('_')[0]
    $Tail       = $_.BaseName.Split('_')[-1]
    $SubFolder  = "$Tail`_$Head"
    $FullPath   = Join-Path $Destination $SubFolder

    [PSCustomObject]@{
        'Head'       = $Head
        'Tail'       = $Tail
        'SubFolder'  = $SubFolder
        'FullPath'   = $FullPath
        'SubExists'  = Test-Path $FullPath
    }
} | Out-gridview

最適化

中間変数は開発/テスト/デバッグ中は便利ですが、ループ内で何度も実行されると速度が低下する可能性があります。そのため、割り当てが堅牢であることがわかったら、逆方向に作業を進め、まず定義式を$SubFolderクリップボードにコピーし、次に$SubFolderの出現を$FullPath = ...括弧で囲んだ定義に置き換え、次にその定義内の変数に対して同じことをしました。また、割り当てと最初の使用を組み合わせました。結果は次のようになりました。

Get-ChildItem -Path $Source -FIlter *.pdf -File | ForEach{
If ( ($BaseName = $_.BaseName) -match '_' ) {
        If ( Test-Path ( $FullPath=Join-Path $Destination "$($BaseName.Split('_')[-1])`_$($BaseName.Split('_')[0])" ) ) {
            Copy-Item -Literal $_.FullName $FullPath
        } Else {
            '"{0}" not copied because the directory "{1}" does not exist' -f $_.Name , $FullPath | Write-Output
        }
    }
}

読むのは大変ですが、大規模なデータセットの場合は価値があるはずです。

関連情報