Làm cách nào để sử dụng Join-Path để kết hợp nhiều hơn hai chuỗi vào một đường dẫn tệp?


105

Nếu tôi muốn kết hợp hai chuỗi thành một đường dẫn tệp, tôi sử dụng Join-Pathnhư sau:

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

Bản in đó "C:\Program Files". Nếu tôi muốn thực hiện việc này với nhiều hơn hai chuỗi:

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

PowerShell thông báo lỗi:

Tham số đường dẫn: Không thể tìm thấy tham số vị trí chấp nhận đối số 'Microsoft Office'.
Tại D: \ users \ ma \ my_script.ps1: 1 char: 18
+ $ path = join-path <<<< C: "Program Files" "Microsoft Office"
+ CategoryInfo: InvalidArgument: (:) [Tham gia đường dẫn] , ParameterBindingException
+ CompleteQualifiedErrorId: PositionalParameterNotFound, Microsoft.PowerShell
.Commands.JoinPathCommand

Tôi đã thử sử dụng một mảng chuỗi:

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

Nhưng PowerShell nhắc tôi nhập đường dẫn con (vì tôi không chỉ định -childpathđối số), ví dụ: "somepath", rồi tạo ba đường dẫn tệp,

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

mà cũng không đúng.

Câu trả lời:


171

Bạn có thể sử dụng lớp .NET Path :

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

3
Chắc chắn là biểu mẫu ngắn gọn nhất và xử lý các dấu phân tách đường dẫn và dấu gạch chéo ở cuối / đầu trên các đoạn đường dẫn một cách chính xác, điều mà câu trả lời được chấp nhận hiện tại (nối chuỗi cơ bản) không làm được.
David Keaveny

3
Để thực hiện lệnh trên trong powershell của tôi, tôi gặp lỗi này -Không thể tìm thấy quá tải cho "Kết hợp" và số đối số: "3". Tại dòng: 1 char: 19 + [io.path] :: kết hợp <<<< ('c: \', 'foo', 'bar') + CategoryInfo: NotSpecified: (:) [], MethodException + CompleteQualifiedErrorId: MethodCountCouldNotFindBest
Aamol

@Aamol Bạn đang chạy phiên bản CLR nào ( $PSVersionTable)? Có [io.path]::combine([string[]]('c:\','foo','bar'))hiệu quả không?
Marek Toman

1
Có vẻ như giới hạn tham số là 3, sau 3 tham số đầu tiên bị bỏ qua. (ít nhất ở đây, ps 5.1, clr 4.0)
ehiller

4
@DavidKeaveny "xử lý các dấu phân cách đường dẫn và dấu gạch chéo ở cuối / đầu trên các đoạn đường dẫn một cách chính xác" - Không hẳn vậy. join-paththực hiện những gì bạn mong đợi, join-path "C:\" "\foo"đầu ra C:\foo, Path.Combinetuy nhiên , bỏ qua đối số đầu tiên bất cứ khi nào đối số thứ hai chứa dấu phân tách hàng đầu: [io.path]::combine('c:\', '\foo')đầu ra gây khó chịu \foo.
Quantic

99

Vì Tham gia đường dẫn có thể được tạo một giá trị đường dẫn, bạn có thể kết hợp nhiều câu lệnh Tham gia đường dẫn với nhau:

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

Nó không ngắn gọn như bạn có thể muốn, nhưng nó hoàn toàn là PowerShell và tương đối dễ đọc.


3
+1 vì nó sẽ hoạt động trên tất cả các powershell 2,3,4, vấn đề với [io.path] :: API kết hợp là khác nhau đối với .net framework 3,4
Ram

18

Kể từ PowerShell 6.0, Join-Path có một tham số mới được gọi -AdditionalChildPathvà có thể kết hợp nhiều phần của một đường dẫn out-of-box . Bằng cách cung cấp tham số bổ sung hoặc chỉ cung cấp danh sách các phần tử.

Ví dụ từ tài liệu :

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

Vì vậy, trong PowerShell 6.0 trở lên, biến thể của bạn

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

hoạt động như mong đợi!


17

Tham gia đường dẫn không phải là chính xác những gì bạn đang tìm kiếm. Nó có nhiều công dụng nhưng không phải là công dụng bạn đang tìm kiếm. Một ví dụ từ Tiệc tùng với Tham gia :

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

Bạn thấy rằng nó chấp nhận một mảng các chuỗi và nó nối chuỗi con với mỗi việc tạo đường dẫn đầy đủ. Trong ví dụ của bạn $path = join-path C: "Program Files" "Microsoft Office",. Bạn đang gặp lỗi vì bạn đang chuyển ba đối số vị trí và join-pathchỉ chấp nhận hai. Những gì bạn đang tìm kiếm là một -join, và tôi có thể thấy đây là một sự hiểu lầm. Thay vào đó, hãy xem xét điều này với ví dụ của bạn:

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

-Joinlấy mảng các mục và nối chúng \thành một chuỗi duy nhất.

C:\Program Files\Microsoft Office

Nỗ lực nhỏ trong việc cứu hộ

Có, tôi sẽ đồng ý rằng câu trả lời này tốt hơn, nhưng câu trả lời của tôi vẫn có thể hoạt động. Các nhận xét cho thấy có thể có vấn đề với dấu gạch chéo, vì vậy, để tuân theo phương pháp nối của tôi, bạn cũng có thể làm điều này.

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

Vì vậy, nếu có vấn đề với dấu gạch chéo bổ sung, nó có thể được xử lý miễn là chúng không nằm ở đầu chuỗi (cho phép đường dẫn UNC ). [io.path]::combine('c:\', 'foo', '\bar\')sẽ không hoạt động như mong đợi và tôi sẽ tính đến điều đó. Cả hai đều yêu cầu các chuỗi thích hợp cho đầu vào vì bạn không thể tính cho tất cả các trường hợp. Hãy xem xét cả hai cách tiếp cận, nhưng, có, câu trả lời khác được đánh giá cao hơn ngắn gọn hơn và tôi thậm chí không biết nó tồn tại.

Ngoài ra, tôi muốn chỉ ra rằng, câu trả lời của tôi giải thích việc OP đã làm sai như thế nào bên cạnh việc cung cấp một gợi ý để giải quyết vấn đề cốt lõi.


2
Điều này là sai vì mặc dù nhiều đường dẫn \ in liên tiếp sẽ hoạt động, nó rất xấu và có thể gây ra sự cố.
Mikhail Orlov

@MikhailOrlov Bạn có thể mô tả một vấn đề tiềm ẩn như được cho là chỉ gợi ý rằng nó có thể xảy ra không? Bạn có gợi ý khác không? Tôi đang hỏi vì tôi không thấy có vấn đề gì. Nếu có điều gì sai sót, tôi muốn giải quyết.
Matt

2
Gần đây, tôi đã xử lý rất nhiều mã chất lượng thấp, mọi người so sánh các đường dẫn bằng String.Equals và phân tích cú pháp đường dẫn bằng String.Split ('\\') mà không xóa các chuỗi trống. Tôi không thể nghĩ đến bất cứ điều gì nguy hiểm hơn trong hậu quả, chủ yếu là tôi bị hoang tưởng. Cảm ơn bạn đã chỉnh sửa.
Mikhail Orlov

3
Việc bao gồm dấu phân cách đường dẫn một cách rõ ràng có thể gây ra sự cố với tính di động đa nền tảng. Mặc dù PowerShell hiện chỉ chạy trên Windows, nhưng điều đó có thể sẽ thay đổi trong tương lai không xa, và bạn nên phát triển những thói quen tốt càng sớm càng tốt. Chưa kể rằng những thói quen này có thể chuyển sang các ngôn ngữ khác.
bshacklett,

10

Nếu bạn vẫn đang sử dụng .NET 2.0, thì [IO.Path]::Combinesẽ không có params string[]quá tải mà bạn cần phải nối nhiều hơn hai phần và bạn sẽ thấy lỗi Không thể tìm thấy quá tải cho "Kết hợp" và số đối số: "3".

Hơi kém thanh lịch nhưng giải pháp PowerShell thuần túy là tổng hợp các phần đường dẫn theo cách thủ công:

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

hoặc là

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

5

Đây là một cái gì đó sẽ làm những gì bạn muốn khi sử dụng một mảng chuỗi cho ChildPath.

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

Đầu ra nào

C:\Program Files\Microsoft Office

Cảnh báo duy nhất tôi tìm thấy là giá trị ban đầu cho $ path phải có một giá trị (không được rỗng hoặc trống).


4

Đây là hai cách khác để viết một hàm PowerShell thuần túy để nối một số thành phần tùy ý vào một đường dẫn.

Hàm đầu tiên này sử dụng một mảng duy nhất để lưu trữ tất cả các thành phần và sau đó là một vòng lặp foreach để kết hợp chúng:

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
}

Vì các thành phần đường dẫn là các phần tử trong một mảng và tất cả là một phần của một đối số, chúng phải được phân tách bằng dấu phẩy. Cách sử dụng như sau:

PS C: \> Tham gia Đường dẫn 'C:', 'Tệp Chương trình', 'Microsoft Office'
C: \ Program Files \ Microsoft Office


Một cách đơn giản hơn để viết hàm này là sử dụng $argsbiến có sẵn, sau đó thu gọn vòng lặp foreach thành một dòng duy nhất bằng phương pháp của Mike Fair.

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

Không giống như phiên bản trước của hàm, mỗi thành phần đường dẫn là một đối số riêng biệt, vì vậy chỉ cần một khoảng trắng để phân tách các đối số:

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

2

Cách tiếp cận sau đây ngắn gọn hơn câu lệnh Tham gia đường ống:

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

$ p sau đó giữ đường dẫn nối 'a \ b \ c \ d'.

(Tôi chỉ nhận thấy rằng đây là cách tiếp cận giống hệt như của Mike Fair, xin lỗi.)


1

Hoặc bạn có thể viết hàm của riêng bạn cho nó (đó là những gì tôi đã làm).

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

Sau đó, bạn có thể gọi hàm như sau:

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

Điều này có lợi thế là có hoạt động giống hệt như chức năng Join-Path bình thường và không phụ thuộc vào .NET Framework.


0

Bạn có thể sử dụng nó theo cách này:

$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"
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.