
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
/XXXX
die 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_99
→C:\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_123
und 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:
$Destination
Legen 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-Recurse
Schalter hinzu, wenn Sie nur bestimmte Dateitypen-Filter *.txt
oder eine andere Erweiterung hinzufügen möchten) - Schleife über alle gefundenen Dateien mit
ForEach-Object
- den Basisnamen (das ist der Dateiname ohne Erweiterung) aufteilen
_
und den letzten Teil des Basisnamens erhalten und in der$Ending
Variable speichern - Berechnen Sie den Ordnernamen, indem Sie die Endvariable in einen Ordnernamen formatieren mit
-f
- Verbinden Sie
Join-Path
mit 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 mit
Copy-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
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_%x
zu%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-Objekt • String.Split()-Methode • Verbindungspfad • Testpfad • Kopiere 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 eineGridView
zum 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, $SubFolder
in die Zwischenablage kopierte, dann $SubFolder
das 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.