Powershell 5.x: Verzeichnis erstellen und Leerzeichen

ral9004

Lieutenant
Registriert
Dez. 2017
Beiträge
569
Hallo

In Powershell erstelle ich ein Verzeichnis mit Leerzeichen so:
New-Item -Path "x:\temp\Alle lieben Pizza" -ItemType Directory -Force

In meinem Skript muss ich die Anführungszeichen nicht angeben.
Warum das so ist, interessiert mich. Falls das jemand auf Anhieb versteht.

Das Skript sucht nach Dateien des Types PPT.
In jedem gefundenen Dateinanen werden Punkte durch Leerzeichen ersetzt.
Dann wird ein Verzeichnis mit dem Namen der Datei erstellt
Die Datei wird dorthin verschoben.

D.h. aus einer Datei "CP.GK.NV.PPT" wird ein Verzeichnis "CP GK NV" erstellt und die Datei dorthin verschoben.

Ich hätte erwartet, dass diese Zeile nur akzeptiert wird, wenn ich Anführungszeichen hinzufüge
New-Item -Path $destinationDirectory -ItemType Directory -Force


** Start Skript **

PowerShell:
# Get a list of all ".ppt" files in the source directory
$pptFiles = Get-ChildItem -Path $sourceDirectory -Filter "*.ppt"
foreach ($pptFile in $pptFiles) {
    # Get the file name without extension
    $fileNameWithoutExtension = [System.IO.Path]::GetFileNameWithoutExtension($pptFile.Name)

    # Remove dots from the file name
    $folderName = $fileNameWithoutExtension -replace '\.',' '
        
    # Create the directory with the modified folder name
    $destinationDirectory = Join-Path -Path $sourceDirectory -ChildPath $folderName
    write-host destination
    write-host $foldername
    write-host $destinationDirectory
    New-Item -Path $destinationDirectory -ItemType Directory -Force
    
    # Move the .ppt file to the new directory
    Move-Item -Path $pptFile.FullName -Destination $destinationDirectory
}

** Ende Skript **

Beste Grüsse
 
Weil Leerzeichen in Powershell Trennzeichen sind.

Beispiel:
PowerShell:
New-Item -gedöns
# ^ Fehler, weil kein Schalter "gedöns" existiert

New-Item -Path
# ^ Fehler, weil kein Parameter für Path angegeben wurde

New-Item -Path "C:\Temp\test test"
# ^ Geht, weil korrekte Syntax. Erstellt eine Datei "test test" unter C:\Temp

New-Item -Path C:\Temp\test test
# ^ Fehler, "test" ist hier ein weiteres Argument und PowerShell versucht,
# es an einen Parameter zu binden. Das geht nicht

$folder = "C:\Temp\test test"
New-Item -Path $folder
# ^ Der Inhalt von $folder ist klar definiert und es muss nur $folder an -Path gebunden werden

$folder = C:\Temp\test test
# ^ Das geht nicht, weil C:\Temp\test keine Funktion ist.

Das Problem, was sich dabei ergibt ist einfach, dass nach einem Leerzeichen das aktuelle Token endet und ein neues anfängt. PowerShell hat nichts gegen Leerzeichen in Pfaden. PowerShell kann nur nicht wissen, wo du gerne möchtest, dass der Pfad endet. Und Leerzeichen sind vorrangig Trennzeichen zwischen Befehlstoken, nicht Literale in Pfadangaben.
Oder in bildlicher: Solange du den String in eine Variable packst (hier: $destinationDirectory ) weiß PowerShell, dass Leerzeichen darin NICHT zum Befehl gehören, sondern zum string.

Ich hoffe, das ist halbwegs verständlich.
 
  • Gefällt mir
Reaktionen: Der Lord und Rego
alles was nach "-path" steht wird als path interpretiert solange kein scriptsprachenspezifisches "keyword" getriggert wird.

du wirst nur probleme bekommen ohne anführungszeichen, wenn dein pfad beispielsweise den string "-itemtype" enthalten soll. was ja nach ordner-/dateinamenkonvention auch valide wäre.
 
Hallo SoDaTierchen und Redundanz

Danke für das Feedback.
Das PS kein Problem bzw. diese als Trennzeichen interpretiert ist klar.

Was mich verwirrt, dass das Betriebssystem einen Namen mit Leerzeichen für ein Verzeichnis akzeptiert.
Ich übergebe einen String mit Leerzeichen an das Cmdlet und erstellte mit New-item ein Verzeichnis.

Beste Grüsse
 
ral9004 schrieb:
Was mich verwirrt, dass das Betriebssystem einen Namen mit Leerzeichen für ein Verzeichnis akzeptiert.
Warum? Leerzeichen sind gültige Zeichen für Dateinamen. Wie oben schon geschrieben steht, ist das einzige Problem mit Leerzeichen, das es in vielen Programmier- und Skriptsprachen gleichzeitig ein Trennzeichen ist. Daher muss man Pfadnamen als Literale (konstante Strings) mit Leerzeichen fast immer "quoten", in Powershell also in doppelte Anführungszeichen einschließen.
 
TomH22 schrieb:
Warum? Leerzeichen sind gültige Zeichen für Dateinamen. Wie oben schon geschrieben steht, ist das einzige Problem mit Leerzeichen, das es in vielen Programmier- und Skriptsprachen gleichzeitig ein Trennzeichen ist.
Hallo TomH22

Weil das Dateisystem NTFS ein Problem mit Leerzeichen hat, wenn ich sie nicht "quote".
Egal ob ich CMD oder PS nehme:

New-Item -Path "Alle lieben Pizza" -ItemType Directory -Force

Wenn ich mit write-host die Variable ausgebe sehe ich, dass diese keine quotes hat.
Dennoch akzeptiert new-item den Namen. D.h. die Leerzeichen werden nicht als Trennzeichen interpretiert.
Einerseits bequem das ich diese nicht auch hinzufügen muss.
Anderseits wäre es nett gewesen, die Logik dahinter zu verstehen.

Aber lassen wir es. Einem geschenkten Gaul... ;):p

Beste Grüsse
 
ral9004 schrieb:
Anderseits wäre es nett gewesen, die Logik dahinter zu verstehen.
Das ist kein Hexenwerk, also versuchen wir es weiter.
ral9004 schrieb:
Aber lassen wir es. Einem geschenkten Gaul...
Ich weiß nicht, wie ich das interpretieren soll. Ohne Deine Smileys hätte ich es jetzt für "passiv-aggresiv" gehalten. Aber ich nehme mal an, dass Du einfach nur "genervt" bist. Aus über 30 Jahren Programmiererfahrung heraus kann ich Dir sagen: Das kommt vor, davon darf man sich nicht beirren lassen;)


ral9004 schrieb:
Weil das Dateisystem NTFS ein Problem mit Leerzeichen hat, wenn ich sie nicht "quote".
Nein, nicht das Dateisystem hat ein "Problem" mit den Leerzeichen, sondern die Skriptsprache. Und auch die hat kein wirkliches Problem, dazu gleich mehr...

Das Dateisystem (bzw. der Dateisystem-Treiber im Betriebsystem) bekommt den Pfadnamen immer ohne Anführungszeichen, und im Falle von NTFS auch intern immer als Unicode String.

Zurück zur Powershell Programmiersprache: Die Anführungsstriche sind ein "syntaktisches Element" der Programmiersprache, die sagen dem Parser (das ist der Teil der den PowerShell Code analysiert und "versteht"), "hier geht eine Zeichenfolge (engl. String) los, und ab sofort interpretierst Du alles was jetzt kommt nicht mehr als Powershell Code bis wieder ein Anführungszeichen kommt". Die Anführungsstriche selber werden aber nicht mehr Bestandteil dieser Zeichenfolge.
Schreibst Du also
PowerShell:
$s="Guten Morgen"
Write-Host $s
wird Guten Morgen ohne die Anführungsstriche ausgegeben. So einen String in Anführungstrichen bezeichnet man als "String-Literal".
Ein bisschen kompliziert wird es, wenn man Anführungsstriche selber in einen String einbauen will. Dann muss man in Powershell die Anführungstriche "escapen":

PowerShell:
$s="Guten "" Morgen"
Write-Host $s

gibt Guten " Morgen aus. Und
PowerShell:
$s="""Guten  Morgen"""
Write-Host $s
dann "Guten Morgen", also den Text in Anführungsstrichen.

Der Versuch ein Directory mit diesem Namen anzulegen, führt zu diesem Ergebnis:
1693332838530.png


Windows meckert, dass an Position 1 ein "illegales Zeichen" ist. Das ist logisch, den Windows Dateinamen dürfen zwar Leerzeichen enthalten, aber keine Anführungszeichen.

Das Guten Morgen ohne Anführungszeichen funktioniert hingegen - auf den Screnshot verzichte ich, das hast Du ja schon selber ausprobiert...

Dieser Code
ral9004 schrieb:
New-Item -Path "Alle lieben Pizza" -ItemType Directory -Force
macht in Wirklichkeit folgendes:
Er ruft das New-Item "CmdLet" auf, das hat mehrere Parameter, einer davon ist -Path. Ein Parameter ist im Prinzip das gleiche wie eine Variable. Dieser Variable wird der String Alle lieben Pizza zugewiesen, auch hier wieder ohne die Anführungsstriche.

Um das ganze ein bisschen verwirrender zu machen, erlaubt Powershell eine "Vereinfachung": Wenn ein String keine Sonderzeichen oder Leerzeichen enthält und nicht mit einem "-" Zeichen beginnt, dann darf man ausnahmsweise die Anführungsstriche weglassen.
Das ist im obigen Beispiel beim ItemType Parameter der Fall. Denn eigentlich ist Directory auch ein String.

PowerShell:
New-Item -Path Dummy -ItemType Directory
# ist das gleiche wie
New-Item -Path "Dummy" -ItemType "Directory"

#oder
$path = Dummy
$ItemType = "Directory"
New-Item -Path $path -ItemType $ItemType

Um es weiter "verwirrend" zu machen: Bei Zuweisungen an Variablen darf man die Anführungsstriche nicht weglassen:
PowerShell:
#Legal:
Write-Host Dummy
#Fehler:
$s=Dummy

Das hat damit zu tun, dass Powershell die rechte Seite der Zuweisung als Skript, Funktion oder CmdLet interpretiert. Und wenn es kein solches Element mit dem Namen "Dummy" gibt, dann wird ein Fehler geworfen.

ral9004 schrieb:
Wenn ich mit write-host die Variable ausgebe sehe ich, dass diese keine quotes hat.
Genau, siehe obige Erklärungen
 
  • Gefällt mir
Reaktionen: Art Vandelay, Ranayna und Der Lord
Hallo TomH22

Deine Antwort erst jetzt gesehen. Daher die verspätete Antwort.

A: Danke für die anschauliche und minuziöse Information

B: Wir haben unterschiedliche Ansichten zu Sprache und Logik
TomH22 schrieb:
Nein, nicht das Dateisystem hat ein "Problem" mit den Leerzeichen, sondern die Skriptsprache. Und auch die hat kein wirkliches Problem, dazu gleich mehr...
Wenn das Ziel ist, ein Verzeichnis mit dem Namen "Alle lieben Pizza" zu erstellen und die Eingabe lautet md alle lieben pizza dann wirst nicht das gewünschte Verzeichnis erhallten. Weil Du die Kommunikationsparameter des Dateisystems nicht berücksichtig hast. Somit ein Problem. Etwa so, wie wenn man beim Fast Food Laden korrekte, vollständige deutsche Sätze verwendet...

C: Jetzt geht es zur Sache...
TomH22 schrieb:
Dieser Code
macht in Wirklichkeit folgendes:
Er ruft das New-Item "CmdLet" auf, das hat mehrere Parameter, einer davon ist -Path. Ein Parameter ist im Prinzip das gleiche wie eine Variable. Dieser Variable wird der String Alle lieben Pizza zugewiesen, auch hier wieder ohne die Anführungsstriche.
Exakt. Das ist die Logik, die ich nicht nachvollziehen kann. Windows Powershell (5.x) stellt andere Kommunikationsregeln auf, abhängig ob der Befehl aus einem Skript (.ps1) oder direkt in der Shell abgesetzt wird.
Auf den Punkt gebracht:
Shell: ich will Anführungszeichen:
New-Item -Path "Alle lieben Pizza" -ItemType Directory -Force
Skript: ich will keine Anführungszeichen
New-Item -Path Alle lieben Pizza -ItemType Directory -Force

Beide Szenarien führen zu einem Verzeichnis mit dem Namen "Alle lieben Pizza".
Vergleichbar mit der CMD Shell und der For Schlaufe, wo die Anzahl der Prozentzeichen abhängig ist von Skript oder Shell.

Holger Schwichtenberg erklärt das in seinem Buch "PowerShell 7 und Windows PowerShell 5 – das Praxisbuch" im Kapitel 3 damit, dass die Win-PS zwei Modi kennt:
  • "Expression Mode"
  • "Command Mode"
Andere Quellen:
https://www.red-gate.com/simple-talk/wp-content/uploads/imported/2264-PS Quoting Wallchart_1_0_1.pdf
modus.png

Das - und nur das - nach heutigem Wissenstand erklärt das duale Verhalten der PS.

Letzte Nacht konnte ich nicht einschlafen und habe mich lesend müde gemacht.

OT:
"Passiv-aggresiv" ist "new Speach" im Sinne von Orwell. Analog "Entbindende" anstatt "Mutter" bzw "Zufußgehenden" anstatt Fussgäner. Ideologien und schlechte Laune - beides lässt der erzogene Mensch zu Hause bzw. im Privaten...

Beste Grüsse
 
ral9004 schrieb:
Wenn das Ziel ist, ein Verzeichnis mit dem Namen "Alle lieben Pizza" zu erstellen und die Eingabe lautet md alle lieben pizza dann wirst nicht das gewünschte Verzeichnis erhallten. Weil Du die Kommunikationsparameter des Dateisystems nicht berücksichtig hast. Somit ein Problem. Etwa so, wie wenn man beim Fast Food Laden korrekte, vollständige deutsche Sätze verwendet...
Das ist kein Problem des Dateisystems.
In deinem Beispiel ist es ein Problem von md.
Interessanterweise ist das Verhalten vom Windows md (welches ein alias fuer MKDIR ist) nicht in der Hilfe dokumentiert, aber md verkettet automatisch, und sieht die durch Leerzeichen getrennten Woerter als separate anzulegende Ordner an.
md Alle lieben Pizza legt drei Verzeichnisse an: "Alle", "lieben" und "Pizza".
Es sieht die drei getrennten Worte, und schickt drei separate Befehle an das Dateisystem.

Das md sich so verhaelt wurde vermutlich vor vielen Jahren so entschieden, und bei einem so grundlegenden Kommando wirft man nicht die Syntax ueber den Haufen weil jemand keine Anfuehrungszeichen verwenden will.

By the way ist dieses Verhalten einer der Gruende, warum ich es auch heute immer noch vermeide Leerzeichen in Pfaden und Dateinamen zu verwenden. Auch wenn es schon viele Jahre her ist das ich damit Probleme hatte, ich lasse das sein und verwende als optischen Trenner lieber einen Unterstrich.
 
  • Gefällt mir
Reaktionen: aragorn92, Der Lord und TomH22
ral9004 schrieb:
Das ist die Logik, die ich nicht nachvollziehen kann. Windows Powershell (5.x) stellt andere Kommunikationsregeln auf, abhängig ob der Befehl aus einem Skript (.ps1) oder direkt in der Shell abgesetzt wird.
Auf den Punkt gebracht:
Shell: ich will Anführungszeichen:
New-Item -Path "Alle lieben Pizza" -ItemType Directory -Force
Skript: ich will keine Anführungszeichen
New-Item -Path Alle lieben Pizza -ItemType Directory -Force

Beide Szenarien führen zu einem Verzeichnis mit dem Namen "Alle lieben Pizza".
Das Problem bei deinem Verständnis ist auch, dass du diese Behauptung ungeprüft aufgestellt hast. Sie stimmt nämlich nicht. Die Zeile New-Item -Path Alle lieben Pizza -ItemType Directory -Force in einem .ps1-Skript ist genau so ungültig wie bei direkter (händischer) Eingabe in der Shell. Ausgabe:
New-Item : Es wurde kein Positionsparameter gefunden, der das Argument "lieben" akzeptiert
Dahingegen funktionieren die beiden Zeilen
Code:
$s = "Alle lieben Pizza"
New-Item -Path $s -ItemType Directory -Force
sowohl in Skript als auch in Shell.

Dass @TomH22 bereits dargelegt hat, dass das Objekt $s keine Anführungszeichen enthält, ignorierst du einfach, ist aber das tatsächliche Verständnisproblem.
Du kannst deinen String auch ganz anders zusammenbauen, dann funktioniert das genauso.
Lege zum Beispiel einen Ordner namens AABAA an, hol dir seinen Namen ersetze dann das "B" durch ein Leerzeichen. Diesen String kannst du genauso weiterverwenden, ohne ihn von Anführungszeichen zu umschließen.

Das funktioniert in den meisten Programmiersprachen mit String-Literals so. Manchmal verwendet man Anführungszeichen zur Kennzeichnung, manchmal Apostrophen.

Eigentlich müsstest du dich ja fragen, warum New-Item -Path AlleliebenPizza -ItemType Directory -Force funktioniert, obwohl es nämlich eigentlich bzw. im Normalfall New-Item -Path "AlleliebenPizza" -ItemType Directory -Force heißen müsste.
 
  • Gefällt mir
Reaktionen: TomH22 und Der Lord
Zurück
Oben