Powershell: Parsing/passing parameter: token yang tidak dikutip yang terlihat seperti argumen bernama dengan titik dua sebagai pemisah dipecah menjadi dua saat diteruskan secara tidak langsung melalui $Args / @Args

Dibuat pada 11 Mar 2018  ·  3Komentar  ·  Sumber: PowerShell/PowerShell

Terkait: #6291, #6292, dan #4624

Menindaklanjuti dari https://github.com/PowerShell/PowerShell/issues/6292#issuecomment -371344550:

Catatan: Token yang dimaksud, seperti -foo:bar , _look_ like bernama argumen PowerShell (dan, di belakang layar, awalnya selalu diuraikan seperti itu - lihat komentar @BrucePay di bawah), tetapi harus diteruskan sebagai -is (kecuali untuk kemungkinan memperluas string) saat memanggil program eksternal.

Token yang terlihat seperti -foo.bar mengalami nasib yang sama (lihat #6291).

Ini sudah berfungsi seperti yang diharapkan dengan _direct_ argumen lewat (mis.,
echoArgs -foo:bar melewati -foo:bar sebagai argumen tunggal
), tetapi tidak ketika melewati $Args / memercikinya ( @Args ).

Catatan: Masalahnya mendasar pada penggunaan $Args / @Args dan, meskipun mungkin kurang umum, juga memengaruhi penggunaannya saat memanggil perintah asli PowerShell - lihat komentar di bawah .

Langkah-langkah untuk mereproduksi

Jalankan di macOS atau Linux.

function baz {
  bash -c 'for a; do echo $a; done' - $Args   # note: same with <strong i="32">@Args</strong>
}

baz -foo:bar
'---'
baz -foo.bar

Perilaku yang diharapkan

-foo:bar
---
-foo.bar

Perilaku sebenarnya

-foo:
bar
---
-foo
.bar

Argumen tiba-tiba terbelah menjadi dua. Lihat komentar tertaut untuk latar belakang.

Data lingkungan

PowerShell Core v6.0.1 on macOS 10.13.3
PowerShell Core v6.0.1 on Ubuntu 16.04.3 LTS
PowerShell Core v6.0.1 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)
Windows PowerShell v5.1.15063.674 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)
Issue-Discussion WG-Language

Komentar yang paling membantu

Saya baru saja menghabiskan seminggu terakhir ini untuk bergulat dengan masalah ini. Setelah melacaknya ke nilai parameter sakelar paksa (seperti -foo:$bar ) tidak disetel setelah memerciki @Args , saya pikir ini pasti masalah pelingkupan yang tidak jelas. Bayangkan keterkejutan saya ketika saya menemukan bahwa memaksakan nilai parameter sakelar apa pun dengan cara ini tidak akan memiliki perilaku yang diharapkan. Saya mengujinya bahkan dengan rilis Powershell 7 Preview 1 dan masalah yang sama masih muncul. Saya berharap itu akan diperbaiki setelah lebih dari satu tahun menjadi masalah yang diketahui ...

Untuk referensi, ini adalah tes yang saya buat untuk menunjukkan bug. Melihatnya di mesin Server 2016 dengan Powershell 7 Pratinjau 1, serta mesin Server 2012 R2 dengan Powershell 5.1.

function foo
{
    param(
        [switch]$testArg = $false
    )

    write-host "Test arg value: '$testArg'"
}

function bar
{
    foo <strong i="9">@Args</strong>
}

$testSplat = @{
    testArg = $false
}

write-host "#### Foo tests ####"

foo
foo -testArg:$true
foo -testArg:$false
foo <strong i="10">@testSplat</strong>

write-host "#### Bar tests ####"

bar
bar -testArg:$true
bar -testArg:$false
bar <strong i="11">@testSplat</strong>

Semua 3 komentar

Catatan: Token yang dimaksud, seperti - foo:bar terlihat seperti argumen PowerShell bernama,

@mklement0 Seperti yang saya jelaskan di komentar di #6292, argumen _are_ bernama PowerShell ini. Selalu. Pengikatan parameter dilakukan lama setelah kompilasi selesai. Sekarang di AST yang dikompilasi, token untuk -foo: memiliki tanda yang menunjukkan bahwa tidak ada spasi setelahnya dalam kode sumber. NativeCommandParameterBinder melihat elemen AST ini untuk melihat apakah flag sudah disetel, lalu menggabungkan parameter dan argumen tanpa spasi di antara keduanya. Jika tidak disetel, maka spasi dimasukkan. Ini hanya berfungsi jika argumennya literal (yaitu NativeCommandParameter adalah akses ke AST untuk argumen). Dalam kasus percikan, argumennya adalah nilai, bukan argumen literal sehingga Anda mendapatkan spasi.

Salah satu cara untuk memperbaikinya adalah dengan menyebarkan properti token "NoSpace" ke dalam metadata pada nilai string yang sesuai. NativeCommandParameterBinder kemudian dapat memeriksa metadata ini saat mengubah array arg menjadi string. Mungkin ada baiknya memikirkan hal ini sedikit lagi untuk melihat apakah ada kasus lain (terutama *nix kasus tertentu) yang harus ditangani.

6492 (sejak ditutup sebagai duplikat) menunjukkan bahwa percikan @args juga rusak saat memanggil fungsi PowerShell (bukan hanya program eksternal):

Mendukung skenario itu mungkin bahkan lebih rumit, karena harapan di sini adalah bahwa parameter bernama dilewatkan apa adanya, dengan tipe aslinya.

Berikut repro yang disederhanakan:

function b {
  Param
  (
      [Switch] $p1,
      [int] $p2,
      $rest
  )
  "`$p1: [$p1]"
  "`$p2: [$p2]"
  "`$rest: [$rest]"
}

& { b <strong i="9">@args</strong> } -p1:$false 666
$p1: [True]  # `-p1:` was interpreted as just `-p1`
$p2: [0]      # `$false`, as a separate argument, was coerced to [int] 0
$rest: [666] # what was meant to be the 2nd argument was passed as the 3rd

Solusinya - dan solusi yang lebih baik untuk memulai - adalah dengan mendefinisikan blok param() dalam fungsi relay dan percikan @PSBoundParameters .

Saya baru saja menghabiskan seminggu terakhir ini untuk bergulat dengan masalah ini. Setelah melacaknya ke nilai parameter sakelar paksa (seperti -foo:$bar ) tidak disetel setelah memerciki @Args , saya pikir ini pasti masalah pelingkupan yang tidak jelas. Bayangkan keterkejutan saya ketika saya menemukan bahwa memaksakan nilai parameter sakelar apa pun dengan cara ini tidak akan memiliki perilaku yang diharapkan. Saya mengujinya bahkan dengan rilis Powershell 7 Preview 1 dan masalah yang sama masih muncul. Saya berharap itu akan diperbaiki setelah lebih dari satu tahun menjadi masalah yang diketahui ...

Untuk referensi, ini adalah tes yang saya buat untuk menunjukkan bug. Melihatnya di mesin Server 2016 dengan Powershell 7 Pratinjau 1, serta mesin Server 2012 R2 dengan Powershell 5.1.

function foo
{
    param(
        [switch]$testArg = $false
    )

    write-host "Test arg value: '$testArg'"
}

function bar
{
    foo <strong i="9">@Args</strong>
}

$testSplat = @{
    testArg = $false
}

write-host "#### Foo tests ####"

foo
foo -testArg:$true
foo -testArg:$false
foo <strong i="10">@testSplat</strong>

write-host "#### Bar tests ####"

bar
bar -testArg:$true
bar -testArg:$false
bar <strong i="11">@testSplat</strong>
Apakah halaman ini membantu?
0 / 5 - 0 peringkat