wake-up-neo.net

Wie verwende ich join-path, um mehr als zwei Zeichenfolgen in einem Dateipfad zu kombinieren?

Wenn ich zwei Zeichenfolgen in einem Dateipfad kombinieren möchte, verwende ich join-path wie folgt:

$path = join-path C: "Program Files"
write-Host $path

Das druckt "C:\Program Files". Wenn ich dies jedoch für mehr als zwei Zeichenfolgen tun möchte:

$path = join-path C: "Program Files" "Microsoft Office"
write-Host $path

Powershell wirft einen Fehler:

Join-Path : A positional parameter cannot be found that accepts argument 'Micro
soft Office'.
At D:\users\ma\my_script.ps1:1 char:18
+ $path = join-path <<<<  C: "Program Files" "Microsoft Office"
    + CategoryInfo          : InvalidArgument: (:) [Join-Path], ParameterBindi
   ngException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell
   .Commands.JoinPathCommand

Ich habe versucht, ein String-Array zu verwenden:

[string[]] $pieces = "C:", "Program Files", "Microsoft Office"
$path = join-path $pieces
write-Host $path

powershell fordert mich jedoch auf, den untergeordneten Pfad einzugeben (da ich das Argument -childpath nicht angegeben habe), z. "somepath" und erstellt dann drei Dateipfade, 

C:\somepath
Program Files\somepath
Microsoft Office\somepath

was auch nicht richtig ist. 

76
Michael A

Sie können die .NET Path-Klasse verwenden:

[io.path]::combine('c:\', 'foo', 'bar')
128
mtman

Da Join-Path seinen Pfadwert weiterleiten kann, können Sie mehrere Join-Path-Anweisungen zusammen leiten:

Join-Path "C:" -ChildPath "Windows" | Join-Path -ChildPath "system32" | Join-Path -ChildPath "drivers"

Es ist nicht so knapp, wie Sie es sich vielleicht wünschen, aber es ist voll PowerShell und relativ einfach zu lesen

76
David Keaveny

Join-Path ist nicht genau das, wonach Sie suchen. Es hat mehrere Anwendungen, aber nicht die, nach der Sie suchen. Ein Beispiel aus Feiern mit Join-Path

Join-Path C:\hello,d:\goodbye,e:\hola,f:\adios world
C:\hello\world
d:\goodbye\world
e:\hola\world
f:\adios\world

Sie sehen, dass es ein Array von Zeichenfolgen akzeptiert und die untergeordnete Zeichenfolge mit jedem einzelnen Pfad verkettet. In Ihrem Beispiel $path = join-path C: "Program Files" "Microsoft Office". Sie erhalten den Fehler, da Sie 3 Positionsargumente übergeben und join-path nur 2 akzeptiert. Was Sie suchen, ist ein -join, und ich könnte feststellen, dass dies ein Missverständnis ist. Betrachten Sie dies stattdessen mit Ihrem Beispiel:

"C:","Program Files","Microsoft Office" -join "\"

-Join nimmt das Array von Elementen und verknüpft sie mit \ in einer einzigen Zeichenfolge.

C:\Program Files\Microsoft Office

Kleiner Versuch einer Bergung

Ja, ich stimme zu, dass diese Antwort besser ist, aber meine könnte noch funktionieren. Kommentare deuten darauf hin, dass es ein Problem mit Schrägstrichen geben könnte, sodass Sie dies auch tun können, wenn Sie meinen Verkettungsansatz einhalten.

"C:","\\Program Files\","Microsoft Office\" -join "\" -replace "(?!^\\)\\{2,}","\"

Wenn es also Probleme mit zusätzlichen Schrägstrichen gibt, kann es behandelt werden, solange sie nicht am Anfang der Zeichenfolge stehen (erlaubt UNC-Pfade). [io.path]::combine('c:\', 'foo', '\bar\') würde nicht wie erwartet funktionieren und meine würde das erklären. Beide erfordern korrekte Zeichenfolgen für die Eingabe, da Sie nicht alle Szenarien berücksichtigen können. Betrachten Sie beide Ansätze, aber die andere höher bewertete Antwort ist knapper und ich wusste gar nicht, dass es sie gab. 

Außerdem möchte ich darauf hinweisen. In meiner Antwort wird erläutert, dass das OP falsch war und nicht nur einen Vorschlag zur Lösung des Kernproblems gemacht hat. 

15
Matt

Wenn Sie immer noch .Net 2.0 verwenden, wird [IO.Path]::Combine keine params string[]-Überladung haben, die Sie benötigen, um mehr als zwei Teile zu verbinden. Der Fehler kann keine Überladung für "Kombinieren" und das Argument count: "3" finden. .

Etwas weniger elegant, aber reine Powershell-Lösung besteht darin, Wegteile manuell zu aggregieren:

join-path C: (join-path "Program Files" "Microsoft Office")

oder

join-path (join-path C: "Program Files") "Microsoft Office"
7

Hier ist etwas, was Sie tun können, wenn Sie ein String-Array für ChildPath verwenden.

$path = "C:"
@( "Program Files", "Microsoft Office" ) | %{ $path = Join-Path $path $_ }
Write-Host $path

Welche Ausgänge 

C:\Program Files\Microsoft Office

Der einzige Nachteil, den ich gefunden habe, ist, dass der Anfangswert für $ path einen Wert haben muss (kann nicht null oder leer sein).

4
Mike Fair

Oder Sie könnten Ihre eigene Funktion dafür schreiben (was ich am Ende getan habe).

function Join-Path-Recursively($PathParts) {
    $NumberOfPathParts = $PathParts.Length;

    if ($NumberOfPathParts -eq 0) {
        return $null
    } elseif ($NumberOfPathParts -eq 1) {
        return $PathParts[0]
    } else {
        return Join-Path -Path $PathParts[0] -ChildPath $(Join-Path-Recursively -PathParts $PathParts[1..($NumberOfPathParts-1)])
    }
}

Sie können die Funktion dann wie folgt aufrufen:

Join-Path-Recursively -PathParts  @("C:", "Program Files", "Microsoft Office")
Join-Path-Recursively  @("C:", "Program Files", "Microsoft Office")

Dies hat den Vorteil, dass es genau das gleiche Verhalten wie die normale Join-Path-Funktion aufweist und nicht von .NET Framework abhängig ist.

1
Kevin

Seit PowerShell 6.0 verfügt Join-Path über einen neuen Parameter mit dem Namen -AdditionalChildPath und kann mehrere Teile eines Pfads out-of-the-box kombinieren. Entweder indem Sie den zusätzlichen Parameter angeben oder einfach eine Liste von Elementen angeben.

Beispiel aus der Dokumentation :

Join-Path a b c d e f g
a\b\c\d\e\f\g

Also in PowerShell 6.0 und darüber Ihre Variante

$path = Join-Path C: "Program Files" "Microsoft Office"

funktioniert wie erwartet!

1

Sie können es so verwenden:

$root = 'C:'
$folder1 = 'Program Files (x86)'
$folder2 = 'Microsoft.NET'

if (-Not(Test-Path $(Join-Path $root -ChildPath $folder1 | Join-Path -ChildPath $folder2)))
{
   "Folder does not exist"
}
else 
{
   "Folder exist"
}
0
Francesco

Es gibt zwei weitere Möglichkeiten, eine reine Powershell-Funktion zu schreiben, um eine beliebige Anzahl von Komponenten in einen Pfad einzufügen.

Diese erste Funktion verwendet ein einziges Array, um alle Komponenten zu speichern, und dann eine Foreach-Schleife, um sie zu kombinieren:

function Join-Paths {
    Param(
        [Parameter(mandatory)]
        [String[]]
        $Paths
    )
    $output = $Paths[0]
    foreach($path in $Paths[1..$Paths.Count]) {
        $output = Join-Path $output -ChildPath $path
    }
    $output
}

Da die Pfadkomponenten Elemente in einem Array sind und Teil eines einzelnen Arguments sind, müssen sie durch Kommas getrennt werden. Die Verwendung ist wie folgt:

 PS C: \> Join-Pfade 'C:', 'Programme', 'Microsoft Office' 
 C:\Programme\Microsoft Office 


Eine minimalistischere Methode zum Schreiben dieser Funktion besteht darin, die eingebaute Variable $ args zu verwenden und dann die foreach-Schleife mit der Methode von Mike Fair in eine einzige Zeile zu reduzieren.

function Join-Paths2 {
    $path = $args[0]
    $args[1..$args.Count] | %{ $path = Join-Path $path $_ }
    $path
}

Im Gegensatz zur vorherigen Version der Funktion ist jede Pfadkomponente ein separates Argument. Daher ist nur ein Leerzeichen erforderlich, um die Argumente zu trennen:

 PS C: \> Join-Paths2 'C:' 'Programme' 'Microsoft Office' 
 C:\Programme\Microsoft Office 
0
Jon

Wie wäre es mit dem folgenden Ansatz, der knapper ist, als Join-Path-Anweisungen zu übergeben:

$p="a"; "b","c","d" | Foreach-Object -Process { $p = Join-Path $p $_ }

$ p enthält dann den verketteten Pfad 'a\b\c\d'.

Edit: Ich habe gerade bemerkt, dass dies genau der gleiche Ansatz wie bei Mike Fair ist, sorry.

0
Daniel