Batch Dateinamen aufteilen und in Variable speichern

mischaef

Kassettenkind
Teammitglied
Registriert
Aug. 2012
Beiträge
5.937
Hallo zusammen,

ich suche unter Win7 nach einer Möglichkeit, einen Dateinamen an einer bestimmten Stelle (hier " - ") zu trennen und die Teile in Variablen zu speichern (z.B. um daraus später Ordner und Unterordner generieren zu können).

Ich habe dazu bereits Google bemüht und bin auch auf ein paar Dinge gestoßen, aber so ganz habe ich es noch nicht zusammen:

Code:
for /f "tokens=1 delims= - " %%a in %%~ni do @set var1=%%a
for /f "tokens=2 delims= - " %%a in %%~ni do @set var2=%%a

echo var1
echo var2

pause

Das war zumindest ein Gedanke, aber ich müsste die Abfrage ja in eine Schleife bekommen, damit ich den Inhalt direkt weiterverarbeiten kann.

Hat jemand eine Idee, was ich machen kann?

Besten dank

Michael
 
Machs einfach mit der PowerShell:
Code:
function Split-Files
{
	Param(
		[Parameter(
			Position = 1,
			Mandatory = $true,
			ValueFromPipeline = $true
		)]
		$Path
	)

	process
	{
		dir $Path | % {
			$dir = $_.BaseName -replace "-","\"

			Write-Host -ForegroundColor Green ("{0} -> {1}" -f $_.BaseName,$dir)
			mkdir -Force $dir
			mv $_ $dir | Out-Null
		}
	}
}

cd "R:\foo"
"." | Split-Files
Ersetzt - durch \, erstellt nen Ordner und verschiebt die Datei da rein.
 
OK, ich muss dazu sagen, dass ich mich NULL mit der PowerShell auskenne...

Ich habe jetzt versucht die Datei in der PS aufzufrufen, aber die Fehlermeldung wird ausgegeben:

Die Benennung "Dateinamen-zu-Ordner-Unterordner.ps1" wurde nicht als Name eines Cmdlet, einer Funktion, einer Skriptdatei oder eines ausführbaren Programms erkannt. Überprüfen Sie die Schreibweise des Namens, oder ob der Pfad korrekt ist (sofern enthalten), und wiederholen Sie den Vorgang. Bei Zeile:1 Zeichen:37
+ Dateinamen-zu-Ordner-Unterordner.ps1 <<<<
+ CategoryInfo : ObjectNotFound: (Dateinamen-zu-Ordner-Unterordner.ps1:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
 
Du musst es prinzipiell nicht als Funktion nutzen, sondern kannst auch direkt die Funktion bzw. das Script so aufrufen.

Erstell dir ne *.ps1 Datei im entsprechenden Ordner und füg einfach Folgendes ein:
Code:
$src = Resolve-Path .
dir -Exclude $MyInvocation.MyCommand.Name  | % {
	$dir = $_.BaseName -replace "-","\"
	$o = New-Object psobject
	$o | Add-Member Source (Join-Path $src $_.Name)
	$o | Add-Member Dest (Join-Path $src (Join-Path $dir $_.Name))
	$o
	#mkdir -Force $dir | Out-Null
	#mv $_ $dir | Out-Null
}
Danach ins Verzeichnis wechseln mit cd und .\dateiname.ps1 aufrufen. Ich hab mal noch den Output angepasst, damit du eine bessere Übersicht hast, ob das so passt. Beim Durchlauf dann einfach den Kommentar # vor mkdir und mv entfernen, damit auch erstellt und verschoben wird.
 
OK, bekomme aber weiterhin eine Fehlermeldung:

Die Benennung "Dateinamen.ps1" wurde nicht als Name eines Cmdlet, einer Funktion, einer Skriptdatei oder eines ausführbaren Programms erkannt. Überprüfen Sie die Schreibweise des Namens, oder ob der Pfad korrekt ist (sofern enthalten), un
d wiederholen Sie den Vorgang.
Bei Zeile:1 Zeichen:15
+ Dateinamen.ps1 <<<<
+ CategoryInfo : ObjectNotFound: (Dateinamen.ps1:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException

Habe die Set-ExecutionPolicy auf AllSigned stehen...falls das wichtig ist...
 
Die Execution Policy stört dort nicht, er findet das Script bei dir einfach nicht. Wie sieht denn dein Aufruf genau aus? Eigentlich musst du nur per cd in den Ordner wechseln und den Namen des Scripts mit .\ präfixen.
Code:
PS C:\> cd R:\foo
PS R:\foo> .\script.ps1

Source                           Dest
------                           ----
R:\foo\das ist-ein test.txt      R:\foo\das ist\ein test\das ist-ein test.txt
R:\foo\das ist-noch ein test.txt R:\foo\das ist\noch ein test\das ist-noch ein test.txt
R:\foo\das-ist-ein-test.txt      R:\foo\das\ist\ein\test\das-ist-ein-test.txt
R:\foo\das-ist-noch-ein-test.txt R:\foo\das\ist\noch\ein\test\das-ist-noch-ein-test.txt


PS R:\foo>
Aber ja, die Execution Policy kannst du auch temporär oder permanent auf RemoteSigned stellen. Wenn die Execution Policy greift, kommt folgende Fehlermeldung:
Code:
.\script.ps1 : Die Datei "R:\foo\script.ps1" kann nicht geladen werden. Die Datei "R:\foo\script.ps1" ist nicht digital signiert. Sie
könnend dieses Skript im aktuellen System nicht ausführen. Weitere Informationen zum Ausführen von Skripts und Festlegen der
Ausführungsrichtlinie erhalten Sie in "about_Execution_Policies" unter "http://go.microsoft.com/fwlink/?LinkID=135170"..
In Zeile:1 Zeichen:1
+ .\script.ps1
+ ~~~~~~~~~~~~
    + CategoryInfo          : Sicherheitsfehler: (:) [], PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess
 
OK, jetzt sind wir zumindest einen Schritt weiter, dass das Script abläuft, die Fehlermeldungen gehen aber weiter:

Add-Member : Es wurde kein Positionsparameter gefunden, der das Argument "Dest" akzeptiert.
Bei X:\AA\Ordnen\Dateinamen.ps1:6 Zeichen:21
+ $o | Add-Member <<<< Dest (Join-Path $src (Join-Path $dir $_.Name))
+ CategoryInfo : InvalidArgument: (:) [Add-Member], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.AddMemberCommand

Hab mal kurz Google bemüht, aber kann mit dem, was da steht, nicht wirklich viel anfangen. Aber wenn ich das richtig verstehe, schein irgendein Parameter nicht übergeben zu werden...
 
Zuletzt bearbeitet:
Hm, falsch kopiert? Beim Parameter drüber macht ers ja ohne zu Murren.

Sonst mal explizit versuchen:
Code:
$o | Add-Member -MemberType NoteProperty -Name Dest -Value (Join-Path $src (Join-Path $dir $_.Name))
 
Ok, wieder ein Schritt weiter: Es wird in der Ausgabe zumindest jetzt der Pfad und Dateiname angezeigt...aber auch wieder eine Fehlermeldung:

Add-Member : Es wurde kein Positionsparameter gefunden, der das Argument "Source" akzeptiert.
Bei X:\AA\Ordnen\Dateinamen.ps1:5 Zeichen:21
+ $o | Add-Member <<<< Source (Join-Path $src $_.Name)
+ CategoryInfo : InvalidArgument: (:) [Add-Member], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.AddMemberCommand

Ich war jetzt mal so naiv und hab die letzte Zeile von Dir mal mit "Source" zusätzlich reinkopiert (wollte ja auch mal was beisteuern..^^) aber das funktionierte auch nicht.
 
Ok, dann mal komplett explizit und vereinfacht:
Code:
$src = Resolve-Path .
Get-ChildItem -Exclude $MyInvocation.MyCommand.Name  | % {
	$dir = $_.BaseName -replace "-","\"
	$o = New-Object psobject
	$o | Add-Member -MemberType NoteProperty -Name Source -Value ($src.Path + "\" + $_.Name)
	$o | Add-Member -MemberType NoteProperty -Name Dest -Value ($src.Path + "\" + $dir + "\"+ $_.Name)
	$o
	#New-Item -Path $dir -ItemType Directory -Force | Out-Null
	#Move-Item -Path $_ -Destination $dir | Out-Null
}
 
Zuletzt bearbeitet: (Code überarbeitet)
Mäusemelken und so...^^

Fehler beim Aufrufen der Methode, da [System.Management.Automation.PathInfo] keine Methode mit dem Namen "op_Addition"
enthält.
Bei X:\AA\Ordnen\Dateinamen.ps1:6 Zeichen:72
+ $o | Add-Member -MemberType NoteProperty -Name Dest -Value ($src + <<<< "" + $dir + ""+ $_.Name)
+ CategoryInfo : InvalidOperation: (op_Addition:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
 
OK, großer Fortschritt (Yeah!!!) - keine Fehlermeldung, die Dateinamen werden richtig ausgegeben, nur das erstellen der Ordner und Unterordner erfolgt nicht...
 
Bingo....das wars jetzt, habe ich im Eifer übersehen. Hab nur eine Fehlermeldung wegen eines zu langen Dateinamens bekommen, aber das ist kein Problem.

Besten dank bis hier hin für den Sonntag-Support...^^

Kann ich die Datei jetzt eigentlich noch ändern, dass ich sie wie eine Batch-Datei per Doppelklick starten kann?

Und eine Frage zur Shell generell: Könnte ich alle Programme, die per Kommandozeile angesprochen werden, auch mit der PS verwenden? Dann würde ich mich eher in die PS als in Batchdateien einarbeiten...
 
Per Doppelklick gehts natürlich auch bzw. Rechtsklick -> Mit PowerShell ausführen. Für nen richtigen Doppelklick müsstest du halt das Kontextmenü ändern und die Standardaktion tauschen. Anpassungen musst du im Script keine dafür vornehmen, weil er ja eh immer aufs Arbeitsverzeichnis zurückgreift.

Mit der PS kannst du auch normale Progamme ausführen, klar. Favorisiert werden hierbei natürlich Cmdlets. Willst du explizit ne Anwendung ansprechen, hängst du einfach die Extension dran. Bspw. gibts in PS den Alias wget (zu finden über Get-Alias). Wenn du hier immer nur wget schreibst, wird das natürlich nix und er bezieht immer den Alias bzw. das Cmdlet Invoke-WebRequest ein. Hierbei reicht ein einfaches wget.exe als Aufruf. Du kannst aber auch nen expliziten Call machen, das würde dann mit dem & Operator (MSDN, SS64) geschehen. & wget p1 p2 p3 ...

Aber eins sei dir gesagt: Die PS arbeitet mit Objekten. Du kannst hierbei natürlich auch die üblichen Shell-Tools verwenden und darauf zurückgreifen, aber Strings auseinander friemeln und selbst parsen macht hierbei keinen Spaß.

Hinters Konzept wirst du mit der Zeit kommen. ;)
 
Bei mir wären das z.B. die Bereiche Video- oder Audiokodierung. Ich empfinde es als großen Vorteil über eine Batch-Datei Dateien mit verschiedenen Programmen automatisiert nacheinander bearbeiten zu können. Viele Dinge, die vorher einen hohen zeitlichen Aufwand und viele Clicks erfordert haben, laufen nun ab ohne das man am Rechner sein muss. Oder das man sich damit bestimmte spezielle Abläufe erstellen kann, ohne im jeweiligen Programm immer die Einstellungen ändern zu müssen - z.B. verschiedene Auflösung bei der MP3-Kodierung oder so. Einfach die jeweilige Batch nehmen und gut ist.

Aber gerade wenn es um verschiedene Verwendungen der Dateinamen (wie hier) geht, scheint die Batch an ihre Grenzen zu stoßen. Und gerade da könnte ich noch vieles anpassen oder vereinfachen. Daher die Frage...
 
Wenn du stumpfsinnig Programme aufrufen musst, dann kannst du ruhig bei Batch bleiben. Wenn du aber Daten sammeln und aufbereiten willst, kommst du mit der bedeutend PS besser. Es zusätzlich zu lernen schadet aber nie. ;) Scripte schreib ich persönlich nur noch in der PS, für irgendwelche Wrapper kommt aber weiterhin Batch zum Einsatz.

Der immense Vorteil ist auch, dass du direkt .NET Assemblies einbinden kannst. Wenn du also bspw. irgendwelche speziellen Prozesse benötigst und es dafür ne .NET Lib gibt, kannst du die auch direkt einbinden. Bspw. nutz ich für den Perceptual Image Hash diese Lib. Damit kann man zwei Bilder vergleichen und ihre Ähnlichkeit herausfinden. Sowas kann man dann wunderbar mit einem Script für zum Erstellen von Filmvorschauen (erstellt ein x*y Grid mit Vorschaubildern eines Films mittels ffmpeg, ähnlich MTN) verwenden und man kann perfekt ähnliche Videos auflisten und weiter verfahren.
 
Zurück
Oben