Kopieren von Dateien in bestimmte Verzeichnisse basierend auf dem Dateinamen

Kopieren von Dateien in bestimmte Verzeichnisse basierend auf dem Dateinamen

Echte Berührungspunkte mit Batch oder PowerShell habe ich zwar nicht, dennoch habe ich ein Mitarbeiterverzeichnis aufgesetzt, für das ich nun eine Lösung suche, bei der Dokumente über ein Skript automatisch in das besagte Verzeichnis kopiert werden:

  • Die zu kopierenden Dokumente liegen aktuell alle in einem Ordner und sollen in die jeweiligen Unterordner des Verzeichnisses kopiert werden.
  • Die Dateinamen der Dokumente sind wie folgt aufgebaut, wobei XX/ XXXXdie Variablen sind, die bei der Zuordnung eine Rolle spielen:
    • ABCD_123456_1234567_12345_XX.pdf
      ABCD_123456_1234567_12345_XXXX.pdf
  • Auch die Zielverzeichnisse enthalten diese, derzeit etwa fünfzig verschiedene

Ist es möglich, nur auf die Variablen zwischen dem dritten Unterstrich und der Dateiendung zu verweisen, da diese variieren, evtl. durch Anfügen einer drei- oder fünfstelligen Variable?

  • Ich habe die Suchfunktion bereits benutzt, aber nichts gefunden, was genau dem angegebenen Beispiel entspricht:
    • 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\
    • Möglicherweise:
      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\

Ich habe versucht, dies mit einzurichten $FileName-split, was nicht wirklich funktioniert hat, aber ich habe es geschafft, die Dateinamen zu vervollständigen, um sie tatsächlich hochzuzählen, wie in 30:27:

  • C:\ABCD_123456_1234567_12345_99C:\ABCD_123456_1234567_12345_0099
  • Ich habe Probleme beim Parsen der Zielordner, bei denen alle Ordner vorhanden sind und die Variablen in ihren Dateinamen enthalten sind, und bin bisher so weit gekommen:
    $Directory = "C:\Folder\" 
    $FileNames = (Get-Item $Directory).GetFiles()
    
    foreach($FileName in $FileNames) {
      $Destination = "\\10.10.10.10\destinationfolder"
      { Move-Item -Path $FileName -Destination $Destination }
    }
    

Gibt es eine Möglichkeit die Dateien nach Übereinstimmung zu verschieben oder muss ich alle Zielordner ebenfalls durch Zählen analysieren, damit der zu prüfende Teil festgelegt ist?

Dies kann es visualisieren

Antwort1

Dies wäre eine PowerShell-Lösung:

$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
}

Aktualisieren:

Aus Ihren Kommentaren schließe ich, dass dies eher das ist, was Sie wollen. Sie müssen jedoch sicherstellen, dass jede Endung genau einen passenden Ordner hat. Wenn es beispielsweise einen Ordner mit dem Namen „“ abc_123und einen anderen mit dem Namen „“ gibt def_123, wird einfach der erste Ordner verwendet, da wir gemäß Ihren Kommentaren nur die Endung berücksichtigen.

$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
    }
}

Es hat folgende Funktion:

  • $DestinationLegen Sie zunächst in der Variable den Zielordner fest.
  • Alle Elemente in einem Ordner abrufen mit Get-ChildItem(wenn Sie dies rekursiv machen müssen, fügen Sie den -RecurseSchalter hinzu, wenn Sie nur bestimmte Dateitypen -Filter *.txtoder eine andere Erweiterung hinzufügen möchten)
  • Schleife über alle gefundenen Dateien mitForEach-Object
  • den Basisnamen (das ist der Dateiname ohne Erweiterung) aufteilen _und den letzten Teil des Basisnamens erhalten und in der $EndingVariable speichern
  • Berechnen Sie den Ordnernamen, indem Sie die Endvariable in einen Ordnernamen formatieren mit-f
  • Verbinden Sie Join-Pathmit Ziel und berechneten Ordner und speichern Sie diesen in$Destinationpath
  • Erstellen Sie den Ordner, wenn er noch nicht existiert, mit New-Item. Wenn er bereits existiert, überschreibt diese Zeile den vorhandenen Ordner nicht
  • Kopieren Sie das Element in den Zielordner mitCopy-Item

Antwort2

ich verstehe Ihre Frage nicht, aber wenn Sie eine Datei wie diese kopieren und einfügen möchten

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

Dann kann dir vielleicht dieser cmd-Befehl helfen

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

zweiter Befehl mit gleicher Ausgabe:

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

Sie können ändern 20zu9999 Ausgabe

Dieser Befehl erstellt einen Ordner, falls dieser nicht existiert, und fügt dann die Datei ein. Wenn die Datei in einem Ordner vorhanden ist, wird sie überschrieben

AKTUALISIEREN:

Befehl zum Suchen von Dateinamen ohne Erweiterung(und Ergebnis an DateinameohneErweitert.txt anhängen)

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

Ausgabedatei lesen und Variablen in neuer Datei speichern variables.txt

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

Variablen lesen und Dateien in andere Ordner kopieren

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

Sie können ändern ABC_%xzu%x_ABC

Antwort3

Wenn ich Ihre Frage lese, denke ich, dass dies funktionieren sollte:

$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-ObjektString.Split()-MethodeVerbindungspfadTestpfadKopiere Artikel


Debuggen

Ich habe diese Variante des Codes verwendet, um die String-Manipulation zu beheben. Dabei wurde das Problem der Verwendung eines Unterstrichs in Erweiterungsstrings aufgedeckt. Setzt alle Zwischenvariablen in eineGridViewzum schnellen Scannen:

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

Optimierung

Zwischenvariablen sind während der Entwicklung/des Testens/Debuggens großartig, können aber die Dinge in einer so oft ausgeführten Schleife verlangsamen. Nachdem die Zuweisungen robust erschienen, arbeitete ich mich also rückwärts vor, indem ich zuerst den Ausdruck, der sie definiert, $SubFolderin die Zwischenablage kopierte, dann $SubFolderdas Vorkommen von $FullPath = ...durch seine in Klammern eingeschlossene Definition ersetzte und dann dasselbe für die Variablen innerhalb dieser Definition wiederholte. Außerdem kombinierte ich die Zuweisung mit der ersten Verwendung. Das Ergebnis war:

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
        }
    }
}

Höllisch schwer zu lesen, sollte sich aber bei großen Datensätzen lohnen.

verwandte Informationen