Bagaimana cara menggunakan Join-Path untuk menggabungkan lebih dari dua string ke dalam jalur file?

105

Jika saya ingin menggabungkan dua string menjadi jalur file, saya menggunakan Join-Pathseperti ini:

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

Cetakan itu "C:\Program Files". Jika saya ingin melakukan ini untuk lebih dari dua string:

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

PowerShell membuat kesalahan:

Jalur Gabungan: Parameter posisi tidak dapat ditemukan yang menerima argumen 'Microsoft Office'.
Di 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

Saya mencoba menggunakan array string:

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

Tapi PowerShell meminta saya untuk masuk ke jalur anak (karena saya tidak menentukan -childpathargumennya), misalnya "somepath", lalu membuat tiga jalur file,

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

yang juga tidak benar.

Michael A
sumber

Jawaban:

171

Anda dapat menggunakan kelas Path .NET :

[IO.Path]::Combine('C:\', 'Foo', 'Bar')
Marek Toman
sumber
3
Bentuk yang pasti paling ringkas, dan menangani pemisah jalur dan garis miring di akhir / di depan pada fragmen jalur dengan benar, yang tidak dilakukan oleh jawaban yang diterima saat ini (penggabungan string dasar).
David Keaveny
3
Untuk menjalankan perintah di atas di PowerShell saya mendapatkan kesalahan ini -Tidak dapat menemukan kelebihan beban untuk "Gabungkan" dan jumlah argumen: "3". Pada baris: 1 karakter: 19 + [io.path] :: gabungkan <<<< ('c: \', 'foo', 'bar') + CategoryInfo: NotSpecified: (:) [], MethodException + FullyQualifiedErrorId: MethodCountCouldNotFindBest
Aamol
@Aamol Versi CLR apa yang Anda jalankan ( $PSVersionTable)? Apakah [io.path]::combine([string[]]('c:\','foo','bar'))bekerja?
Marek Toman
1
Tampaknya batas parameter adalah 3, setelah 3 parameter pertama diabaikan. (di sini setidaknya, ps 5.1, clr 4.0)
ehiller
4
@DavidKeaveny "menangani pemisah jalur dan garis miring di akhir / di depan pada fragmen jalur dengan benar" - Tidak juga. join-pathmelakukan apa yang Anda harapkan, join-path "C:\" "\foo"keluaran C:\foo, Path.Combinenamun mengabaikan argumen pertama setiap kali argumen kedua berisi pemisah utama: [io.path]::combine('c:\', '\foo')keluaran yang mengganggu \foo.
Quantic
99

Karena Join-Path dapat menyalurkan nilai path, Anda dapat menyalurkan beberapa pernyataan Join-Path bersama-sama:

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

Ini tidak sesingkat yang mungkin Anda inginkan, tetapi ini sepenuhnya PowerShell dan relatif mudah dibaca.

David Keaveny
sumber
3
+1 karena ini akan bekerja di semua PowerShell 2,3,4, masalah dengan [io.path] :: Combine API berbeda untuk .net framework 3,4
Ram
18

Sejak PowerShell 6.0, Join-Path memiliki parameter baru yang dipanggil -AdditionalChildPathdan dapat menggabungkan beberapa bagian jalur secara out-of-the-box . Baik dengan memberikan parameter ekstra atau dengan hanya menyediakan daftar elemen.

Contoh dari dokumentasi :

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

Jadi di PowerShell 6.0 dan di atasnya varian Anda

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

bekerja seperti yang diharapkan!

Marcus Mangelsdorf
sumber
17

Join-Path bukanlah yang Anda cari. Ini memiliki banyak kegunaan tetapi bukan yang Anda cari. Contoh dari 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

Anda melihat bahwa itu menerima larik string, dan itu menggabungkan string anak ke setiap jalur lengkap pembuatan. Dalam contoh Anda $path = join-path C: "Program Files" "Microsoft Office",. Anda mendapatkan kesalahan karena Anda meneruskan tiga argumen posisi dan join-pathhanya menerima dua. Apa yang Anda cari adalah a -join, dan saya bisa melihat ini sebagai kesalahpahaman. Pertimbangkan sebagai gantinya ini dengan contoh Anda:

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

-Joinmengambil array item dan menggabungkannya \menjadi satu string.

C:\Program Files\Microsoft Office

Upaya kecil untuk menyelamatkan

Ya, saya setuju bahwa jawaban ini lebih baik, tetapi jawaban saya masih bisa berfungsi. Komentar menunjukkan bahwa mungkin ada masalah dengan garis miring, jadi untuk tetap menggunakan pendekatan penggabungan saya, Anda juga dapat melakukan ini.

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

Jadi jika ada masalah dengan garis miring ekstra, itu bisa ditangani selama tidak di awal string (mengizinkan jalur UNC ). [io.path]::combine('c:\', 'foo', '\bar\')tidak akan bekerja seperti yang diharapkan dan saya akan menjelaskannya. Keduanya membutuhkan string yang tepat untuk input karena Anda tidak dapat memperhitungkan semua skenario. Pertimbangkan kedua pendekatan tersebut, tetapi, ya, jawaban lain yang berperingkat lebih tinggi lebih singkat, dan saya bahkan tidak tahu itu ada.

Juga, ingin menunjukkan, jawaban saya menjelaskan bagaimana kesalahan yang dilakukan OP selain memberikan saran untuk mengatasi masalah inti.

Matt
sumber
2
Ini salah karena meskipun beberapa jalur \ yang berurutan akan berfungsi, itu buruk dan berpotensi menyebabkan masalah.
Mikhail Orlov
@MikhailOrlov Dapatkah Anda mendeskripsikan potensi masalah yang seharusnya hanya menyarankan agar hal itu bisa terjadi? Apakah Anda punya saran lain? Saya bertanya karena saya tidak melihat masalah. Jika ada yang salah, saya ingin mengatasinya.
Matt
2
Saya telah menangani banyak kode berkualitas rendah baru-baru ini, orang membandingkan jalur dengan String.Equals dan jalur parse dengan String.Split ('\\') tanpa menghapus string kosong. Saya tidak bisa memikirkan konsekuensi yang lebih berbahaya, kebanyakan saya hanya menjadi paranoid. Terima kasih atas hasil edit Anda.
Mikhail Orlov
3
Menyertakan pemisah jalur secara eksplisit dapat menyebabkan masalah dengan portabilitas lintas platform. Meskipun PowerShell saat ini hanya berjalan di Windows, kemungkinan besar akan berubah dalam waktu yang tidak terlalu lama, dan merupakan ide yang baik untuk mengembangkan kebiasaan baik sedini mungkin. Belum lagi kebiasaan tersebut bisa berpindah ke bahasa lain.
bshacklett
10

Jika Anda masih menggunakan .NET 2.0, maka [IO.Path]::Combinetidak akan ada params string[]kelebihan beban yang Anda perlukan untuk menggabungkan lebih dari dua bagian, dan Anda akan melihat kesalahan Tidak dapat menemukan beban berlebih untuk "Gabungkan" dan jumlah argumen: "3".

Sedikit kurang elegan, tetapi solusi PowerShell murni adalah dengan menggabungkan bagian jalur secara manual:

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

atau

Join-Path  (Join-Path  C: "Program Files") "Microsoft Office"
Konstantin Spirin
sumber
5

Inilah sesuatu yang akan melakukan apa yang Anda inginkan saat menggunakan array string untuk ChildPath.

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

Output yang mana

C:\Program Files\Microsoft Office

Satu-satunya peringatan yang saya temukan adalah bahwa nilai awal $ path harus memiliki nilai (tidak boleh null atau kosong).

Mike Fair
sumber
4

Berikut adalah dua cara lagi untuk menulis fungsi PowerShell murni untuk menggabungkan sejumlah komponen ke dalam jalur.

Fungsi pertama ini menggunakan satu larik untuk menyimpan semua komponen dan kemudian perulangan foreach untuk menggabungkannya:

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
}

Karena komponen jalur adalah elemen dalam larik dan semuanya merupakan bagian dari satu argumen, mereka harus dipisahkan dengan koma. Penggunaannya adalah sebagai berikut:

PS C: \> Gabung-Jalur 'C:', 'Program Files', 'Microsoft Office'
C: \ Program Files \ Microsoft Office


Cara yang lebih minimalis untuk menulis fungsi ini adalah dengan menggunakan $argsvariabel built-in , dan kemudian menutup loop foreach menjadi satu baris menggunakan metode Mike Fair.

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

Tidak seperti versi fungsi sebelumnya, setiap komponen jalur adalah argumen terpisah, jadi hanya diperlukan spasi untuk memisahkan argumen:

PS C: \> Bergabung-Paths2 'C:' 'Program Files' 'Microsoft Office'
C: \ Program Files \ Microsoft Office
Jon
sumber
2

Pendekatan berikut ini lebih ringkas daripada menyisipkan pernyataan Join-Path:

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

$ p lalu menampung jalur gabungan 'a \ b \ c \ d'.

(Saya baru saja memperhatikan bahwa ini adalah pendekatan yang sama persis dengan Mike Fair, maaf.)

Daniel
sumber
1

Atau Anda bisa menulis fungsi Anda sendiri untuk itu (yang akhirnya saya lakukan).

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

Anda kemudian dapat memanggil fungsi seperti ini:

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

Ini memiliki keuntungan karena memiliki perilaku yang sama persis dengan fungsi Jalur Gabungan normal dan tidak bergantung pada .NET Framework.

Kevin
sumber
0

Anda dapat menggunakannya dengan cara ini:

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