web-dev-qa-db-fra.com

Comment utiliser join-path pour combiner plus de deux chaînes dans un chemin de fichier?

Si je veux combiner deux chaînes dans un chemin de fichier, j'utilise join-path comme ceci:

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

Cela imprime "C:\Program Files". Si je veux faire cela pour plus de deux chaînes cependant:

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

Powershell génère une erreur:

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

J'ai essayé d'utiliser un tableau de chaînes:

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

mais Powershell me demande d'entrer le childpath (puisque je n'ai pas spécifié l'argument -childpath), par exemple "somepath", puis crée trois chemins de fichiers, 

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

ce qui n'est pas juste non plus. 

76
Michael A

Vous pouvez utiliser la classe .NET Path:

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

Comme Join-Path peut utiliser sa valeur de chemin, vous pouvez diriger simultanément plusieurs instructions Join-Path:

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

Ce n'est pas aussi concis que vous le souhaiteriez probablement, mais c'est entièrement PowerShell et relativement facile à lire.

76
David Keaveny

Join-Path n'est pas exactement ce que vous recherchez. Il a des usages multiples mais pas celui que vous recherchez. Un exemple de Partying with 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

Vous voyez qu'il accepte un tableau de chaînes et concatène la chaîne enfant à chaque création de chemins complets. Dans votre exemple $path = join-path C: "Program Files" "Microsoft Office". Vous obtenez l'erreur puisque vous passez 3 arguments de position et que join-path n'en accepte que 2. Ce que vous recherchez est un -join et je pourrais voir cela comme un malentendu. Considérez plutôt ceci avec votre exemple:

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

-Join prend le tableau d'éléments et les concatène avec \ en une seule chaîne.

C:\Program Files\Microsoft Office

Tentative mineure de sauvetage

Oui, je conviens que cette réponse est meilleure, mais la mienne pourrait toujours fonctionner. Les commentaires suggèrent qu'il pourrait y avoir un problème avec les barres obliques. Par conséquent, pour rester dans mon approche de concaténation, vous pouvez le faire également.

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

Donc, s'il y a des problèmes avec des barres obliques supplémentaires, elles peuvent être traitées tant qu'elles ne sont pas au début de la chaîne (autorise les chemins UNC). [io.path]::combine('c:\', 'foo', '\bar\') ne fonctionnerait pas comme prévu et le mien en tiendrait compte. Les deux nécessitent des chaînes appropriées pour l'entrée car vous ne pouvez pas prendre en compte tous les scénarios. Considérez les deux approches, mais oui, l'autre réponse la mieux notée est plus concise et je ne savais même pas qu'elle existait. 

En outre, j'aimerais préciser que ma réponse explique comment le PO a mal agi en plus de suggérer de résoudre le problème fondamental. 

15
Matt

Si vous utilisez toujours .Net 2.0, [IO.Path]::Combine n'aura pas de surcharge params string[], vous devez joindre plus de deux parties et vous verrez l'erreur Impossible de trouver une surcharge pour "Combine" et le nombre d'arguments: "3" .

La solution Powershell légèrement moins élégante mais pure consiste à agréger manuellement les éléments de chemin:

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

ou

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

Voici quelque chose qui fera ce que vous voudriez en utilisant un tableau de chaînes pour ChildPath.

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

Quelles sorties 

C:\Program Files\Microsoft Office

Le seul inconvénient que j'ai trouvé est que la valeur initiale de $ path doit avoir une valeur (ne peut pas être nulle ou vide).

4
Mike Fair

Ou vous pouvez écrire votre propre fonction (ce que j'ai fini par faire).

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

Vous pouvez alors appeler la fonction comme ceci:

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

Cela présente l’avantage d’avoir exactement le même comportement que la fonction de chemin de jointure normale et de ne pas dépendre du .NET Framework.

1
Kevin

Depuis PowerShell 6.0, Join-Path a un nouveau paramètre appelé -AdditionalChildPath et peut combiner plusieurs parties d’un chemin d’origine. Soit en fournissant le paramètre supplémentaire, soit en fournissant simplement une liste d'éléments.

Exemple tiré de la documentation :

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

Donc, dans PowerShell 6.0 et supérieur, votre variante

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

fonctionne comme prévu!

1
Marcus Mangelsdorf

Vous pouvez l'utiliser de cette façon:

$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

Voici deux autres manières d'écrire une fonction pure PowerShell pour joindre un nombre arbitraire de composants dans un chemin.

Cette première fonction utilise un seul tableau pour stocker tous les composants, puis une boucle foreach pour les combiner:

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
}

Étant donné que les composants de chemin d'accès sont des éléments d'un tableau et qu'ils font tous partie d'un seul argument, ils doivent être séparés par des virgules. L'utilisation est la suivante:

 PS C: \> chemins d'accès 'C:', 'Program Files', 'Microsoft Office' 
 C:\Program Files\Microsoft Office 


Une méthode plus minimaliste pour écrire cette fonction consiste à utiliser la variable intégrée $ args, puis à réduire la boucle foreach en une seule ligne à l'aide de la méthode de Mike Fair.

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

Contrairement à la version précédente de la fonction, chaque composant de chemin est un argument séparé. Par conséquent, seul un espace est nécessaire pour séparer les arguments:

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

Que diriez-vous de l'approche suivante, elle est plus concise que les instructions de jointure de chemin d'accès Join:

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

$ p contient alors le chemin concaténé 'a\b\c\d'.

Edit: je viens de remarquer que c'est exactement la même approche que celle de Mike Fair, désolée.

0
Daniel