ChatGPT解决这个技术问题 Extra ChatGPT

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

如果我想将两个字符串组合成一个文件路径,我可以像这样使用 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 抛出错误:

加入路径:找不到接受参数“Microsoft Office”的位置参数。在 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

我尝试使用字符串数组:

[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

这也不对。

请注意,since PowerShell 6 您现在的直观第一次尝试works as expected 并且 正确处理部分路径中的尾随/前导路径分隔符 🎉

P
Peter Mortensen

您可以使用 .NET Path 类:

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

当然是最简洁的形式,并正确处理路径片段上的路径分隔符和尾随/前导斜杠,当前接受的答案(基本字符串连接)没有这样做。
为了在我的 powershell 中执行上述命令,遇到此错误 - 找不到“Combine”的重载和参数计数:“3”。在 line:1 char:19 + [io.path]::combine <<<< ('c:\', 'foo', 'bar') + CategoryInfo : NotSpecified: (:) [], MethodException + FullyQualifiedErrorId : MethodCountCouldNotFindBest
@Aamol 您正在运行什么 CLR 版本($PSVersionTable)? [io.path]::combine([string[]]('c:\','foo','bar')) 有效吗?
似乎参数限制是 3,在 3 之后第一个参数被忽略。 (至少在这里,ps 5.1,clr 4.0)
@DavidKeaveny“正确处理路径片段上的路径分隔符和尾随/前导斜杠”——并非如此。 join-path 符合您的预期,join-path "C:\" "\foo" 输出 C:\foo,但 Path.Combine 忽略第一个参数,只要第二个参数包含前导分隔符:[io.path]::combine('c:\', '\foo') 烦人地输出 \foo
P
Peter Mortensen

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

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

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


+1,因为它适用于所有 powershell 2,3,4 ,[io.path]::Combine API 的问题在于 .net 框架 3,4 的不同
M
Marcus Mangelsdorf

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

the documentation 中的示例:

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"

按预期工作!


M
Matt

Join-Path 并不是您正在寻找的。它有多种用途,但不是您要寻找的用途。 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

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

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

-Join 获取项目数组并将它们与 \ 连接成一个字符串。

C:\Program Files\Microsoft Office

打捞的小尝试

是的,我同意 this answer 更好,但我的仍然可以工作。评论表明斜杠可能存在问题,因此为了保持我的串联方法,您也可以这样做。

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

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

另外,想指出的是,我的回答除了提供解决核心问题的建议外,还解释了 OP 做错了什么。


这是错误的,因为即使路径中的多个连续 \ 可以工作,它也很丑陋并且可能会导致问题。
@MikhailOrlov 你能描述一个潜在的问题,只是暗示它可能会发生吗?你还有什么建议吗?我在问,因为我没有看到问题。如果有什么问题,我想解决它。
我最近一直在处理许多低质量的代码,人们通过 String.Equals 比较路径并使用 String.Split('\\') 解析路径而不删除空字符串。我想不出任何更危险的后果,主要是我只是偏执。感谢您的编辑。
显式包含路径分隔符可能会导致跨平台可移植性问题。虽然 PowerShell 目前仅在 Windows 上运行,但在不久的将来可能会发生变化,尽早养成良好习惯是个好主意。更不用说这些习惯可以转移到其他语言。
您的派对链接已损坏。这是更新后的链接:devblogs.microsoft.com/powershell/partying-with-join-path
P
Peter Mortensen

如果您仍在使用 .NET 2.0,则 [IO.Path]::Combine 不会有 params string[] 重载,您需要加入两个以上的部分,并且您会看到错误 Cannot find an overload for "Combine"和参数计数:“3”。

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

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

或者

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

P
Peter Mortensen

这里有另外两种编写纯 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:\> Join-Paths 'C:', 'Program Files', '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:' 'Program Files' 'Microsoft Office'
C:\Program Files\Microsoft Office

M
Mike Fair

当为 ChildPath 使用字符串数组时,这里有一些东西可以满足您的需求。

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

哪个输出

C:\Program Files\Microsoft Office

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


P
Peter Mortensen

以下方法比管道 Join-Path 语句更简洁:

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

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

(我刚刚注意到这与 Mike Fair 的方法完全相同,抱歉。)


F
Francesco

你可以这样使用它:

$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"
}

K
Kevin

或者你可以为它编写你自己的函数(这就是我最终做的)。

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。


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅