如何使用Join-Path将两个以上的字符串组合到文件路径中?


105

如果我想将两个字符串合并到一个文件路径中,请使用Join-Path以下命令:

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

那打印"C:\Program Files"。如果我想对两个以上的字符串执行此操作:

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

PowerShell引发错误:

Join-Path:找不到接受参数'Microsoft Office'的位置参数。
在D:\ users \ ma \ my_script.ps1:1 char:18
+ $ path = join-path <<<< C:“程序文件”“ Microsoft Office”
+ CategoryInfo:InvalidArgument:(:) [Join-Path] ,ParameterBindingException
+ FullyQualifiedErrorId:PositionalParameterNotFound,Microsoft.PowerShell
.Commands.JoinPathCommand

我尝试使用字符串数组:

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

但是PowerShell提示我输入子路径(因为我未指定-childpath参数),例如“ somepath”,然后创建了三个文件路径,

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

这也不对。

Answers:


171

您可以使用.NET Path类:

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

3
当然,这是最简洁的形式,可以正确处理路径片段的路径分隔符和尾部/前导斜线,而当前接受的答案(基本字符串串联)则不这样做。
David Keaveny,2015年

3
为了在我的Powershell中执行上述命令,会出现此错误-找不到“ Combine”的重载和参数计数:“ 3”。在第1行:char:19 + [io.path] :: combine <<<<('c:\','foo','bar')+ CategoryInfo:未指定:(:) [],MethodException + FullyQualifiedErrorId: MethodCountCouldNotFindBest
Aamol

@Aamol您正在运行哪个CLR版本($PSVersionTable)?有[io.path]::combine([string[]]('c:\','foo','bar'))工作吗?
Marek Toman

1
似乎参数限制为3,在3之后,第一个参数将被忽略。(至少在这里是ps 5.1,clr 4.0)
ehiller

4
@DavidKeaveny“正确处理路径分隔符以及路径片段上的尾随/前导斜杠” –并非如此。join-path达到您的期望,join-path "C:\" "\foo"输出C:\fooPath.Combine但是每当第二个参数包含前导分隔符时,都会忽略第一个参数:[io.path]::combine('c:\', '\foo')烦人的output \foo
量子

99

由于可以通过Join-Path传递路径值,因此可以将多个Join-Path语句传递到管道:

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

它不像您希望的那样简洁,但是它完全是PowerShell,并且相对容易阅读。


3
+1,因为它可以在所有Powershell 2,3,4上正常工作,因此[io.path] :: Combine API的问题与.net Framework 3,4有所不同
Ram

18

从PowerShell 6.0开始,Join-Path具有一个名为的新参数-AdditionalChildPath,可以直接组合路径的多个部分。通过提供额外的参数或仅提供元素列表即可。

文档中的示例:

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

因此在PowerShell 6.0及更高版本中

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

正常工作!


17

Join-Path并不是您要找的东西。它有多种用途,但不是您要找的用途。来自使用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

您会看到它接受字符串数组,并将子字符串连接到每个创建完整路径的子字符串。在您的示例中,$path = join-path C: "Program Files" "Microsoft Office"。由于传递了三个位置参数并且join-path仅接受两个位置参数,因此出现了错误。您正在寻找的是-join,我可能认为这是一种误解。相反,请考虑以下示例:

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

-Join获取项目数组并将其与\单个字符串连接。

C:\Program Files\Microsoft Office

小尝试打捞

是的,我同意这个答案更好,但是我的仍然可以工作。注释表明斜线可能存在问题,因此与我的串联方法保持一致,您也可以这样做。

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

因此,如果存在多余的斜杠问题,只要它们不在字符串的开头(允许UNC路径),就可以进行处理。[io.path]::combine('c:\', 'foo', '\bar\')将无法按预期工作,而我会对此做出解释。两者都要求输入正确的字符串,因为您无法说明所有情况。考虑两种方法,但是,是的,另一个更高评价的答案更简洁,我什至不知道它的存在。

另外,我想指出的是,我的答案解释了OP在提供解决核心问题的建议的基础上是怎么做的。


2
这是错误的,因为即使路径中的多个连续\都可以使用,但它却很丑陋,并且可能会引起问题。
Mikhail Orlov'3

@MikhailOrlov能否将潜在问题描述为仅暗示可能会发生?您还有其他建议吗?我问是因为我没有发现问题。如果有什么问题,我想解决。
马特

2
我最近一直在处理许多低质量的代码,人们比较String.Equals的路径并使用String.Split('\\')解析路径,而不会删除空字符串。我想不出有什么危害性更大的后果,主要是我只是偏执。感谢您的修改。
Mikhail Orlov

3
明确包含路径分隔符可能会导致跨平台可移植性问题。虽然PowerShell当前仅在Windows上运行,但在不久的将来有可能会改变,并且最好早日养成良好的习惯。更不用说这些习惯可以转移到其他语言。
bshacklett

10

如果您仍在使用.NET 2.0,[IO.Path]::Combine则将没有params string[]需要连接两个以上部分的重载,并且您将看到错误“找不到Combine”的重载和参数计数:“ 3”。

稍微不太优雅,但是纯PowerShell解决方案是手动聚合路径部分:

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

要么

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

5

当为ChildPath使用字符串数组时,这可以满足您的需求。

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

哪个输出

C:\Program Files\Microsoft Office

我发现的唯一警告是$ path的初始值必须有一个值(不能为null或为空)。


4

这是另外两种编写纯PowerShell函数以将任意数量的组件连接到路径中的方法。

第一个函数使用单个数组存储所有组件,然后使用foreach循环将它们组合起来:

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
}

因为路径组件是数组中的元素,并且是单个参数的所有部分,所以它们必须用逗号分隔。用法如下:

PS C:\>联接路径“ C:”,“程序文件”,“ Microsoft Office”
C:\ Program Files \ Microsoft Office


编写此函数的一种更简单的方法是使用内置$args变量,然后使用Mike Fair的方法将foreach循环折叠为一行。

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

与以前的函数版本不同,每个路径组件都是一个单独的参数,因此仅需一个空格即可分隔参数:

PS C:\> Join-Paths2'C:''程序文件''Microsoft Office'
C:\ Program Files \ Microsoft Office

2

与传递Join-Path语句相比,以下方法更加简洁:

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

然后$ p保留连接的路径'a \ b \ c \ d'。

(对不起,我刚刚注意到这与Mike Fair的方法完全一样。)


1

或者,您可以为此编写自己的函数(这就是我最终要做的)。

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

然后,您可以像下面这样调用函数:

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

这样做的好处是,其行为与普通的Join-Path函数完全相同,并且不依赖于.NET Framework。


0

您可以通过以下方式使用它:

$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"
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.