ฉันจะใช้ Join-Path เพื่อรวมสตริงมากกว่าสองสตริงเข้ากับเส้นทางไฟล์ได้อย่างไร


107

หากฉันต้องการรวมสองสตริงเข้ากับเส้นทางไฟล์ฉันใช้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 ถ่าน: 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 (เนื่องจากฉันไม่ได้ระบุ-childpathอาร์กิวเมนต์) เช่น "somepath" จากนั้นสร้างเส้นทางไฟล์สามไฟล์

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

ซึ่งไม่ถูกต้องเช่นกัน


โปรดทราบว่าเนื่องจาก PowerShell 6ความพยายามครั้งแรกที่ใช้งานง่ายของคุณทำงานได้ตามที่คาดไว้ และจัดการตัวคั่นเส้นทางต่อท้าย / นำหน้าในบางส่วนของเส้นทางได้อย่างถูกต้อง🎉
Marcus Mangelsdorf

คำตอบ:


171

คุณสามารถใช้คลาส. NET Path :

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

3
แน่นอนว่าเป็นรูปแบบที่รัดกุมที่สุดและจัดการตัวคั่นเส้นทางและเครื่องหมายทับ / เครื่องหมายทับบนชิ้นส่วนพา ธ อย่างถูกต้องซึ่งคำตอบที่ยอมรับในปัจจุบัน (การต่อสายอักขระพื้นฐาน) ไม่ทำ
David Keaveny

3
สำหรับการดำเนินการคำสั่งด้านบนใน powershell ของฉันคือการได้รับข้อผิดพลาดนี้ - ไม่พบการโอเวอร์โหลดสำหรับ "รวม" และจำนวนอาร์กิวเมนต์: "3" ที่บรรทัด: 1 ถ่าน: 19 + [io.path] :: รวม <<<< ('c: \', 'foo', 'bar') + CategoryInfo: NotSpecified: (:) [], 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:\foo, Path.Combineแต่ละเว้นอาร์กิวเมนต์แรกเมื่อใดก็ตามที่อาร์กิวเมนต์ที่สองมีตัวคั่นชั้นนำ: รำคาญเอาท์พุท[io.path]::combine('c:\', '\foo') \foo
Quantic

101

เนื่องจาก Join-Path สามารถ pip ค่าเส้นทางได้คุณจึงสามารถไพพ์คำสั่ง Join-Path หลาย ๆ คำสั่งเข้าด้วยกัน:

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

มันไม่ได้สั้นเท่าที่คุณอาจต้องการ แต่เป็น PowerShell ที่สมบูรณ์และอ่านง่าย


4
1 เพราะมันจะทำงานในทุก PowerShell 2,3,4, ปัญหากับ [io.path] :: รวม API เป็นที่แตกต่างกันสำหรับกรอบ net 3,4
ราม

20

ตั้งแต่ 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 ไม่ใช่สิ่งที่คุณกำลังมองหา มีการใช้งานหลายอย่าง แต่ไม่ใช่สิ่งที่คุณกำลังมองหา ตัวอย่างจาก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ยอมรับเพียงสองข้อ สิ่งที่คุณกำลังมองหาคือ a -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
นี่เป็นสิ่งที่ผิดเพราะแม้ว่า \ in path จะทำงานต่อเนื่องกันหลาย ๆ ทาง แต่ก็น่าเกลียดและอาจทำให้เกิดปัญหาได้
Mikhail Orlov

@MikhailOrlov คุณสามารถอธิบายถึงปัญหาที่อาจเกิดขึ้นตามที่ควรจะบอกว่ามันอาจเกิดขึ้นได้หรือไม่? คุณมีข้อเสนอแนะอื่น ๆ หรือไม่? ฉันกำลังถามเพราะฉันไม่เห็นปัญหา หากมีสิ่งผิดปกติฉันต้องการที่จะแก้ไข
Matt

2
เมื่อเร็ว ๆ นี้ฉันได้จัดการโค้ดคุณภาพต่ำจำนวนมากผู้คนเปรียบเทียบเส้นทางด้วย String.Equals และแยกวิเคราะห์เส้นทางด้วย String.Split ('\\') โดยไม่ต้องลบสตริงว่าง ฉันไม่สามารถคิดถึงผลที่ตามมาที่เป็นอันตรายได้ส่วนใหญ่ฉันแค่หวาดระแวง ขอบคุณสำหรับการแก้ไข
Mikhail Orlov

3
การรวมตัวคั่นเส้นทางอย่างชัดเจนอาจทำให้เกิดปัญหากับการพกพาข้ามแพลตฟอร์ม แม้ว่า PowerShell จะทำงานบน Windows เท่านั้น แต่ก็มีแนวโน้มที่จะเปลี่ยนแปลงในอนาคตอันใกล้นี้และเป็นความคิดที่ดีที่จะพัฒนานิสัยที่ดีให้เร็วที่สุด ไม่ต้องพูดถึงว่านิสัยเหล่านี้สามารถถ่ายโอนไปยังภาษาอื่นได้
bshacklett

10

หากคุณยังคงใช้. NET 2.0 [IO.Path]::Combineจะไม่มีการparams string[]โอเวอร์โหลดซึ่งคุณต้องเข้าร่วมมากกว่าสองส่วนและคุณจะเห็นข้อผิดพลาดไม่พบการโอเวอร์โหลดสำหรับ "รวม" และจำนวนอาร์กิวเมนต์: "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 ต้องมีค่า (ต้องไม่เป็นโมฆะหรือว่างเปล่า)


4

ต่อไปนี้เป็นอีกสองวิธีในการเขียนฟังก์ชัน PowerShell แบบเพียว ๆ เพื่อรวมส่วนประกอบต่างๆเข้ากับเส้นทางโดยพลการ

ฟังก์ชันแรกนี้ใช้อาร์เรย์เดียวเพื่อจัดเก็บส่วนประกอบทั้งหมดจากนั้น foreach loop เพื่อรวมเข้าด้วยกัน:

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ตัวแปรในตัวจากนั้นยุบลูป foreach ให้เป็นบรรทัดเดียวโดยใช้วิธีของ Mike Fair

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"
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.