Come posso utilizzare Join-Path per combinare più di due stringhe in un percorso di file?


105

Se voglio combinare due stringhe in un percorso di file, lo uso in Join-Pathquesto modo:

$path = Join-Path C: "Program Files"
Write-Host $path

Quello stampa "C:\Program Files". Se voglio farlo per più di due stringhe però:

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

PowerShell genera un errore:

Join-Path: Impossibile trovare un parametro posizionale che accetta l'argomento "Microsoft Office".
In D: \ users \ ma \ my_script.ps1: 1 char: 18
+ $ path = join-path <<<< C: "Program Files" "Microsoft Office"
+ CategoryInfo: InvalidArgument: (:) [Join-Path] , ParameterBindingException
+ FullyQualifiedErrorId: PositionalParameterNotFound, Microsoft.PowerShell
.Commands.JoinPathCommand

Ho provato a utilizzare un array di stringhe:

[string[]] $pieces = "C:", "Program Files", "Microsoft Office"
$path = Join-Path $pieces
Write-Host $path

Ma PowerShell mi chiede di inserire il percorso figlio (poiché non ho specificato l' -childpathargomento), ad esempio "somepath", quindi crea tre percorsi di file,

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

che non è neanche giusto.

Risposte:


171

Puoi usare la classe .NET Path :

[IO.Path]::Combine('C:\', 'Foo', 'Bar')

3
Certamente la forma più concisa e gestisce correttamente i separatori di percorso e le barre finali / iniziali sui frammenti di percorso, cosa che la risposta attualmente accettata (concatenazione di stringhe di base) non fa.
David Keaveny

3
Per eseguire il comando di cui sopra nel mio PowerShell si ottiene questo errore -Non è possibile trovare un sovraccarico per "Combina" e il conteggio degli argomenti: "3". Alla riga: 1 carattere: 19 + [io.path] :: combina <<<< ('c: \', 'foo', 'bar') + CategoryInfo: NotSpecified: (:) [], MethodException + FullyQualifiedErrorId: MethodCountCouldNotFindBest
Aamol

@Aamol Quale versione di CLR stai utilizzando ( $PSVersionTable)? Funziona [io.path]::combine([string[]]('c:\','foo','bar'))?
Marek Toman,

1
Sembra che il limite del parametro sia 3, dopo 3 il primo parametro viene ignorato. (almeno qui, ps 5.1, clr 4.0)
ehiller

4
@DavidKeaveny "gestisce correttamente i separatori di percorso e le barre finali / iniziali sui frammenti di percorso" - Non proprio. join-pathfa quello che ti aspetti, join-path "C:\" "\foo"restituisce C:\foo, Path.Combinetuttavia , ignora il primo argomento ogni volta che il secondo argomento contiene un separatore iniziale: [io.path]::combine('c:\', '\foo')produce fastidiosamente \foo.
Quantic

99

Poiché Join-Path può essere reindirizzato a un valore di percorso, è possibile reindirizzare insieme più istruzioni Join-Path:

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

Non è così conciso come probabilmente vorresti che fosse, ma è completamente PowerShell ed è relativamente facile da leggere.


3
+1 poiché funzionerà su tutti i powershell 2,3,4, il problema con l'API [io.path] :: Combine è diverso per .net framework 3,4
Ram

18

A partire da PowerShell 6.0, Join-Path ha un nuovo parametro chiamato -AdditionalChildPathe può combinare più parti di un percorso out-of-the-box . O fornendo il parametro extra o semplicemente fornendo un elenco di elementi.

Esempio dalla documentazione :

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

Quindi in PowerShell 6.0 e versioni successive la tua variante

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

funziona come previsto!


17

Join-Path non è esattamente quello che stai cercando. Ha molteplici usi ma non quello che stai cercando. Un esempio da Partying con 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

Si vede che accetta un array di stringhe e concatena la stringa figlia a ciascuna creando percorsi completi. Nel tuo esempio $path = join-path C: "Program Files" "Microsoft Office",. Stai ricevendo l'errore poiché stai passando tre argomenti posizionali e join-pathne accetti solo due. Quello che stai cercando è un -join, e potrei vedere che questo è un malinteso. Considera invece questo con il tuo esempio:

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

-Joinprende l'array di elementi e li concatena con \in una singola stringa.

C:\Program Files\Microsoft Office

Piccolo tentativo di salvataggio

Sì, sono d'accordo che questa risposta è migliore, ma la mia potrebbe ancora funzionare. I commenti suggeriscono che potrebbe esserci un problema con le barre, quindi per mantenere il mio approccio di concatenazione potresti farlo anche tu.

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

Quindi, se ci sono problemi con barre extra, potrebbe essere gestito fintanto che non sono all'inizio della stringa (consente percorsi UNC ). [io.path]::combine('c:\', 'foo', '\bar\')non funzionerebbe come previsto e il mio lo spiegherebbe. Entrambi richiedono stringhe appropriate per l'input poiché non è possibile tenere conto di tutti gli scenari. Considera entrambi gli approcci, ma, sì, l'altra risposta più votata è più concisa e non sapevo nemmeno che esistesse.

Inoltre, vorrei sottolineare, la mia risposta spiega come ciò che ha fatto l'OP era sbagliato oltre a fornire un suggerimento per affrontare il problema principale.


2
Questo è sbagliato perché anche se più percorsi \ in consecutivi funzioneranno, è brutto e può potenzialmente causare problemi.
Mikhail Orlov

@MikhailOrlov Puoi descrivere un potenziale problema come se suggerisse solo che potrebbe accadere? Hai un altro suggerimento? Lo chiedo perché non vedo alcun problema. Se qualcosa non va, vorrei affrontarlo.
Matt

2
Recentemente ho gestito un sacco di codice di bassa qualità, le persone confrontano i percorsi con String.Equals e analizzano i percorsi con String.Split ('\\') senza rimuovere le stringhe vuote. Non riesco a pensare a niente di più pericoloso nelle conseguenze, per lo più sono solo paranoico. Grazie per la tua modifica.
Mikhail Orlov

3
L'inclusione esplicita del separatore di percorso può causare problemi con la portabilità multipiattaforma. Sebbene PowerShell attualmente funzioni solo su Windows, è probabile che cambierà in un futuro non troppo lontano ed è una buona idea sviluppare buone abitudini il prima possibile. Senza contare che queste abitudini possono essere trasferite ad altre lingue.
bshacklett

10

Se stai ancora utilizzando .NET 2.0, [IO.Path]::Combinenon avrai il params string[]sovraccarico che devi unire più di due parti e vedrai l'errore Impossibile trovare un sovraccarico per "Combina" e il conteggio degli argomenti: "3".

Un po 'meno elegante, ma una pura soluzione PowerShell consiste nell'aggregazione manuale delle parti del percorso:

Join-Path C: (Join-Path  "Program Files" "Microsoft Office")

o

Join-Path  (Join-Path  C: "Program Files") "Microsoft Office"

5

Ecco qualcosa che farà quello che vorresti quando usi un array di stringhe per ChildPath.

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

Quali uscite

C:\Program Files\Microsoft Office

L'unico avvertimento che ho trovato è che il valore iniziale di $ path deve avere un valore (non può essere nullo o vuoto).


4

Ecco altri due modi per scrivere una funzione PowerShell pura per unire un numero arbitrario di componenti in un percorso.

Questa prima funzione utilizza un singolo array per memorizzare tutti i componenti e quindi un ciclo foreach per combinarli:

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
}

Poiché i componenti del percorso sono elementi in una matrice e fanno tutti parte di un singolo argomento, devono essere separati da virgole. L'utilizzo è il seguente:

PS C: \> Join-Paths "C:", "Programmi", "Microsoft Office"
C: \ Programmi \ Microsoft Office


Un modo più minimalista per scrivere questa funzione è usare la $argsvariabile incorporata e quindi comprimere il ciclo foreach in una singola riga usando il metodo di Mike Fair.

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

A differenza della versione precedente della funzione, ogni componente del percorso è un argomento separato, quindi è necessario solo uno spazio per separare gli argomenti:

PS C: \> Join-Paths2 'C:' 'Programmi' 'Microsoft Office'
C: \ Programmi \ Microsoft Office

2

Il seguente approccio è più conciso rispetto al piping delle istruzioni Join-Path:

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

$ p contiene quindi il percorso concatenato 'a \ b \ c \ d'.

(Ho appena notato che questo è esattamente lo stesso approccio di Mike Fair, mi dispiace.)


1

Oppure potresti scrivere la tua funzione (che è quello che ho finito per fare).

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

È quindi possibile chiamare la funzione in questo modo:

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

Questo ha il vantaggio di avere lo stesso identico comportamento della normale funzione Join-Path e non dipende da .NET Framework.


0

Puoi usarlo in questo modo:

$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"
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.