Powershell: Argumen untuk executable eksternal tidak di-escape dengan benar

Dibuat pada 21 Agu 2016  ·  170Komentar  ·  Sumber: PowerShell/PowerShell

Langkah-langkah untuk mereproduksi

  1. tulis program C native.exe yang memperoleh ARGV
  2. Jalankan native.exe "`"a`""

    Perilaku yang diharapkan

ARGV [1] == "a"

Perilaku sebenarnya

ARGV [1] == a

Data lingkungan

Windows 10 x64

Name                           Value
----                           -----
PSVersion                      5.1.14393.0
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.14393.0
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
Committee-Reviewed WG-Engine

Komentar yang paling membantu

Membuat operator khusus untuk ini sama sekali tidak masuk akal untuk shell baris perintah, karena tugas utamanya adalah meluncurkan program dan meneruskan argumen kepada mereka. Memperkenalkan operator baru yang menghasilkan system() untuk pekerjaan ini seperti Matlab memperkenalkan cara memanggil calc.exe karena ada bug dalam aritmatikanya. Yang seharusnya dilakukan adalah:

  • Tim pwsh bersiap untuk rilis utama baru yang memperbaiki hal-hal baris perintah, memindahkan perilaku saat ini ke belakang cmdlet bawaan.
  • Sebagai solusi stop-gap, versi pwsh yang akan datang mendapatkan cmdlet bawaan yang menggunakan perilaku baru yang benar untuk penyampaian baris perintah.

Hal yang sama berlaku untuk Start-Process . (Sebenarnya ini adalah kandidat yang cukup bagus untuk cmdlet "baru" dengan beberapa opsi seperti -QuotingBehavior Legacy ...) Lihat # 13089.

Semua 170 komentar

Menurut Anda, mengapa "\"a\"" perilaku yang diharapkan? Pemahaman saya tentang pelarian PowerShell mengatakan bahwa perilaku sebenarnya adalah perilaku yang benar dan diharapkan. " "a "" adalah sepasang tanda kutip yang mengelilingi pasangan tanda kutip yang lolos di sekitar a , jadi PowerShell menafsirkan pasangan luar yang tidak lolos sebagai" ini adalah argumen string "dan jadi lepaskan mereka, lalu tafsirkan pasangan lolos sebagai tanda kutip lolos dan simpanlah, meninggalkan Anda dengan "a" . Tidak ada poin \ ditambahkan ke string.

Fakta bahwa Bash menggunakan \ sebagai karakter pelarian tidak relevan. Di PowerShell, karakter melarikan diri adalah pukulan balik. Lihat karakter escape PowerShell.

Jika Anda benar-benar ingin memberikan "\"a\"" , saya yakin Anda akan menggunakan:

> echo `"\`"a\`"`"
"\"a\""

@santrikece
Ya, escapes berfungsi dengan baik untuk cmdlet internal, tetapi hal-hal menjadi aneh saat berkomunikasi dengan biner asli , terutama di Windows.
Saat menjalankan native.exe " "a "" , ARGV [1] seharusnya

"a"

(tiga karakter)

dari pada

a

(satu karakter).

Saat ini untuk membuat native.exe dengan benar menerima ARGV dengan dua tanda kutip dan karakter a , Anda harus menggunakan panggilan aneh ini:

native.exe "\`"a\`""

Ah, begitu. Membuka kembali.

Karena rasa ingin tahu yang kuat, apa yang terjadi jika Anda mencoba bangunan menggunakan # 1639?

@andsmall . Anda HARUS melakukan double-esacpe untuk memuaskan PowerShell dan CommandLineToArgvW . Garis ini:

native.exe "`"a`""

menghasilkan StartProcess yang sama dengan cmd

native.exe ""a""

@ be5invis @douglaswth apakah ini diselesaikan melalui https://github.com/PowerShell/PowerShell/pull/2182?

Tidak, Kami masih perlu menambahkan garis miring terbalik sebelum tanda kutip ganda yang di-escape? Ini tidak menyelesaikan masalah pelolosan ganda. (Artinya, kita harus menghindari tanda kutip ganda untuk PowerShell dan CommandLineToArgvW.)

Karena " "a "" sama dengan '"a"' , apakah Anda menyarankan bahwa native.exe '"a"' harus menghasilkan "\"a\"" ?

Ini tampak seperti permintaan fitur yang jika diterapkan dapat merusak sejumlah besar skrip PowerShell yang sudah ada yang menggunakan pelolosan ganda yang diperlukan, sehingga solusi apa pun harus sangat hati-hati.

@ Ya.
@douglaswth Pengolosan ganda benar-benar konyol: mengapa kita membutuhkan pelolosan "batin" yang dibuat di era DOS?

@vors @douglasw
Ini adalah kode C yang digunakan untuk menampilkan hasil GetCommandLineW dan CommandLineToArgvW:

#include <stdio.h>
#include <wchar.h>
#include <Windows.h>

int main() {
  LPWSTR cmdline = GetCommandLineW();
  wprintf(L"Command Line : %s\n", cmdline);

  int nArgs;
  LPWSTR *szArglist = CommandLineToArgvW(cmdline, &nArgs);
  if (NULL == szArglist) {
    wprintf(L"CommandLineToArgvW failed\n");
    return 0;
  } else {
    for (int i = 0; i < nArgs; i++) {
      wprintf(L"argv[%d]: %s\n", i, szArglist[i]);
    }
  }
  LocalFree(szArglist);
}

Inilah hasilnya

$ ./a "a b"
Command Line : "Z:\playground\ps-cmdline\a.exe" "a b"
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: a b

$ ./a 'a b'
Command Line : "Z:\playground\ps-cmdline\a.exe" "a b"
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: a b

$ ./a 'a"b'
Command Line : "Z:\playground\ps-cmdline\a.exe" a"b
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: ab

$ ./a 'a"b"c'
Command Line : "Z:\playground\ps-cmdline\a.exe" a"b"c
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: abc

$ ./a 'a\"b\"c'
Command Line : "Z:\playground\ps-cmdline\a.exe" a\"b\"c
argv[0]: Z:\playground\ps-cmdline\a.exe
argv[1]: a"b"c

@ be5invis Saya tidak setuju dengan Anda tentang pelolosan ganda yang mengganggu, tetapi saya hanya mengatakan bahwa perubahan pada ini harus kompatibel ke belakang dengan apa yang digunakan skrip PowerShell yang ada.

Berapa jumlahnya Saya kira tidak ada penulis naskah yang tahu tentang kutipan ganda seperti itu. Ini adalah bug, bukan fitur, dan tidak didokumentasikan.

???? iPhone

? 2016? 9? 21 ?? 01: 58? Douglas Thrift < [email protected] [email protected] > ???

@ be5i nvishttps: //github.com/be5invis Saya tidak setuju dengan Anda tentang pelolosan ganda yang menjengkelkan, tetapi saya hanya mengatakan bahwa perubahan pada ini harus kompatibel ke belakang dengan apa yang digunakan skrip PowerShell yang ada.

Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di Gi tHubhttps: //github.com/PowerShell/PowerShell/issues/1995#issuecomment -248381045, atau nonaktifkan pembacaan thhttps: //github.com/notifications/unsubscribe-auth/AAOp20f_W0mTl2YiJKi_flQB5JpZB

PowerShell telah ada selama 9 tahun sehingga kemungkinan besar ada banyak skrip di luar sana. Saya menemukan banyak informasi tentang perlunya pelolosan ganda dari StackOverflow dan sumber lain ketika saya membutuhkannya, jadi saya tidak tahu apakah saya setuju dengan klaim Anda tentang tidak ada yang mengetahui tentang kebutuhannya atau bahwa itu tidak didokumentasikan .

Untuk konteks tambahan, saya ingin berbicara sedikit tentang implementasinya.
PowerShell memanggil .NET API untuk menelurkan proses baru, yang memanggil Win32 API (di windows).

Di sini, PS membuat StartProcessInfo yang digunakan
https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/NativeCommandProcessor.cs#L1063

API yang disediakan mengambil satu string untuk argumen dan kemudian diurai kembali menjadi array argumen untuk melakukan eksekusi.
Aturan penguraian ulang ini tidak dikontrol oleh PowerShell. Ini adalah Win32 API (dan untungnya, itu konsisten dalam inti dotnet dan aturan unix).
Secara khusus, kontrak ini menjelaskan perilaku \ dan " .

Meskipun, PowerShell mungkin mencoba untuk menjadi lebih pintar dan memberikan pengalaman yang lebih baik, perilaku saat ini konsisten dengan cmd dan bash: Anda dapat menyalin baris yang dapat dieksekusi asli dari mereka dan menggunakannya di PowerShell dan cara kerjanya sama.

@ be5invis Jika Anda mengetahui cara untuk meningkatkan expirience dengan cara yang tidak terputus, harap https://github.com/PowerShell/PowerShell/blob/master/docs/dev-process/breaking-change-contract.md

Ini berlaku untuk Windows, tetapi ketika menjalankan perintah di Linux atau Unix, aneh bahwa seseorang perlu menggandakan tanda kutip melarikan diri.

Di Linux, proses tidak memiliki satu baris perintah, melainkan serangkaian argumen.
Oleh karena itu, argumen di PowerShell harus sama dengan yang diteruskan ke file yang dapat dieksekusi, alih-alih menggabungkan semua argumen dan kemudian melepaskannya.

Bahkan di windows, perilaku saat ini tidak konsisten:
Jika sebuah argumen tidak berisi spasi, argumen itu diteruskan tanpa perubahan.
Jika sebuah argumen berisi spasi, jika akan diapit oleh tanda kutip, untuk menyatukannya melalui CommandLineToArgvW call. => Argumen diubah untuk memenuhi persyaratan CommandLineToArgvW .
Tetapi jika argumen berisi tanda kutip, itu tidak di-escape. => Argumen tidak diubah, meskipun CommandLineToArgvW membutuhkan ini.

Saya pikir argumen tidak boleh diubah, atau selalu diubah untuk memenuhi persyaratan CommandLineToArgvW , tetapi tidak di setengah kasus.

Mengenai pemutusan kontrak:
Karena saya tidak dapat menemukan dokumentasi resmi apa pun tentang pelolosan ganda, saya akan menganggap ini sebagai kategori "Keranjang 2: Area Abu-abu yang Wajar", jadi ada kemungkinan untuk mengubahnya, atau apakah saya salah?

@vors Ini sangat menjengkelkan jika argumen Anda adalah variabel atau sesuatu yang lain: Anda harus menghindarinya secara manual sebelum mengirimnya ke aplikasi asli.
Operator "pelolosan otomatis" dapat membantu. seperti ^"a " " -> "a\ " "`

Saya pikir @TSlivede memperbaikinya dengan ketidakkonsistenan dalam perilaku.

Saya pikir argumen tidak boleh diubah, atau selalu diubah untuk memenuhi persyaratan CommandLineToArgvW, tetapi tidak di setengah kasus.

Saya tidak yakin tentang keranjangnya, tetapi bahkan keranjang "perubahan yang jelas melanggar" berpotensi dapat diubah. Kami ingin membuat PowerShell lebih baik, tetapi kompatibilitas ke belakang adalah salah satu prioritas tertinggi kami. Itulah mengapa tidak mudah.
Kami memiliki komunitas yang hebat dan saya yakin kami dapat menemukan konsensus.

Adakah yang ingin memulai proses RFC?

Sebaiknya investigasi penggunaan P / Invoke daripada .Net untuk memulai proses jika itu menghindari kebutuhan PowerShell untuk menambahkan kutipan ke argumen.

@lzybkr Sejauh yang saya tahu, PInvoke tidak akan membantu.
Dan di sinilah API unix dan windows berbeda:

https://msdn.microsoft.com/en-us/library/20y988d2.aspx (memperlakukan spasi sebagai pemisah)
https://linux.die.net/man/3/execvp (tidak memperlakukan spasi sebagai pemisah)

Saya tidak menyarankan untuk mengubah implementasi Windows.

Saya akan mencoba menghindari perilaku khusus platform di sini. Ini akan merusak portabilitas skrip.
Saya pikir kita dapat mempertimbangkan untuk mengubah perilaku windows dengan cara yang tidak bisa dipecahkan. Yakni dengan variabel preferensi. Dan kemudian kita dapat memiliki default yang berbeda atau semacamnya.

Kita berbicara tentang memanggil perintah eksternal - bagaimanapun juga agak bergantung pada platform.

Yah, saya pikir itu tidak bisa benar-benar platform independen, karena Windows dan Linux hanya memiliki cara yang berbeda untuk memanggil file yang dapat dieksekusi. Di Linux suatu proses mendapat array argumen sementara di Windows suatu proses hanya mendapat satu baris perintah (satu string).
(bandingkan yang lebih mendasar
CreateProcess -> baris perintah (https://msdn.microsoft.com/library/windows/desktop/ms682425)
dan
execve -> array perintah (https://linux.die.net/man/2/execve)
)

Saat Powershell menambahkan kutipan tersebut saat argumen memiliki spasi di dalamnya, menurut saya, PowerShell mencoba ** untuk meneruskan argumen dengan cara, bahwa CommandLineToArgvW membagi baris perintah ke argumen yang awalnya diberikan di PowerShell. (Dengan cara ini program-c tipikal mendapatkan argumen yang sama dalam array argv-nya seperti fungsi PowerShell yang didapat sebagai $ args.)
Ini akan sangat cocok dengan hanya meneruskan argumen ke systemcall linux (seperti yang disarankan melalui p / invoke).

** (dan gagal, karena tidak luput dari kutipan)

PS: Apa yang diperlukan untuk memulai proses RFC?

Persis - PowerShell mencoba memastikan CommandLineToArgvW menghasilkan perintah yang benar dan _after_ mem-reparsing apa yang telah diurai oleh PowerShell.

Ini telah menjadi titik sakit yang lama di Windows, saya melihat alasan untuk membawa kesulitan itu ke * nix.

Bagi saya, ini terasa seperti detail implementasi, tidak terlalu membutuhkan RFC. Jika kami mengubah perilaku di Windows PowerShell, ini mungkin memerlukan RFC, tetapi meskipun demikian, perubahan yang tepat dapat dianggap sebagai perbaikan bug (mungkin berisiko).

Ya, saya juga berpikir, bahwa mengubahnya di Linux untuk menggunakan panggilan sistem langsung akan membuat semua orang merasa lebih bahagia.

Saya masih berpikir itu juga harus diubah di windows,
(Mungkin dengan menambahkan variabel preferensi bagi mereka yang tidak ingin mengubah skrip mereka)
karena sekarang hanya salah - ini adalah bug. Jika ini diperbaiki, syscall langsung di linux bahkan tidak diperlukan, karena argumen apa pun akan mencapai proses selanjutnya tanpa perubahan.

Tetapi karena ada file yang dapat dieksekusi, yang membagi baris perintah dengan cara, tidak kompatibel dengan CommandLineToArgvW , saya suka ide @ be5invis tentang operator untuk argumen - tetapi saya tidak akan membuat operator pelolosan otomatis (seharusnya default untuk semua argumen), tetapi tambahkan operator agar tidak lolos dari argumen (tambahkan tanpa tanda kutip, jangan escape apa pun).

Masalah ini baru saja muncul bagi kami hari ini ketika seseorang mencoba perintah berikut di PowerShell dan menolak PowerShell ketika tidak berhasil tetapi CMD melakukannya:

wmic useraccount where name='username' get sid

Dari echoargs PSCX, wmic.exe melihat ini:

94> echoargs wmic useraccount where name='tso_bldadm' get sid
Arg 0 is <wmic>
Arg 1 is <useraccount>
Arg 2 is <where>
Arg 3 is <name=tso_bldadm>
Arg 4 is <get>
Arg 5 is <sid>

Command line:
"C:\Users\hillr\Documents\WindowsPowerShell\Modules\Pscx\3.2.2\Apps\EchoArgs.exe" wmic useraccount where name=tso_bldadm get sid

Jadi, API apa yang digunakan CMD.exe untuk menjalankan proses / membentuk baris perintah? Dalam hal ini, apa yang -% lakukan agar perintah ini berfungsi?

@rkehill CreateProcessW . panggilan langsung. Betulkah.

Mengapa Powershell berperilaku berbeda dalam dua situasi ini? Secara khusus, ini tidak konsisten membungkus arg yang berisi spasi dalam tanda kutip ganda.

# Desired argv[1] is 4 characters: A, space, double-quote, B
$ .\echoargs.exe 'A \"B'
<"C:\test\echoargs.exe" "A \"B">
<A "B>
# Correct!

# Desired argv value is 4 characters: A, double-quote, space, B
$ .\echoargs.exe 'A\" B'
<"C:\test\echoargs.exe" A\" B>
<A"> <B>
# Wrong...

Sepertinya tidak ada sajak atau alasan. Dalam situasi pertama, ini membungkus argumen saya dengan tanda kutip ganda, tetapi dalam situasi kedua tidak. Saya perlu tahu persis kapan itu akan dan tidak akan membungkus dalam tanda kutip ganda sehingga saya dapat membungkus secara manual (atau tidak) dalam skrip saya.

.echoargs.exe dibuat dengan mengkompilasi yang berikut ini dengan cl echoargs.c

// echoargs.c
#include <windows.h>
#include <stdio.h>
int wmain(int argc, WCHAR** argv) {
    wprintf(L"<%ls>\n", GetCommandLineW());
    for(int i = 1; i < argc; i++) {
        wprintf(L">%s< ", argv[i]);
    }
    wprintf(L"\n");
}

EDIT: Ini $ PSVersionTable saya:

Name                           Value
----                           -----
PSVersion                      5.1.15063.296
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.15063.296
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

Perilaku terkait kutipan berubah beberapa kali, oleh karena itu saya menyarankan untuk menggunakan sesuatu seperti ini:

Edit: Formulir yang diperbarui di bawah
Versi lama:

# call helper

function Run-Native($command) {
    $env:commandlineargumentstring=($args | %{'"'+ ($_ -replace '(\\*)"','$1$1\"' -replace '(\\*)$','$1$1') + '"'}) -join ' ';
    & $command --% %commandlineargumentstring%
}

# some test cases

Run-Native .\echoargs.exe 'A "B' 'A" B'
Run-Native .\echoargs.exe 'A "B'
Run-Native .\echoargs.exe 'A" B'
Run-Native .\echoargs.exe 'A\" B\\" \'

Keluaran:

<"C:\test\echoargs.exe"  "A \"B" "A\" B">
<A "B> <A" B>

<"C:\test\echoargs.exe"  "A \"B">
<A "B>

<"C:\test\echoargs.exe"  "A\" B">
<A" B>

<"C:\test\echoargs.exe"  "A\\\" B\\\\\" \\">
<A\" B\\" \>

Garis miring terbalik -replace ganda pertama di depan tanda kutip dan menambahkan satu garis miring terbalik tambahan, untuk menghindari qoute.
Garis miring terbalik -replace di akhir argumen, sehingga kutipan penutup tidak di-escape.

Ini menggunakan --% (PS v3 dan di atasnya), yang merupakan AFAIK satu-satunya cara yang dapat diandalkan untuk meneruskan kutipan ke executable asli.


Edit:

Versi terbaru dari Run-Native , sekarang disebut Invoke-NativeCommand (seperti yang disarankan )

function Invoke-NativeCommand() {
    $command, [string[]] $argsForExe = $args
    if($argsForExe.Length -eq 0){
        & $command
    } else {
        $env:commandlineargumentstring=($argsForExe | %{
            if($_ -match '^[\w\d\-:/\\=]+$'){
                $_ #don't quote nonempty arguments consisting of only letters, numbers, or one of -:/\=
            } else {
                $_ <# double backslashes in front of quotes and escape quotes with backslash #> `
                    -replace '(\\*)"','$1$1\"' `
                   <# opening quote after xxx= or after /xxx: or at beginning otherwise #> `
                    -replace '^([\w\d]+=(?=.)|[/-][\w\d]+[:=](?=.)|^)','$1"' `
                   <# double backslashes in front of closing quote #> `
                    -replace '(\\*)$','$1$1' `
                   <# add closing quote #> `
                    -replace '$','"'
            }
        }) -join ' ';
        & $command --% %commandlineargumentstring%
    }
}

(dengan beberapa inspirasi dari iep )

  • tidak mengutip sakelar sederhana
  • masih berfungsi dengan argumen kosong
  • bekerja jika tidak ada argumen
  • sebagian besar harus bekerja dengan msiexec, cmdkey, dll ...
  • masih selalu berfungsi untuk program, yang mengikuti aturan umum
  • tidak menggunakan "" tidak standar sebagai pelarian " - oleh karena itu tetap tidak akan berfungsi untuk kutipan yang disematkan dalam argumen .bat atau msiexec

Terima kasih, saya tidak tahu tentang --% . apakah ada cara untuk melakukannya tanpa membocorkan variabel lingkungan ke proses asli? (dan untuk proses apa pun yang mungkin dipanggil)

Apakah ada modul PowerShell yang mengimplementasikan Run-Native Cmdlet untuk digunakan semua orang? Ini terdengar seperti sesuatu yang seharusnya ada di Galeri Powershell. Jika cukup bagus, itu bisa menjadi dasar untuk RFC.

"bocor" terdengar seperti Anda mengkhawatirkan keamanan. Namun perhatikan bahwa baris perintah tetap terlihat oleh proses anak. (Misalnya: gwmi win32_process |select name,handle,commandline|Format-Table di Windows dan ps -f di Linux)

Jika Anda masih ingin menghindari variabel lingkungan, Anda mungkin dapat membuat sesuatu menggunakan ekspresi-panggil.

Mengenai RFC:
Saya tidak berpikir perintah seperti itu diperlukan, sebagai gantinya ini harus menjadi perilaku default:

https://github.com/PowerShell/PowerShell-RFC/issues/90

Saya setuju bahwa perilaku default PowerShell harus diperbaiki. Saya dengan pesimis berasumsi bahwa itu tidak akan pernah berubah karena alasan kompatibilitas ke belakang, itulah sebabnya saya menyarankan untuk menulis modul. Namun, saya sangat menyukai cara RFC Anda memungkinkan perilaku pelolosan lama diaktifkan kembali melalui variabel preferensi.

Izinkan saya meringkas pembahasannya, dengan dosis opini yang tepat:

  • Jelas bahwa kami memiliki masalah kompatibilitas ke belakang, jadi perilaku lama harus tetap tersedia.

  • Proposal RFC @TSlivede menjelaskan hal itu sambil menunjuk _ jalan ke masa depan_.
    Sayangnya, proposalnya gagal menjadi _PR_ saat tulisan ini dibuat, dan bahkan belum diterima sebagai _draft_ RFC.


Yang saya maksud dengan _ masa depan_:

  • PowerShell adalah shell dengan sendirinya yang diharapkan akan segera melepaskan bagasi terkait cmd.exe , jadi satu-satunya pertimbangan yang penting ketika datang untuk memanggil utilitas eksternal (executable yang (biasanya) aplikasi konsol / terminal) adalah :

    • Argumen yang akan diteruskan harus ditentukan oleh aturan parsing mode argumen _PowerShell_ _only_.

    • Apa pun hasil _literals_ dari proses itu harus diteruskan _ sebagaimana adanya_ ke target yang dapat dieksekusi, sebagai argumen _individual_.

    • Dengan kata lain: Sebagai pengguna, yang perlu Anda fokuskan adalah hasil dari parsing _PowerShell_ nantinya, dan untuk dapat mengandalkan hasil tersebut diteruskan apa adanya, dengan PowerShell yang menangani semua yang ada di belakangnya. -kode adegan - jika perlu.


_Menerapkan_ masa depan:

  • Di _Windows_:

    • Untuk alasan _historical_, Windows tidak _tidak_ mengizinkan melewatkan argumen _sebagai array literals_ ke target yang dapat dieksekusi; sebagai gantinya, diperlukan _single_ string yang mengkodekan _all_ argumen menggunakan sintaks shell _pseudo_. Yang lebih buruk, itu _ akhirnya tergantung pada target individu yang dapat dieksekusi untuk menafsirkan string tunggal itu_ dan membaginya menjadi argumen.

    • PowerShell terbaik yang dapat dilakukan adalah membentuk string tunggal itu - di belakang layar, setelah melakukan pemisahan _own_ dari baris perintah menjadi argumen individual - dengan cara _predictable dan standar_.

    • Proposal RFC @TSlivede mengusulkan hal itu, dengan menyarankan agar PowerShell mensintesis baris perintah shell semu dengan cara yang akan menyebabkan runtime Windows C / C ++ untuk memulihkan argumen input sebagaimana adanya saat melakukan penguraiannya :

      • Mengingat bahwa pada akhirnya bergantung pada setiap target yang dapat dieksekusi untuk menafsirkan baris perintah, tidak ada _guarantee_ bahwa ini akan berfungsi di semua kasus, tetapi aturan tersebut adalah pilihan yang paling masuk akal, karena _most_ utilitas yang ada menggunakan konvensi ini.

      • Satu-satunya pengecualian penting adalah _batch files_, yang dapat menerima perlakuan khusus, seperti yang disarankan oleh proposal RFC.

  • Di platform _Unix_:

    • Sebenarnya, masalah yang mengganggu penguraian argumen Windows _need tidak pernah muncul_, karena panggilan asli platform untuk membuat proses baru _menerima argumen sebagai array literal_ - argumen apa pun yang diakhiri oleh PowerShell setelah melakukan penguraian _own_ harus diteruskan ke _as-is_ .
      Mengutip @lzybkr : "Saya tidak melihat alasan untuk membawa kesulitan itu ke * nix."

    • Sayangnya, karena keterbatasan .NET Core (CoreFX) saat ini, masalah ini _do_ ikut bermain, karena CoreFX API tidak perlu memaksa anarki argumen _Windows_ yang diteruskan ke dunia Unix juga, dengan mengharuskan penggunaan baris perintah pseudo bahkan pada Unix.

    • Saya telah membuat masalah CoreFX ini untuk meminta agar masalah tersebut diperbaiki.

    • Sementara itu, mengingat CoreFX membagi kembali baris perintah semu menjadi argumen berdasarkan aturan C / C ++ yang dikutip di atas, proposal @TSlivede juga akan berfungsi pada platform Unix.

Karena https://github.com/PowerShell/PowerShell/issues/4358 ditutup sebagai duplikat dari ini, berikut ringkasan singkat dari masalah itu:

Jika argumen dari executable eksternal dengan garis miring terbalik berisi spasi, itu saat ini dikutip secara naif (tambahkan kutipan sebelum dan sesudah argumen). Semua yang dapat dieksekusi, yang mengikuti aturan biasa mengartikannya seperti ini:
Dari @ mklement0 's komentar :

" di ".\test 2\" , karena didahului oleh \ ditafsirkan sebagai "escaped", menyebabkan sisa string - meskipun ada penutup yang hilang "untuk diinterpretasikan sebagai bagian dari argumen yang sama.

Contoh:
(dari komentar @akervinen )

PS X:\scratch> .\ps-args-test.exe '.\test 2\'
Argumen yang diterima: .\test 2"

Masalahnya sangat sering terjadi, karena PSReadLine menambahkan garis miring terbalik pada pelengkapan otomatis untuk direktori.

Karena corefx tampaknya terbuka untuk memproduksi api yang kita butuhkan, saya menunda ini ke 6.1.0. Untuk 6.0.0, saya akan melihat apakah kita dapat memperbaiki # 4358

@TSlivede Saya mengambil fungsi Anda, mengganti namanya menjadi Invoke-NativeCommand (karena Run bukan kata kerja yang valid) dan menambahkan alias ^ dan menerbitkannya sebagai modul di PowerShellGallery:

install-module NativeCommand -scope currentuser
^ ./echoargs 'A "B' 'A" B'

@ SteveL-MSFT:

Sangat menyenangkan memiliki sementara, tetapi yang tidak terlalu rumit adalah - sementara kami menunggu solusi CoreFX - untuk mengimplementasikan aturan kutipan / argumen-parsing resmi yang terdefinisi dengan baik seperti yang dijelaskan dalam proposal RFC @TSlivede sendiri sebelumnya - yang tidak Kedengarannya tidak terlalu sulit untuk dilakukan.

Jika kita hanya memperbaiki masalah \" , pengoperan argumen pada dasarnya masih rusak, bahkan dalam skenario sederhana seperti berikut:

PS> bash -c 'echo "hi there"'
hi    # !! Bash sees the following tokens:  '-c', 'echo hi', 'there'

Saya pikir pada titik ini ada kesepakatan yang cukup tentang perilaku apa yang seharusnya jadi kita tidak memerlukan proses RFC secara penuh, bukan?

Satu-satunya keputusan yang belum diselesaikan adalah bagaimana menangani masalah kompatibilitas-mundur di _Windows_.

@ mister0 @ SteveL-MS
Apakah kompatibilitas kita sudah rusak?

Satu-satunya keputusan luar biasa adalah bagaimana menangani masalah kompatibilitas ke belakang di Windows.

Ya, tapi itu bagian yang sulit, bukan?

@ be5invis apa yang Anda maksud dengan "kompatibilitas yang sudah rusak"?

Plus, jika CoreFX berada di ambang perbaikan di lapisan mereka, saya lebih suka tidak membuat celah sementara di lapisan kami sebelum mereka melakukannya.

Dan seperti yang dikatakan seseorang di atas di utas, ini menjengkelkan, tetapi juga didokumentasikan dengan cukup baik di komunitas. Saya tidak yakin kami harus memecahkannya dua kali dalam dua rilis berikutnya.

@joeyaello :

Bukankah perbaikan untuk # 4358 sudah merupakan perubahan besar bagi mereka yang telah mengatasi masalah ini dengan menggandakan \ ; mis., "c:\tmp 1\\" ? Dengan kata lain: jika Anda membatasi perubahan pada perbaikan ini, _two_ perubahan yang melanggar dijamin: ini sekarang, dan yang lain nanti setelah beralih ke CoreFx API di masa mendatang; dan sementara _bisa_ itu juga terjadi jika stopgap lengkap akan diterapkan sekarang, hal itu tidak mungkin terjadi, mengingat apa yang kami ketahui tentang perubahan yang akan datang ini.

Sebaliknya, ini dapat menghambat adopsi di Unix jika skenario kutipan umum seperti
bash -c 'echo "hi there"' tidak berfungsi dengan baik.

Namun, saya menyadari bahwa memperbaiki ini adalah perubahan yang jauh lebih besar.

@ PowerShell / powershell-committee membahas hal ini dan menyetujui bahwa minimal, menggunakan --% harus memiliki perilaku yang sama seperti bash di mana tanda kutip di-escape sehingga perintah asli menerimanya. Apa yang masih terbuka untuk diperdebatkan adalah apakah ini harus menjadi perilaku default tanpa menggunakan --%

catatan:

  • Saya berasumsi bahwa panggilan ke shell aktual yang dapat dieksekusi diperlukan saat menggunakan --% di _Unix_, sebagai lawan dari mencoba _emulate_ perilaku shell, yang terjadi pada _Windows_. Meniru tidak sulit di Windows, tetapi akan jauh lebih sulit di Unix, mengingat lebih banyak fitur yang perlu ditiru.

  • Menggunakan shell yang sebenarnya kemudian menimbulkan pertanyaan shell apa yang akan digunakan: sementara bash ada di mana-mana, perilaku defaultnya tidak sesuai dengan POSIX juga tidak diperlukan oleh POSIX, jadi untuk portabilitas bahasa skrip lain memanggil /bin/sh , shell yang dapat dieksekusi yang ditetapkan oleh POSIX (yang _dapat_ menjadi Bash yang berjalan dalam mode kompatibilitas (misalnya, di macOS), tetapi tentu saja tidak harus (misalnya, Dash di Ubuntu)).

Bisa dibilang, kita harus menargetkan /bin/sh juga - yang, bagaimanapun, berarti bahwa beberapa fitur Bash - terutama ekspansi brace, variabel otomatis tertentu, ... - tidak akan tersedia


Penggunaan --%

Saya akan menggunakan perintah echoargs --% 'echo "hi there"' sebagai contoh di bawah ini.

perilaku yang sama seperti bash di mana tanda kutip di-escape sehingga perintah native menerimanya.

Cara untuk melakukan _ di masa mendatang, setelah CoreFX API diperpanjang_ adalah dengan tidak melakukan pelolosan sama sekali, dan sebagai gantinya lakukan hal berikut:

  • Buat proses sebagai berikut:

    • /bin/sh sebagai dapat dieksekusi, (efektif) ditetapkan ke ProcessStartInfo.FileName .

    • Array berikut dari token argumen _literal_, _individual_ sebagai ProcessStartInfo.ArgumentList :

    • -c sebagai argumen pertama

    • echoargs 'echo "hi there"' sebagai argumen ke-2 - yaitu, baris perintah asli yang digunakan _literally_, persis seperti yang ditentukan, kecuali bahwa --% telah dihapus.

Akibatnya, baris perintah diteruskan melalui _as-is_ ke shell yang dapat dieksekusi, yang kemudian dapat melakukan penguraian _its_.

Saya memahami bahwa, saat ini tidak ada cara berbasis array untuk menyampaikan argumen literal, kita perlu menggabungkan -c dan echoargs 'echo "hi there"' menjadi string _single_ _ dengan escaping_, sayangnya _solely untuk kepentingan CoreFX API_, yang, ketika tiba waktunya untuk membuat proses sebenarnya, lalu _reverses_ langkah ini dan membagi string tunggal kembali menjadi token literal - dan memastikan bahwa pembalikan ini selalu menghasilkan daftar asli token literal adalah bagian yang menantang.

Sekali lagi: Satu-satunya alasan untuk melibatkan pelarian di sini adalah karena batasan CoreFX saat ini.
Untuk bekerja dengan batasan ini, string tunggal berikut ini harus ditetapkan ke properti .Arguments dari instance ProcessStartInfo , dengan pelolosan dilakukan seperti yang ditentukan oleh Parsing C ++ Command-Line Arguments :

  • /bin/sh sebagai dapat dieksekusi, (efektif) ditetapkan ke ProcessStartInfo.FileName .
  • String tunggal yang lolos berikut ini sebagai nilai ProcessStartInfo.Arguments :
    -c "echoargs 'echo \"hi there\"'"

## Perilaku default

Yang masih terbuka untuk diperdebatkan adalah jika ini adalah perilaku default tanpa menggunakan -%

Perilaku default di Unix harus sangat berbeda:

  • Tidak ada pertimbangan melarikan diri _lain dari milik PowerShell_ yang harus ikut bermain (kecuali di _Windows_, di mana hal itu tidak dapat dihindari, sayangnya; tapi di sana aturan MS C ++ adalah cara untuk pergi, _ untuk diterapkan di belakang layar_; jika gagal, --% menyediakan pintu keluar masuk).

  • Apa pun argumen yang diakhiri _PowerShell_, setelah penguraiannya sendiri, harus diteruskan sebagai _array literals_ , melalui properti ProcessStartInfo.ArgumentList .

Diterapkan ke contoh tanpa --% : echoargs 'echo "hi there"' :

  • PowerShell melakukan penguraian yang biasa dan berakhir dengan 2 argumen berikut:

    • echoargs
    • echo "hi there" (tanda kutip tunggal - yang hanya memiliki fungsi sintaksis untuk _PowerShell_, dihapus)
  • ProcessStartInfo kemudian diisi sebagai berikut, dengan ekstensi CoreFX yang akan datang:

    • echoargs sebagai nilai properti .FileName (efektif)
    • _Literal_ echo "hi there" sebagai satu-satunya elemen untuk ditambahkan ke instance Collection<string> diekspos oleh .ArgumentList .

Sekali lagi, jika tidak ada .ArgumentList yang bukan merupakan opsi _yet_, tetapi _ untuk sementara_ pelolosan tambahan yang sesuai dengan MS C ++ yang sama seperti dijelaskan di atas dapat digunakan.

@ SteveL-MS
Seperti yang telah saya sebutkan di Buat simbol penguraian berhenti (-%) berfungsi di Unix (# 3733), saya sangat menyarankan agar tidak mengubah perilaku --% .

Jika beberapa fungsi khusus untuk /bin/sh -c diperlukan gunakan simbol yang berbeda dan meninggalkan --% cara itu!

@Tslivede :

_Jika_ sesuatu --% -_like_ diimplementasikan di Unix - dan dengan native globbing dan umumnya lebih banyak orang yang paham baris perintah di Unix, saya merasa kurang membutuhkannya - kemudian memilih simbol _different_ - seperti --$ - mungkin masuk akal (maaf, saya lupa semua aspek dari debat multi-isu yang panjang ini).

Simbol yang berbeda juga berfungsi sebagai pengingat yang mencolok secara visual bahwa perilaku _platform-specific_ non-portabel sedang digunakan.

Itu meninggalkan pertanyaan apa yang harus dilakukan PowerShell ketika menemukan --% di Unix dan --$ di Windows.

Saya baik-baik saja meninggalkan --% apa adanya. Memperkenalkan sesuatu seperti --$ yang memanggil / bin / sh dan saya kira cmd.exe di Windows mungkin cara yang baik untuk menyelesaikan ini.

Tidak ada kesempatan untuk membuat cmdlet untuk perilaku ini?

@iSazonov, apakah Anda menyarankan sesuatu seperti Invoke-Native ? Tidak yakin saya penggemar itu.

Ya, seperti Start-Native .
Sebagai lelucon :-), apakah Anda tidak suka cmdlet di PowerShell?

Di Build.psm1 kami memiliki Start-NativeExecution dengan tautan ke https://mnaoumov.wordpress.com/2015/01/11/execution-of-external-commands-in-powershell-done-right/

@ SteveL-MS

Saya baik-baik saja pergi -% apa adanya.

Saya pikir kita semua setuju bahwa --% harus terus berperilaku seperti itu di _Windows_.

Sebaliknya, di _Unix_, perilaku ini tidak masuk akal, seperti yang saya coba tunjukkan di sini - singkatnya:

  • tanda kutip tunggal tidak ditangani dengan benar
  • satu-satunya cara untuk mereferensikan variabel lingkungan adalah sebagai _cmd.exe-style_ ( %var% )
  • fitur asli yang penting seperti globbing dan word-splitting tidak berfungsi.

Motivasi utama untuk memperkenalkan --% adalah, jika saya mengerti dengan benar, untuk mengaktifkan _reuse dari cmd.exe command lines_ as-is.

  • Dengan demikian, --% tidak berguna di Unix dengan perilakunya saat ini.
  • _Jika_ kami menginginkan fitur _analogous_ di Unix, fitur itu harus berbasis /bin/sh -c , seperti yang diusulkan di atas, mungkin menggunakan simbol yang berbeda.

Saya tidak berpikir ada kebutuhan untuk fitur berbasis cmd /c pada Windows, karena --% telah _mostly_ tertutupi, bisa dibilang dengan cara yang _good enough_.
@TSlivede telah menunjukkan bahwa tidak semua fitur shell ditiru, tetapi dalam praktiknya hal itu tampaknya tidak menjadi perhatian (misalnya, substitusi nilai variabel seperti %envVar:old=new% tidak didukung, ^ bukan karakter pelarian, dan penggunaan --% terbatas pada perintah _single_ - tidak menggunakan operator pengalihan dan kontrol cmd.exe ; yang mengatakan, saya tidak berpikir --% pernah dimaksudkan untuk meniru seluruh perintah _lines_).

Dengan demikian, sesuatu seperti --$ - jika diterapkan - akan menjadi Unix _counterpart_ menjadi --% .

Bagaimanapun, setidaknya topik about_Parsing help layak mendapatkan peringatan yang mencolok bahwa --% tidak akan berguna di Unix dalam semua kecuali beberapa kasus.

@iSazonov Mungkin masuk akal untuk memiliki Start-Native untuk menangani beberapa skenario tertentu, tetapi kita harus mencoba meningkatkan PowerShell sehingga menggunakan biaya asli lebih alami dan dapat diprediksi

@ PowerShell / powershell-committee meninjau ini dan setuju bahwa --% berarti memperlakukan argumen seperti yang mereka lakukan pada platform mereka yang relevan yang berarti mereka berperilaku berbeda di Windows dan Linux, tetapi konsisten dalam Windows dan konsisten dalam Linux. Akan lebih membingungkan bagi pengguna untuk memperkenalkan sigil baru. Kami akan menyerahkan implementasi kepada insinyur tentang cara mengaktifkan ini.

Skrip lintas platform harus menyadari perbedaan perilaku ini, tetapi tampaknya tidak mungkin pengguna akan mencapai ini. Jika umpan balik pengguna adalah bahwa ada kebutuhan karena lebih banyak penggunaan lintas platform, maka kita dapat mengunjungi kembali dengan memperkenalkan sigil baru.

Untuk kedua kalinya saya digigit oleh perbedaan parsing argumen string asli vs cmdlet ini saat digunakan ripgrep .

Ini adalah hasil dari panggilan PowerShell (echo.exe berasal dari "C: \ Program Files \ Git \ usrbin \ echo.exe")

Sekarang saya tahu saya harus berhati-hati dengan permainan unik " :

> echo.exe '"test'
test

Tapi keanehan ini di luar jangkauan saya ...

echo.exe '^\+.+;'
^\+.+;
echo.exe '^\+.*;'
^+.*;

Dalam kasus kedua saya perlu menempatkan dua kali lipat \ untuk mengirimkan \ ke perintah asli, dalam kasus pertama saya tidak perlu melakukannya 😑

Saya mengerti ini akan menjadi perubahan yang mengganggu untuk mengubah perilaku ini sehingga tidak akan ada perbedaan antara cmlet dan perintah asli. Namun, menurut saya kebiasaan seperti ini adalah sesuatu yang membuat orang tidak bisa menggunakan PowerShell sebagai shell default.

@mpawelski Saya baru saja mencoba ini di kotak Windows saya dengan git 2.20.1.vfs.1.1.102.gdb3f8ae dan tidak merepro untuk saya dengan 6.2-RC.1. Menjalankannya beberapa kali dan secara konsisten menggemakan ^\+.+;

@ SteveL-MSFT, saya pikir @mpawelski secara tidak sengaja menetapkan perintah yang sama dua kali. Anda akan melihat masalah jika Anda mengirimkan '^\"+.*;' misalnya - perhatikan bagian \" - dengan ekspektasi - wajar - bahwa konten string yang dikutip tunggal akan diteruskan sebagaimana adanya , sehingga program target eksternal melihat ^\"+.*; sebagai nilai argumen:

# Note how the "\" char. is eaten.
PS> bash -c 'printf %s "$1"' - '^\"+.*;'
^"+.*; 

#"# Running the very same command from Bash does NOT exhibit the problem:
$ bash -c 'printf %s "$1"' - '^\"+.*;'
^\"+.*; 

-% adalah, jika saya mengerti dengan benar, untuk mengaktifkan penggunaan kembali baris perintah cmd.exe yang ada sebagaimana adanya.

Tidak cukup. --% diperkenalkan karena banyak utilitas baris perintah Windows menggunakan argumen yang diinterpretasikan oleh PowerShell yang sepenuhnya memfubasikan pemanggilan utilitas. Hal ini dapat membuat orang merasa sedih dengan cepat jika mereka tidak dapat lagi dengan mudah menggunakan utilitas asli favorit mereka. Jika saya memiliki seperempat untuk setiap kali saya menjawab pertanyaan tentang SO dan di tempat lain RE perintah exe asli yang tidak berfungsi dengan baik karena masalah ini, saya mungkin bisa mengajak keluarga keluar untuk makan malam di Qoba. :-) Misalnya, tf.exe memungkinkan ; sebagai bagian dari menentukan ruang kerja. Git mengizinkan {} dan @~1 , dll, dll, dll.

--% telah ditambahkan untuk memberitahu PowerShell agar TIDAK mengurai sisa baris perintah - kirimkan saja "apa adanya" ke exe asli. Dengan satu gosokan, izinkan variabel menggunakan sintaks cmd env var. Ini agak jelek tapi man, itu benar-benar berguna masih di Windows.

RE membuat ini cmdlet, saya tidak yakin saya melihat cara kerjanya. --% adalah sinyal bagi parser untuk membodohi penguraian hingga EOL ..

Sejujurnya, sebagai pengguna lama PowerShell dan fitur ini khususnya, masuk akal bagi saya untuk menggunakan operator yang sama di platform lain dengan maksud sederhana - bodohkan parsing hingga EOL. Ada masalah tentang bagaimana mengizinkan beberapa bentuk substitusi variabel. Meskipun rasanya agak jelek, di macOS / Linux Anda dapat menggunakan% envvar% dan mengganti nilai env var yang sesuai. Kemudian bisa portabel antar platform.

Masalahnya, jika Anda tidak melakukan ini maka Anda akan berakhir dengan kode bersyarat - tidak persis seperti yang saya sebut portabel:

$env:Index = 1
if ($IsWindows) {
    git show --% @~%Index%
}
else {
    git show --$ @~$Index
}

Saya lebih suka pekerjaan ini di semua platform:

$env:Index = 1
git show --% @~%Index%

Perilaku di Windows harus tetap apa adanya karena kompatibilitas.

@bayu_joo

Tidak cukup.

Dengan proses eliminasi, baris perintah yang mendahului PowerShell di dunia Windows ditulis untuk cmd.exe - dan itulah yang ingin ditiru oleh

  • --% memperluas cmd.exe -style %...% referensi variabel lingkungan, seperti %USERNAME% (jika tidak ada shell dan tidak ada logika khusus yang terlibat, token tersebut akan diteruskan _kata demi kata_)

  • langsung dari mulut PowerShell (jika Anda mau; penekanan ditambahkan):

Web ini penuh dengan baris perintah yang ditulis untuk Cmd.exe . Baris perintah ini bekerja cukup sering di PowerShell, tetapi ketika mereka menyertakan karakter tertentu, misalnya tanda titik koma (;) dolar ($), atau kurung kurawal, Anda harus membuat beberapa perubahan, mungkin menambahkan beberapa tanda kutip. Ini tampaknya menjadi sumber dari banyak sakit kepala ringan.

Untuk membantu mengatasi skenario ini, kami menambahkan cara baru untuk "keluar" dari penguraian baris perintah. Jika Anda menggunakan parameter ajaib -%, kami menghentikan penguraian normal baris perintah Anda dan beralih ke sesuatu yang jauh lebih sederhana. Kami tidak mencocokkan kutipan. Kami tidak berhenti di titik koma. Kami tidak memperluas variabel PowerShell. Kami memperluas variabel lingkungan jika Anda menggunakan sintaks Cmd.exe (misalnya% TEMP%). Selain itu, argumen hingga akhir baris (atau pipa, jika Anda menyalurkan) diteruskan apa adanya. Berikut ini contohnya:

Perhatikan bahwa pendekatan ini kurang cocok untuk dunia _Unix_ (ringkasan dari atas):

  • Globbing argumen tanpa tanda kutip seperti *.txt tidak akan berfungsi.

  • Kerang tradisional (seperti POSIX) di dunia Unix tidak _not_ menggunakan %...% untuk merujuk ke variabel lingkungan; mereka mengharapkan sintaks $... .

  • Ada (untungnya) tidak ada baris perintah _raw_ di dunia Unix: eksekusi eksternal apa pun harus diteruskan _array_ dari _literals_, jadi masih PowerShell atau CoreFx yang harus mengurai baris perintah menjadi argumen terlebih dahulu.

  • Kerang tradisional (seperti POSIX) di dunia Unix menerima string '...' (tanda kutip tunggal), yang tidak dikenali --% - lihat # 10831.

Tetapi bahkan di dunia Windows --% memiliki batasan yang parah dan tidak jelas :

  • Yang paling jelas, Anda tidak dapat secara langsung mereferensikan variabel PowerShell di baris perintah Anda jika Anda menggunakan --% ; satu-satunya solusi - rumit - adalah dengan mendefinisikan variabel _environment_ untuk sementara, yang kemudian harus Anda referensikan dengan %...% .
  • Anda tidak dapat menyertakan baris perintah dalam (...) - karena penutupan ) diartikan sebagai bagian literal dari baris perintah.
  • Anda tidak dapat mengikuti baris perintah dengan ; dan pernyataan lain - karena ; ditafsirkan sebagai bagian literal dari baris perintah.
  • Anda tidak dapat menggunakan --% di dalam blok skrip baris tunggal - karena penutupan } ditafsirkan sebagai bagian literal dari baris perintah.
  • Anda tidak dapat menggunakan pengalihan - karena mereka diperlakukan sebagai bagian literal dari baris perintah - namun, Anda dapat menggunakan cmd --% /c ... > file , untuk membiarkan cmd.exe menangani pengalihan.
  • Anda tidak dapat menggunakan karakter kelanjutan baris - baik PowerShell ( ` ) maupun cmd.exe ( ^ ) - tidak akan diperlakukan sebagai _literals_.

    • --% hanya akan mem-parsing (paling banyak) ke akhir baris.


Untungnya, kami sudah _do_ memiliki sintaks lintas platform: sintaks _PowerShell sendiri_.

Ya, menggunakan itu mengharuskan Anda untuk mengetahui apa yang _PowerShell_ anggap sebagai karakter meta, yang merupakan _superset_ dari kerangka cmd.exe dan mirip POSIX seperti Bash yang dianggap sebagai meta karakter, tetapi itu adalah harga yang harus dibayar untuk platform yang lebih kaya, platform- pengalaman baris perintah agnostik.

Betapa malangnya, kemudian, PowerShell menangani kutipan " karakter dengan sangat buruk - yang merupakan subjek utama dari masalah ini, dan yang dirangkum dalam terbitan dokumen ini .

@rkeithhill , saya mulai sedikit menyinggung; izinkan saya mencoba menutupnya:

  • --% sebenarnya sudah _is_ diimplementasikan pada PowerShell Core 6.2.0; misalnya,
    /bin/echo --% %HOME% mencetak nilai variabel lingkungan HOME ; sebaliknya,
    /bin/ls --% *.txt tidak akan berfungsi seperti yang diharapkan, karena *.txt diteruskan sebagai literal.

  • Pada akhirnya, ketika tidak menggunakan --% , kita perlu membantu pengguna mendiagnosis seperti apa larik baris perintah / argumen yang dibangun _ di belakang layar pada akhirnya_ terlihat (yang membawa kita kembali ke yang terhormat # 1761):

    • echoArgs.exe melakukan hal itu, dan dalam masalah terkait Anda secara bijaksana meminta agar fungsionalitas tersebut menjadi bagian dari PowerShell itu sendiri.
    • @ SteveL-MSFT merenungkan termasuk baris perintah yang dihasilkan dalam catatan kesalahan.

Akhirnya, untuk menerapkan argumen saya sebelumnya - menggunakan sintaks PowerShell sendiri sebagai yang secara inheren portabel - ke contoh Anda:

# Works cross-platform, uses PowerShell syntax 
# Note: No need for an aux. *environment* variable (which should be cleaned up afterward)
$Index = 1
git show "@~$Index"

# Alternative, quoting just the '@'
git show `@~$Index

Ya, Anda harus mengetahui bahwa token-initial @ adalah karakter meta yang harus Anda kutip, tetapi karena PowerShell menjadi lebih banyak digunakan, kesadaran akan persyaratan semacam itu juga akan semakin meluas.

FYI, masalah ini seharusnya hampir sama di Windows dan Unix-like. Implementasi CoreFx dari Unix SDProcess memiliki sesuatu yang disebut ParseArgumentsIntoList , yang mengimplementasikan CommandLineToArgvW hampir persis tanpa _setargv switch (dan dengan "" tidak berdokumen dalam tanda kutip → " fitur). Unix seharusnya tidak menjadi titik sakit tambahan dalam hal ini karena dalam bentuk saat ini rusak dengan cara yang sama seperti Windows.

_setargv bukanlah sesuatu yang digunakan oleh setiap program, dan mungkin tidak layak untuk dipertimbangkan karena, yah, ini agak dipengaruhi oleh perubahan perilaku di antara versi CRT . Yang terbaik yang bisa dan harus kita lakukan adalah mengelilingi semuanya dengan tanda kutip ganda, menambahkan beberapa backslah yang bagus, dan itu saja.

Contoh lain ketika args tidak diurai dengan benar:

az "myargs&b"

Dalam kasus ini, az mendapatkan myargs dan b dicoba untuk dieksekusi sebagai perintah baru.

Solusinya adalah: az -% "myargs & b"

@ SteveL-MSFT, kita dapat menghapus label Waiting - DotNetCore , dengan mempertimbangkan bahwa fitur yang diperlukan - properti ProcessStartInfo.ArgumentList berbasis koleksi telah tersedia sejak .NET Core 2.1

@TSlivede mengetahui metode baru dan berencana untuk menggunakannya, tetapi RFC terkait, https://github.com/PowerShell/PowerShell-RFC/pull/90 , sayangnya.

Saya sarankan kita melanjutkan diskusi _implementation_ di sana.

Namun, dalam diskusi RFC, @joeyaiello berbicara tentang menjadikan perubahan sebagai fitur eksperimental, tetapi menjadi semakin jelas bagi saya bahwa Anda tidak dapat memperbaiki perilaku kutipan tanpa merusak kode yang ada secara masif:

Siapapun yang harus _bekerja di sekitar_:

  • ketidakmampuan untuk meneruskan argumen string kosong ( foo.exe "" saat ini melewati argumen _no_)
  • penghapusan tanda kutip ganda yang tidak terduga dan efektif karena kurangnya pelolosan otomatis dari tanda kutip ganda yang disematkan ( foo.exe '{ "foo": "bar" }' diloloskan sebagai pelarian yang tidak benar "{ "foo": "bar" }" )
  • kebiasaan CLI tertentu seperti msiexec yang tidak menerima argumen tertentu yang dikutip ganda _secara keseluruhan_ ( foo.exe foo="bar none" disahkan sebagai "foo=bar none" ).
    Catatan: Ini adalah msiexec yang harus disalahkan di sini, dan dengan perubahan yang diusulkan diterapkan, meneruskan formulir foo="bar none" diperlukan kemudian akan membutuhkan --% .

akan bermasalah, karena solusi akan _break_ dengan perubahan yang diusulkan diterapkan.

Oleh karena itu, pertanyaan tambahannya adalah:

  • Bagaimana kami dapat membuat perilaku yang benar tersedia setidaknya sebagai fitur _opt-in_?

    • Dalam kasus kutipan terputus dengan Start-Process , setidaknya kita memiliki opsi untuk memasukkan parameter baru untuk perilaku yang benar , tetapi dengan pemanggilan langsung yang jelas bukan merupakan opsi (kecuali jika kita memperkenalkan "simbol ajaib" lain seperti sebagai --% ).
  • Masalah mendasar dengan mekanisme seperti itu - biasanya, berdasarkan variabel preferensi, tetapi juga meningkat oleh pernyataan using - adalah pelingkupan dinamis PowerShell; artinya, perilaku keikutsertaan secara default akan diterapkan ke kode yang disebut _from_ kode yang diikutsertakan seseorang juga, yang bermasalah.

    • Mungkin sudah waktunya untuk secara umum memperkenalkan fitur pelingkupan _lexical_, generalisasi dari proposal using strict dicakup secara leksikal dalam - sama-sama mendekam - RFC mode ketat leksikal yang dibuat oleh @lzybkr.

    • Sesuatu seperti using preference ProperArgumentQuoting dengan cakupan leksikal? (Nama jelas bisa dinegosiasikan, tapi saya kesulitan untuk membuatnya).

Mengingat semua peringatan itu, dan ini juga merupakan masalah dengan pemanggilan langsung, di mana kita tidak bisa begitu saja menambahkan parameter baru, saya sangat mendukung untuk melanggar perilaku lama.

Ya, mungkin akan rusak banyak. Tapi sungguh, hanya karena itu benar-benar rusak untuk memulai. Menurut saya tidak layak untuk memprioritaskan mempertahankan apa yang merupakan tumpukan besar solusi untuk perilaku rusak daripada memiliki fitur yang _ benar-benar berfungsi_.

Sebagai versi mayor baru, saya pikir v7 benar-benar satu-satunya kesempatan kita akan memperbaiki situasi ini dengan benar untuk beberapa waktu, dan kita harus mengambil kesempatan itu. Asalkan kami membuat pengguna sadar, menurut saya transisi tidak akan diterima dengan baik secara keseluruhan.

Jika kami merasa bahwa perubahan yang tidak dapat dihindari tidak dapat dihindari, maka mungkin kami harus merancang solusi yang ideal, dengan mempertimbangkan bahwa itu harus mudah untuk dicetak dalam sesi interaktif dan dimungkinkan untuk memiliki versi skrip yang berfungsi lebih baik pada semua platform.

Setuju, @ vexx32 : Mempertahankan perilaku yang ada, meskipun hanya secara default, akan tetap menjadi titik sakit yang tak

Sebuah shell yang tidak dapat diandalkan meneruskan argumen ke program eksternal gagal dalam salah satu mandat intinya.

Anda pasti memiliki suara _my_ untuk membuat perubahan yang melanggar, tetapi saya khawatir orang lain akan merasa berbeda, paling tidak karena v7 disebut-sebut memungkinkan pengguna WinPS jangka panjang untuk bermigrasi ke PSCore.


@iSazonov : https://github.com/PowerShell/PowerShell-RFC/pull/90 menjelaskan solusi yang benar.

Untuk meringkas semangatnya:

PowerShell, sebagai shell, perlu mengurai argumen sesuai dengan aturan _its_ dan kemudian meneruskan nilai argumen yang diperluas _verbatim_ ke program target - pengguna tidak perlu memikirkan bagaimana PowerShell membuatnya terjadi; yang harus mereka khawatirkan adalah mendapatkan sintaks _PowerShell_ yang benar.

  • Pada platform mirip Unix, ProcessStartInfo.ArgumentList sekarang memberi kita cara untuk mengimplementasikan ini dengan sempurna, mengingat bahwa _array_ nilai argumen yang diperluas dapat diteruskan _as-is_ ke program target, karena begitulah cara passing argumen - secara masuk akal - bekerja di dunia ini.

  • Pada Windows, kita harus berurusan dengan kenyataan yang tidak menguntungkan dari anarki yaitu penguraian baris perintah Windows , tetapi, sebagai shell, kita harus _hanya membuatnya bekerja di belakang layar, sebanyak mungkin_, itulah yang disebut RFC. menjelaskan - meskipun saya baru saja menemukan kerutan yang membuat penggunaan ProcessStartInfo.ArgumentList tidak cukup baik, sayangnya (karena meluasnya _batch files_ sebagai titik masuk CLI, seperti yang ditunjukkan oleh @ SteveL-MSFT az[.cmd] contoh di atas). Untuk kasus-kasus edge di mana melakukan hal yang masuk akal tidak cukup baik, ada --% .

Mungkin PSSA dapat membantu mengurangi perubahan yang mengganggu dengan memperingatkan pengguna bahwa mereka menggunakan format argumen yang akan diubah.

Saya pikir kita perlu mempertimbangkan untuk mengadopsi sesuatu seperti Fitur Opsional untuk melanjutkan perubahan yang melanggar ini bersama dengan beberapa lainnya .

Apakah memang ada nilai dalam mempertahankan perilaku yang ada? Selain kompatibilitas ke belakang, maksud saya.

Saya rasa tidak ada gunanya mempertahankan dua jalur kode untuk ini, hanya untuk mempertahankan implementasi yang rusak karena beberapa kode lama mungkin memerlukannya sesekali. Saya rasa tidak masuk akal mengharapkan folx untuk memperbarui kode mereka sesekali. 😅

Jadi ganda jika kita mengharapkan PS7 menjadi pengganti untuk WinPS; satu-satunya alasan saya dapat melihat untuk mempertahankan perilaku untuk v7 adalah jika kita mengharapkan orang-orang menggunakan skrip yang sama untuk menjalankan perintah pada 5.1 dan 7, yang (semoga) akan menjadi kasus yang cukup langka jika PS7 merupakan pengganti yang baik untuk 5.1.

Dan bahkan kemudian, tidak akan terlalu sulit bagi pengguna untuk memperhitungkan keduanya. Asalkan kami tidak mengubah sintaks bahasa sebenarnya, seharusnya cukup mudah untuk melakukan sesuatu seperti ini:

if ($PSVersionTable.PSVersion.Major -lt 7) {
    # use old form
}
else {
    # use new form
}

Asalkan kami membuat pengguna menyadari perbedaannya, saya pikir itu akan melegakan dari rasa sakit karena menangani executable asli yang aneh di PS hingga sekarang. 😄

Seperti yang disebutkan @TylerLeonhardt dalam diskusi tentang fitur opsional - mengimplementasikannya berarti Anda sekarang mempertahankan _multiple_ implementasi berbeda yang masing-masing perlu dipelihara dan diuji, ditambah juga memelihara dan menguji framework fitur opsional. Sepertinya tidak layak untuk ini, tbh.

Kompatibilitas mundur https://github.com/PowerShell/PowerShell/issues/1761 dan https://github.com/PowerShell/PowerShell/issues/10675. "Memperbaiki" interoperabilitas dengan perintah asli adalah sesuatu yang ingin saya selesaikan untuk vNext. Jadi, jika ada yang melihat masalah yang ada atau baru dalam kategori ini, cc saya dan saya akan menandainya dengan benar (atau jika Anda memiliki izin triase, beri tag seperti yang lain).

Fitur opsional adalah modul :-) Memiliki fitur opsional di Engine sangat merepotkan untuk mendapatkan dukungan, khususnya untuk dukungan Windows. Kami dapat memodularisasi Engine dengan mengurangi dependensi internal dan mengganti API internal dengan publik - setelah ini kami dapat menerapkan fitur opsional di Engine dengan cara yang mudah.

@iSazonov salah satu hal yang akan dilihat tim saya di vNext adalah membuat mesinnya lebih modular :)

Apa solusi yang direkomendasikan di sini untuk pengguna akhir?

Ini dibuat-buat, tetapi ini adalah cara paling mudah untuk mendapatkan penanganan * ArgumentList yang benar dari kerangka .NET itu sendiri:

Add-Type -AssemblyName "System"
function startProcess([string] $FileName, [string[]] $ArgumentList) {
    $proc = ([System.Diagnostics.Process]::new())
    $proc.StartInfo.UseShellExecute = $false
    $proc.StartInfo.FileName = $FileName
    $proc.StartInfo.CreateNoWindow = $true
    foreach ($a in $ArgumentList) { $proc.StartInfo.ArgumentList.Add($a) }
    $proc.Start()
    return $proc
}

startProcess -FileName 'C:\Program Files\nodejs\node.exe' -ArgumentList '-e','console.log(process.argv.join(''\n''))','--','abc" \" messyString'

Tentu saja Anda dapat membuatnya kurang dibuat-buat untuk digunakan di sini dengan menggunakan parameter posisi dan beberapa tipuan get-command .

* DISCLAIMER: Tidak ada satu cara yang benar untuk mengurai cmdline di Windows. Yang saya maksud dengan "benar" adalah gaya MSVCRT dari cmdline dari / ke konversi argv, yang diimplementasikan oleh .NET pada semua platform untuk penanganan ArgumentList, pemrosesan main (string[] args) , dan pemanggilan bibit eksternal di Unix. Sampel ini disediakan SEBAGAIMANA ADANYA tanpa jaminan untuk interoperabilitas umum. Lihat juga bagian "baris perintah windows" dari dokumentasi NodeJS child_process yang diusulkan .

@ Artoria2e5 Itulah kesimpulan yang saya dapatkan. System.Diagnostics.Process adalah satu-satunya cara yang dapat diandalkan untuk menjalankan eksekusi eksternal, tetapi argumen pelolosan bisa menjadi rumit karena Aturan stdlib:

2N backslashes + " ==> N backslashes and begin/end quote
2N+1 backslashes + " ==> N backslashes + literal " 
N backslashes ==> N backslashes

Sebagai hasilnya saya telah menemukan logika berikut untuk menghindari argumen membungkusnya menjadi tanda kutip ganda " untuk eksekusi proses:
https://github.com/choovick/ps-invoke-externalcommand/blob/master/ExternalCommand/ExternalCommand.psm1#L244

Juga bisa menjadi rumit untuk mendapatkan STDOUT dan STDERR secara realtime sementara eksekusi eksternal berjalan, jadi saya telah membuat paket ini

https://github.com/choovick/ps-invoke-externalcommand

yang saya gunakan banyak pada Windows, Linux dan Mac dan sejauh ini tanpa masalah dan saya dapat menyampaikan argumen dengan baris baru dan karakter khusus lainnya di dalamnya.

GitHub
Berkontribusi pada pengembangan choovick / ps-invoke-externalcommand dengan membuat akun di GitHub.
GitHub
Berkontribusi pada pengembangan choovick / ps-invoke-externalcommand dengan membuat akun di GitHub.

@choovick semua hal pengalihan stdio hebat!

Saya sedikit tidak setuju pada bagian escaping, karena sudah ada hal yang melakukannya untuk Anda yang disebut ArgumentList. Saya mengerti bahwa ini adalah tambahan relatif baru (?), Dan itu mengecewakan karena MS lupa untuk meletakkan String, String [] penginisialisasi untuk SDProcessStartInfo. (Apakah ada tempat untuk… proposal Antarmuka NET ini?)


basa basi

Fungsi pelarian saya dari contoh NodeJS itu sedikit berbeda dari milik Anda: ia menggunakan pelarian tidak berdokumen (tetapi ditemukan di .NET core dan MSVCRT) "" escape untuk tanda kutip. Melakukannya seperti menyederhanakan pekerjaan pengambilan garis miring terbalik. Saya melakukan ini terutama karena itu digunakan untuk semua cmd perkasa, yang tidak memahami bahwa \" tidak boleh mencabut sisa string. Alih-alih berjuang dengan \^" saya berpikir bahwa saya akan lebih baik dengan sesuatu yang telah digunakan secara rahasia sejak awal waktu.

@ Artoria2e5 sayangnya ArgumentList tidak tersedia di PowerShell 5.1 di windows, menggunakan contoh Anda, saya mendapatkan:

`` Anda tidak dapat memanggil metode pada ekspresi bernilai null.
Di C: Pengguna \ yser \ dev \ test.ps1: 7 karakter: 37

  • ... oreach ($ a di $ ArgumentList) {$ proc.StartInfo.ArgumentList.Add ($ a)}
  • ~ ~ ~ ~ ~ ~ ~ ~

    • CategoryInfo: InvalidOperation: (:) [], RuntimeException

    • FullyQualifiedErrorId: InvokeMethodOnNull

      ``

Sejak argumen kustom lolos dari logika ....

basa basi

sehubungan dengan `\ ^" `di NodeJS, saya rasa saya harus melakukannya beberapa tahun yang lalu :) dan menurut saya berhasil

System.Diagnostics.Process adalah satu-satunya cara yang dapat diandalkan untuk menjalankan executable eksternal

Masalahnya adalah Anda tidak akan mendapatkan integrasi dengan aliran keluaran PowerShell dan Anda tidak akan mendapatkan perilaku streaming dalam pipeline.

Berikut ringkasan solusi yang diperlukan jika Anda masih ingin membiarkan PowerShell melakukan pemanggilan (yang pasti lebih disukai) :

  • Jika Anda perlu meneruskan argumen dengan _embedded_ " chars., _Double_ mereka di Windows, jika memungkinkan, atau \ -escape mereka:

    • Di Windows, saat memanggil _batch files_ dan jika Anda tahu bahwa program target memahami "" sebagai escaped " , gunakan $arg -replace '"', '""'

      • Penggunaan "" lebih disukai di Windows (ini menghindari masalah Windows PowerShell dan bekerja dengan CLI yang menggunakan file batch sebagai _stubs_, seperti Node.js dan Azure), tetapi tidak semua file yang dapat dieksekusi mendukungnya (terutama tidak Ruby dan Perl).
    • Jika tidak (selalu di Unix), gunakan $arg -replace '"', '\"'

    • Catatan: Di _Windows PowerShell_, ini masih tidak selalu berfungsi dengan baik jika nilainya juga berisi _spaces_, karena kehadiran literal \" dalam nilai secara situasional tidak _not_ memicu kutip ganda, tidak seperti di PowerShell Core; misalnya, melewati istirahat '3\" of snow' .

    • Selain itu, sebelum pelolosan di atas, Anda harus menggandakan \ tepat sebelum " , jika mereka diperlakukan sebagai literal:

      • $arg = $arg -replace '(\\+)"', '$1$1"'
  • Jika Anda perlu meneruskan argumen _empty_, berikan '""' .

    • '' -eq $arg ? '""' : $arg (Alternatif WinPS: ($arg, '""')['' -eq $arg]
  • Hanya Windows PowerShell, jangan lakukan ini di PS Core (di mana masalah telah diperbaiki):

    • Jika argumen Anda _mengandung spasi_ dan _ diakhiri dengan_ (satu atau lebih) \ , gandakan \ instance.

      • if ($arg -match ' .*?(\\+)$') { $arg = $arg + $Matches[1] }
  • Jika cmd / file batch sedang dipanggil dengan argumen yang _not_ memiliki spasi (karena itu _not_ memicu kutipan ganda otomatis oleh PowerShell) tetapi berisi &|<>^,; (mis., a&b ), gunakan _embedded melampirkan kutip ganda_ untuk memastikan bahwa PowerShell meneruskan token kutip ganda dan oleh karena itu tidak merusak panggilan cmd / batch-file:

    • $arg = '"' + $arg + '"'
  • Jika Anda perlu berurusan dengan file executable yang berperilaku buruk seperti msiexec.exe , kutipan tunggal argumennya:

    • 'foo="bar none"'

Seperti yang dinyatakan, solusi ini akan _break_, setelah masalah yang mendasarinya diperbaiki.


Di bawah ini adalah fungsi sederhana (non-lanjutan) iep (untuk "memanggil program eksternal"), yang:

  • Melakukan semua pelolosan yang dijelaskan di atas, termasuk casing khusus otomatis untuk msiexec dan lebih memilih "" keluar di atas \" tergantung pada program target.

    • Idenya adalah Anda dapat meneruskan argumen apa pun dengan berfokus hanya pada sintaks string _PowerShell_, dan mengandalkan fungsi untuk melakukan pelolosan yang diperlukan sehingga nilai verbatim yang dilihat PowerShell juga terlihat oleh program target.

    • Di PowerShell _Core_, ini akan bekerja cukup kuat; di Windows PowerShell Anda masih memiliki kasus tepi dengan tanda kutip ganda tertanam yang putus jika \" escaping harus digunakan (seperti dibahas di atas).

  • Mempertahankan sintaks pemanggilan perintah shell.

    • Cukup tambahkan iep  ke baris perintah Anda.

  • Seperti doa langsung, itu:

    • terintegrasi dengan aliran PowerShell

    • mengirimkan keluaran baris demi baris melalui pipa

    • menetapkan $LASTEXITCODE berdasarkan kode keluar program eksternal; namun, $? _not_ dapat diandalkan.

Catatan: Fungsi ini sengaja dibuat minimalis (tidak ada deklarasi parameter, tidak ada bantuan baris perintah, nama pendek (tidak beraturan)), karena ini dimaksudkan agar tidak mengganggu: cukup tambahkan iep ke baris perintah Anda, dan hal-hal lainnya harus bekerja.

Contoh pemanggilan menggunakan EchoArgs.exe (dapat diinstal melalui Chocolatey dari sesi _elevated_ dengan choco install echoargs -y ):

PS> iep echoargs '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'
Arg 0 is <>
Arg 1 is <a&b>
Arg 2 is <3" of snow>
Arg 3 is <Nat "King" Cole>
Arg 4 is <c:\temp 1\>
Arg 5 is <a \" b>

Command line:
"C:\ProgramData\chocolatey\lib\echoargs\tools\EchoArgs.exe" "" a&b "3\" of snow" "Nat \"King\" Cole" "c:\temp 1\\" "a \\\" b"

Di atas menunjukkan output PowerShell Core. Perhatikan bagaimana semua argumen diteruskan dengan benar seperti yang terlihat kata demi kata oleh PowerShell, termasuk argumen kosong.

Di Windows PowerShell, argumen 3" of snow tidak akan diteruskan dengan benar, karena \" escaping digunakan karena memanggil executable yang tidak diketahui (seperti dibahas di atas).

Untuk memverifikasi bahwa file batch meneruskan argumen dengan benar, Anda dapat membuat echoargs.cmd sebagai pembungkus untuk echoargs.exe :

'@echoargs.exe %*' | Set-Content echoargs.cmd

Panggil sebagai iep .\echoargs.cmd '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'

Karena file batch sekarang dipanggil, "" -escaping digunakan, yang memperbaiki masalah 3" of snow saat memanggil dari Windows PowerShell.

Fungsinya sama-sama bekerja pada platform mirip Unix, yang dapat Anda verifikasi dengan membuat skrip shell sh bernama echoargs :

@'
#!/bin/sh
i=0; for a; do printf '%s\n' "\$$((i+=1))=[$a]"; done
'@ > echoargs; chmod a+x echoargs

Panggil sebagai iep ./echoargs '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'


Penting : Versi yang lebih lengkap dari fungsi ini telah diterbitkan sebagai ie ( I nvoke (eksternal) E xecutable) di Saya baru saja menerbitkan modul Native , yang saya sarankan untuk Anda gunakan sebagai gantinya. Pasang modul dengan
Install-Module Native -Scope CurrentUser .
Modul ini juga berisi perintah ins ( Invoke-NativeShell ) yang menangani kasus penggunaan yang dibahas di # 13068 - lihat https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -671572939 untuk detailnya.

Fungsi iep kode sumber (gunakan modul Native sebagai gantinya - lihat di atas):

function iep {

  Set-StrictMode -Version 1
  if (-not (Test-Path Variable:IsCoreClr)) { $IsCoreCLR = $false }
  if (-not (Test-Path Variable:IsWindows)) { $IsWindows = $env:OS -eq 'Windows_NT' }

  # Split into executable name/path and arguments.
  $exe, [string[]] $argsForExe = $args

  # Resolve to the underlying command (if it's an alias) and ensure that an external executable was specified.
  $app = Get-Command -ErrorAction Stop $exe
  if ($app.ResolvedCommand) { $app = $app.ResolvedCommand }
  if ($app.CommandType -ne 'Application') { Throw "Not an external program, non-PS script, or batch file: $exe" }

  if ($argsForExe.Count -eq 0) {
    # Argument-less invocation
    & $exe
  }
  else {
    # Invocation with arguments: escape them properly to pass them through as literals.
    # Decide whether to escape embedded double quotes as \" or as "", based on the target executable.
    # * On Unix-like platforms, we always use \"
    # * On Windows, we use "" where we know it's safe to do. cmd.exe / batch files require "", and Microsoft compiler-generated executables do too, often in addition to supporting \",
    #   notably including Python and Node.js
    #   However, notable interpreters that support \" ONLY are Ruby and Perl (as well as PowerShell's own CLI, but it's better to call that with a script block from within PowerShell).
    #   Targeting a batch file triggers "" escaping, but in the case of stub batch files that simply relay to a different executable, that could still break
    #   if the ultimate target executable only supports \" 
    $useDoubledDoubleQuotes = $IsWindows -and ($app.Source -match '[/\\]?(?<exe>cmd|msiexec)(?:\.exe)?$' -or $app.Source -match '\.(?<ext>cmd|bat|py|pyw)$')
    $doubleQuoteEscapeSequence = ('\"', '""')[$useDoubledDoubleQuotes]
    $isMsiExec = $useDoubledDoubleQuotes -and $Matches['exe'] -eq 'msiexec'
    $isCmd = $useDoubledDoubleQuotes -and ($Matches['exe'] -eq 'cmd' -or $Matches['ext'] -in 'cmd', 'bat')
    $escapedArgs = foreach ($arg in $argsForExe) {
      if ('' -eq $arg) { '""'; continue } # Empty arguments must be passed as `'""'`(!), otherwise they are omitted.
      $hasDoubleQuotes = $arg.Contains('"')
      $hasSpaces = $arg.Contains(' ')
      if ($hasDoubleQuotes) {
        # First, always double any preexisting `\` instances before embedded `"` chars. 
        # so that `\"` isn't interpreted as an escaped `"`.
        $arg = $arg -replace '(\\+)"', '$1$1"'
        # Then, escape the embedded `"` chars. either as `\"` or as `""`.
        # If \" escaping is used:
        # * In PS Core, use of `\"` is safe, because its use triggers enclosing double-quoting (if spaces are also present).
        # * !! In WinPS, sadly, that isn't true, so something like `'foo="bar none"'` results in `foo=\"bar none\"` -
        #   !! which - due to the lack of enclosing "..." - is seen as *2* arguments by the target app, `foo="bar` and `none"`.
        #   !! Similarly, '3" of snow' would result in `3\" of snow`, which the target app receives as *3* arguments, `3"`, `of`, and `snow`.
        #   !! Even manually enclosing the value in *embedded* " doesn't help, because that then triggers *additional* double-quoting.
        $arg = $arg -replace '"', $doubleQuoteEscapeSequence
    }
      elseif ($isMsiExec -and $arg -match '^(\w+)=(.* .*)$') { 
        # An msiexec argument originally passed in the form `PROP="value with spaces"`, which PowerShell turned into `PROP=value with spaces`
        # This would be passed as `"PROP=value with spaces"`, which msiexec, sady, doesn't recognize (`PROP=valueWithoutSpaces` works fine, however).
        # We reconstruct the form `PROP="value with spaces"`, which both WinPS And PS Core pass through as-is.
        $arg = '{0}="{1}"' -f $Matches[1], $Matches[2]
      }
      # As a courtesy, enclose tokens that PowerShell would pass unquoted in "...", 
      # if they contain cmd.exe metachars. that would break calls to cmd.exe / batch files.
      $manuallyDoubleQuoteForCmd = $isCmd -and -not $hasSpaces -and $arg -match '[&|<>^,;]'
      # In WinPS, double trailing `\` instances in arguments that have spaces and will therefore be "..."-enclosed,
      # so that `\"` isn't mistaken for an escaped `"` - in PS Core, this escaping happens automatically.
      if (-not $IsCoreCLR -and ($hasSpaces -or $manuallyDoubleQuoteForCmd) -and $arg -match '\\') {
        $arg = $arg -replace '\\+$', '$&$&'
      }
      if ($manuallyDoubleQuoteForCmd) {
        # Wrap in *embedded* enclosing double quotes, which both WinPS and PS Core pass through as-is.
        $arg = '"' + $arg + '"'
      }
      $arg
    }
    # Invoke the executable with the properly escaped arguments.
    & $exe $escapedArgs
  }
}

@ mklement0 Mengesankan, tetapi berikut adalah pasangan yang tidak bekerja untuk saya di windows:

iep echoargs 'somekey="value with spaces"' 'te\" st'

Arg 0 is <somekey="value>
Arg 1 is <with>
Arg 2 is <spaces">
Arg 3 is <te\">
Arg 4 is <st>

Command line:
"C:\ProgramData\chocolatey\lib\echoargs\tools\EchoArgs.exe" somekey=\"value with spaces\" te\\\" st

berikut adalah rangkaian argumen pengujian saya :)

$Arguments = @(
    'trippe slash at the end \\\',
    '4 slash at the end \\\\',
    '\\servername\path\',
    'path=\\servername\path\',
    'key="\\servername\pa th\"',
    '5 slash at the end \\\\\',
    '\\" double slashed double quote',
    'simple',
    'white space',
    'slash at the end \',
    'double slash at the end \\',
    'trippe slash at the end \\\',
    'trippe slash at the end with space \\\ ',
    '\\" double slashed double quote',
    'double slashed double quote at the end \\"',
    '\\\" triple slashed double quote',
    'triple slashed double quote at the end \\\"',
    # slash
    'single slashes \a ^ \: \"',
    'path="C:\Program Files (x86)\test\"'
    # quotes
    'double quote " and single quote ''',
    # windows env var syntax
    "env var OS: %OS%",
    # utf16
    ('"utf16 ETHIOPIC WORDSPACE: \u1361"' | ConvertFrom-Json),
    # special chars
    "newLine`newLine"
    "tab`tab"
    "backspace`bbackspace"
    "carriage`rafter",
    "formFeed`fformFeed",
    # JSON Strings
    @"
[{"_id":"5cdab57e4853ea7b5a707070","index":0,"guid":"25319946-950e-4fe8-9586-ddd031cbb0fc","isActive":false,"balance":"`$2,841.15","picture":"http://placehold.it/32x32","age":39,"eyeColor":"blue","name":{"first":"Leach","last":"Campbell"},"company":"EMOLTRA","email":"[email protected]","phone":"+1 (864) 412-3166","address":"127 Beadel Street, Vivian, Vermont, 1991","about":"Ex labore non enim consectetur id ullamco nulla veniam Lorem velit cillum aliqua amet nostrud. Occaecat ipsum do est qui sint aliquip anim culpa laboris tempor amet. Aute sint anim est sint elit amet nisi veniam culpa commodo nostrud cupidatat in ex.","registered":"Monday, August 25, 2014 4:04 AM","latitude":"-12.814443","longitude":"75.880149","tags":["pariatur","voluptate","sint","Lorem","eiusmod"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Lester Bender"},{"id":1,"name":"Concepcion Jarvis"},{"id":2,"name":"Elsie Whitfield"}],"greeting":"Hello, Leach! You have 10 unread messages.","favoriteFruit":"strawberry"},{"_id":"5cdab57e8cd0ac577ab534a4","index":1,"guid":"0be10c87-6ce7-46c4-8dd6-23b1d9827538","isActive":false,"balance":"`$1,049.56","picture":"http://placehold.it/32x32","age":33,"eyeColor":"green","name":{"first":"Lacey","last":"Terrell"},"company":"XSPORTS","email":"[email protected]","phone":"+1 (858) 511-2896","address":"850 Franklin Street, Gordon, Virginia, 4968","about":"Eiusmod nostrud mollit occaecat Lorem consectetur enim pariatur qui eu. Proident aliqua sunt incididunt Lorem adipisicing ea esse do ullamco excepteur duis qui. Irure labore cillum aliqua officia commodo incididunt esse ad duis ea. Occaecat officia officia laboris veniam id dolor minim magna ut sit. Aute quis occaecat eu veniam. Quis exercitation mollit consectetur magna officia sit. Irure ullamco laborum cillum dolore mollit culpa deserunt veniam minim sunt.","registered":"Monday, February 3, 2014 9:19 PM","latitude":"-82.240949","longitude":"2.361739","tags":["nostrud","et","non","eiusmod","qui"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Meyers Dillard"},{"id":1,"name":"Jacobson Franco"},{"id":2,"name":"Hunt Hernandez"}],"greeting":"Hello, Lacey! You have 8 unread messages.","favoriteFruit":"apple"},{"_id":"5cdab57eae2f9bc5184f1768","index":2,"guid":"3c0de017-1c2a-470e-87dc-5a6257e8d9d9","isActive":true,"balance":"`$3,349.49","picture":"http://placehold.it/32x32","age":20,"eyeColor":"green","name":{"first":"Knowles","last":"Farrell"},"company":"DAYCORE","email":"[email protected]","phone":"+1 (971) 586-2740","address":"150 Bath Avenue, Marion, Oregon, 991","about":"Eiusmod sint commodo eu id sunt. Labore esse id veniam ea et laborum. Dolor ad cupidatat Lorem amet. Labore ut commodo amet commodo. Ipsum reprehenderit voluptate non exercitation anim nostrud do. Aute incididunt ad aliquip aute mollit id eu ea. Voluptate ex consequat velit commodo anim proident ea anim magna amet nisi dolore.","registered":"Friday, September 28, 2018 7:51 PM","latitude":"-11.475201","longitude":"-115.967191","tags":["laborum","dolor","dolor","magna","mollit"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Roxanne Griffith"},{"id":1,"name":"Walls Moore"},{"id":2,"name":"Mattie Carney"}],"greeting":"Hello, Knowles! You have 8 unread messages.","favoriteFruit":"strawberry"},{"_id":"5cdab57e80ff4c4085cd63ef","index":3,"guid":"dca20009-f606-4b99-af94-ded6cfbbfa38","isActive":true,"balance":"`$2,742.32","picture":"http://placehold.it/32x32","age":26,"eyeColor":"brown","name":{"first":"Ila","last":"Hardy"},"company":"OBLIQ","email":"[email protected]","phone":"+1 (996) 556-2855","address":"605 Hillel Place, Herald, Delaware, 9670","about":"Enim eiusmod laboris amet ex laborum do dolor qui occaecat ex do labore quis sunt. Veniam magna non nisi ipsum occaecat anim ipsum consectetur ex laboris aute ut consectetur. Do eiusmod tempor dolore eu in dolore qui anim non et. Minim amet exercitation in in velit proident sint aliqua Lorem reprehenderit labore exercitation.","registered":"Friday, April 21, 2017 6:33 AM","latitude":"64.864232","longitude":"-163.200794","tags":["tempor","eiusmod","mollit","aliquip","aute"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Duncan Guy"},{"id":1,"name":"Jami Maxwell"},{"id":2,"name":"Gale Hutchinson"}],"greeting":"Hello, Ila! You have 7 unread messages.","favoriteFruit":"banana"},{"_id":"5cdab57ef1556326f77730f0","index":4,"guid":"f2b3bf60-652f-414c-a5cf-094678eb319f","isActive":true,"balance":"`$2,603.20","picture":"http://placehold.it/32x32","age":27,"eyeColor":"brown","name":{"first":"Turner","last":"King"},"company":"DADABASE","email":"[email protected]","phone":"+1 (803) 506-2511","address":"915 Quay Street, Hinsdale, Texas, 9573","about":"Consequat sunt labore tempor anim duis pariatur ad tempor minim sint. Nulla non aliqua veniam elit officia. Ullamco et irure mollit nulla do eiusmod ullamco. Aute officia elit irure in adipisicing et cupidatat dolor in sint elit dolore labore. Id esse velit nisi culpa velit adipisicing tempor sunt. Eu sunt occaecat ex pariatur esse.","registered":"Thursday, May 21, 2015 7:44 PM","latitude":"88.502961","longitude":"-119.654437","tags":["Lorem","culpa","labore","et","nisi"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Leanne Lawson"},{"id":1,"name":"Jo Shepard"},{"id":2,"name":"Effie Barnes"}],"greeting":"Hello, Turner! You have 6 unread messages.","favoriteFruit":"apple"},{"_id":"5cdab57e248f8196e1a60d05","index":5,"guid":"875a12f0-d36a-4e7b-aaf1-73f67aba83f8","isActive":false,"balance":"`$1,001.89","picture":"http://placehold.it/32x32","age":38,"eyeColor":"blue","name":{"first":"Petty","last":"Langley"},"company":"NETUR","email":"[email protected]","phone":"+1 (875) 505-2277","address":"677 Leonard Street, Ticonderoga, Utah, 1152","about":"Nisi do quis sunt nisi cillum pariatur elit dolore commodo aliqua esse est aute esse. Laboris esse mollit mollit dolor excepteur consequat duis aute eu minim tempor occaecat. Deserunt amet amet quis adipisicing exercitation consequat deserunt sunt voluptate amet. Ad magna quis nostrud esse ullamco incididunt laboris consectetur.","registered":"Thursday, July 31, 2014 5:16 PM","latitude":"-57.612396","longitude":"103.91364","tags":["id","labore","deserunt","cillum","culpa"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Colette Mullen"},{"id":1,"name":"Lynnette Tanner"},{"id":2,"name":"Vickie Hardin"}],"greeting":"Hello, Petty! You have 9 unread messages.","favoriteFruit":"banana"},{"_id":"5cdab57e4df76cbb0db9be43","index":6,"guid":"ee3852fe-c597-4cb6-a336-1466e8978080","isActive":true,"balance":"`$3,087.87","picture":"http://placehold.it/32x32","age":33,"eyeColor":"brown","name":{"first":"Salas","last":"Young"},"company":"PLAYCE","email":"[email protected]","phone":"+1 (976) 473-2919","address":"927 Elm Place, Terlingua, North Carolina, 2150","about":"Laborum laboris ullamco aliquip occaecat fugiat sit ex laboris veniam tempor tempor. Anim quis veniam ad commodo culpa irure est esse laboris. Fugiat nostrud elit mollit minim. Velit est laborum ut quis anim velit aute enim culpa amet ipsum.","registered":"Thursday, October 1, 2015 10:59 AM","latitude":"-57.861212","longitude":"69.823065","tags":["eu","est","et","proident","nisi"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Day Solomon"},{"id":1,"name":"Stevens Boyd"},{"id":2,"name":"Erika Mayer"}],"greeting":"Hello, Salas! You have 10 unread messages.","favoriteFruit":"apple"},{"_id":"5cdab57ed3c91292d30e141d","index":7,"guid":"ef7c0beb-8413-4f39-987f-022c4e8ec482","isActive":false,"balance":"`$2,612.45","picture":"http://placehold.it/32x32","age":36,"eyeColor":"brown","name":{"first":"Gloria","last":"Black"},"company":"PULZE","email":"[email protected]","phone":"+1 (872) 513-2364","address":"311 Guernsey Street, Hatteras, New Mexico, 2241","about":"Laborum sunt exercitation ea labore ullamco dolor pariatur laborum deserunt adipisicing pariatur. Officia velit duis cupidatat eu officia magna magna deserunt do. Aliquip cupidatat commodo duis aliquip in aute dolore occaecat esse ad. Incididunt est magna in pariatur ut do ex sit minim cupidatat culpa. Voluptate eu veniam cupidatat exercitation.","registered":"Friday, June 26, 2015 7:59 AM","latitude":"38.644208","longitude":"-45.481555","tags":["sint","ea","anim","voluptate","elit"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Abby Walton"},{"id":1,"name":"Elsa Miranda"},{"id":2,"name":"Carr Abbott"}],"greeting":"Hello, Gloria! You have 5 unread messages.","favoriteFruit":"strawberry"},{"_id":"5cdab57edc91491fb70b705d","index":8,"guid":"631ff8a0-ce4c-4111-b1e4-1d112f4ecdc7","isActive":false,"balance":"`$2,550.70","picture":"http://placehold.it/32x32","age":25,"eyeColor":"brown","name":{"first":"Deirdre","last":"Huber"},"company":"VERBUS","email":"[email protected]","phone":"+1 (871) 468-3420","address":"814 Coles Street, Bartonsville, Tennessee, 7313","about":"Ipsum ex est culpa veniam voluptate officia consectetur quis et irure proident pariatur non. In excepteur est aliqua duis duis. Veniam consectetur cupidatat reprehenderit qui qui aliqua.","registered":"Monday, April 1, 2019 2:33 AM","latitude":"-75.702323","longitude":"45.165458","tags":["labore","aute","nisi","laborum","laborum"],"range":[0,1,2,3,4,5,6,7,8,9],"friends":[{"id":0,"name":"Genevieve Clarke"},{"id":1,"name":"Black Sykes"},{"id":2,"name":"Watson Hudson"}],"greeting":"Hello, Deirdre! You have 8 unread messages.","favoriteFruit":"strawberry"}]
"@
)

System.Diagnostics.Process memang memiliki batasan ketika digunakan, itulah mengapa saya harus menulis runner saya sendiri dengan kemampuan untuk menangkap STDOUT STDERR dan kombinasi kemudian menjadi variabel, yang cukup untuk kasus penggunaan saya, tetapi tidak sempurna.

Sunting: seperti yang Anda katakan, kelanjutannya tampaknya memiliki kasus tepi di Windows Poweshell 5. Saya menguji pada Powershell 6 dan berfungsi dengan baik! sayangnya saya harus berurusan dengan Poweshell 5 hingga 7 mengambil alih dalam kasus Windows ...

Mengesankan, tetapi berikut adalah pasangan yang tidak cocok untuk saya di windows:

Ya, batasan ini berlaku untuk _Windows PowerShell_, dengan executable yang mendukung "" keluar dari " disematkan tidak dapat diasumsikan, seperti yang dijelaskan dalam komentar saya sebelumnya.
Jika Anda bersedia menerima dukungan untuk "" escaping di semua pemanggilan Anda (_most_, tetapi tidak semua executable di Windows mendukungnya), Anda dapat dengan mudah mengubah fungsinya.

Dan, untuk mengkonfirmasi apa yang Anda katakan dalam suntingan Anda: Di PowerShell _Core_:

  • iep echoargs 'somekey="value with spaces"' 'te\" st' berfungsi dengan baik.
  • Array argumen pengujian Anda tampaknya berfungsi dengan baik juga.

Jika Anda ingin menerima dukungan untuk "" escaping di semua pemanggilan Anda (_most_, tetapi tidak semua executable di Windows mendukungnya), Anda dapat dengan mudah mengubah fungsinya.

Terima kasih, saya akan mencobanya dalam waktu dekat. Saya kadang-kadang berurusan dengan sqlcmd dan itu tidak mendukung "" pasti. Untuk kasus itu - mudah untuk memberikan opsi untuk melewati logika escape untuk argumen tertentu.

Cara bagaimana Windows mem-parsing argumen baris perintah dapat ditemukan di Parsing C ++ argumen baris perintah :

Kode startup Microsoft C / C ++ menggunakan aturan berikut saat menafsirkan argumen yang diberikan pada baris perintah sistem operasi:

  • Argumen dibatasi oleh spasi, yang merupakan spasi atau tab.

  • Karakter caret (^) tidak dikenali sebagai karakter escape atau pembatas. Karakter tersebut ditangani sepenuhnya oleh parser baris perintah di sistem operasi sebelum diteruskan ke array argv dalam program.

  • String yang diapit oleh tanda kutip ganda (" string ") diartikan sebagai argumen tunggal, terlepas dari spasi kosong yang ada di dalamnya. String yang dikutip dapat disematkan dalam argumen.

  • Tanda kutip ganda yang diawali dengan garis miring terbalik (\ ") diartikan sebagai karakter tanda kutip ganda literal (").

  • Garis miring terbalik diartikan secara harfiah, kecuali jika segera mendahului tanda kutip ganda.

  • Jika sejumlah garis miring terbalik diikuti dengan tanda kutip ganda, satu garis miring terbalik ditempatkan dalam larik argv untuk setiap pasang garis miring terbalik, dan tanda kutip ganda diartikan sebagai pemisah string.

  • Jika sejumlah ganjil dari garis miring terbalik diikuti dengan tanda kutip ganda, satu garis miring terbalik ditempatkan dalam larik argv untuk setiap pasang garis miring terbalik, dan tanda kutip ganda "di-escape" oleh sisa garis miring terbalik, menyebabkan literal tanda kutip ganda (") untuk ditempatkan di argv .

Ini juga dibahas di _exec, _wexec Functions :

Spasi yang disematkan dalam string dapat menyebabkan perilaku yang tidak terduga; misalnya, meneruskan _exec string "hi there" akan menghasilkan proses baru mendapatkan dua argumen, "hi" dan "there" . Jika maksudnya adalah agar proses baru membuka file bernama "hai", proses tersebut akan gagal. Anda dapat menghindari ini dengan mengutip string: "\"hi there\"" .

Bagaimana Python memanggil executable asli

subprocess.Popen Python dapat menangani pelolosan dengan benar dengan mengubah args menjadi string dengan cara yang dijelaskan dalam Mengonversi urutan argumen menjadi string di Windows . Implementasinya adalah subprocess.list2cmdline .

Semoga ini dapat menjelaskan bagaimana PowerShell dapat menangani ini dengan cara yang lebih elegan, daripada menggunakan --% atau pelolosan ganda (mengikuti sintaks PowerShell dan CMD). Solusi saat ini benar-benar mengganggu pelanggan Azure CLI (yang didasarkan pada python.exe).

Kami masih belum memiliki metode yang jelas dan ringkas untuk memahami cara menangani masalah ini. Adakah yang bisa dari tim Powershell menjelaskan jika ini disederhanakan dengan v7?

Nah, kabar baiknya adalah salah satu fokus untuk PS 7.1 adalah membuatnya lebih mudah untuk menjalankan perintah native. Dari posting blog tentang investasi 7.1 :

Sebagian besar perintah asli berfungsi dengan baik dari dalam PowerShell, namun, ada beberapa kasus di mana penguraian argumen tidak ideal (seperti menangani tanda kutip dengan benar). Tujuannya adalah untuk memungkinkan pengguna memotong baris perintah sampel untuk alat asli populer apa pun, menempelkannya ke PowerShell, dan itu hanya berfungsi tanpa memerlukan pelolosan khusus PowerShell.

Jadi mungkin (semoga) ini akan ditangani di 7.1

Terima kasih, @rkeithhill , tetapi tidak:

Tujuannya adalah untuk memungkinkan pengguna memotong baris perintah sampel untuk alat asli populer apa pun, menempelkannya ke PowerShell, dan itu hanya berfungsi tanpa memerlukan pelolosan khusus PowerShell.

terdengar seperti orang lain yang mengambil --% (simbol stop-parsing) yang secara inheren bermasalah?

@ SteveL-MSFT, dapatkah Anda memberi tahu kami lebih banyak tentang fitur yang akan datang ini?


Untuk rekap, perbaikan yang diusulkan di sini adalah bahwa semua yang perlu Anda fokuskan adalah untuk memenuhi persyaratan sintaks _PowerShell_ dan bahwa PowerShell menangani semua pelarian _di belakang adegan_ (di Windows; di Unix ini bahkan tidak diperlukan lagi, sekarang .NET memungkinkan kami meneruskan serangkaian token verbatim ke program target) - tetapi tidak ada keraguan bahwa perbaikan harus diikutsertakan, jika kompatibilitas ke belakang harus dipertahankan.

Dengan perbaikan ini, _some_ mengutak-atik baris perintah untuk shell lain masih akan diperlukan, tetapi akan lebih mudah - dan, dibandingkan dengan --% , Anda mempertahankan kekuatan penuh sintaks PowerShell dan perluasan variabel (ekspresi dengan (...) , pengalihan, ...):

  • Anda perlu mengganti \" dengan `" , tetapi _hanya di dalam "..." _.

  • Anda perlu menyadari bahwa _no (other) shell_ terlibat, sehingga referensi variabel lingkungan gaya Bash seperti $USER akan _not_ berfungsi (mereka akan ditafsirkan sebagai variabel _PowerShell_), kecuali jika Anda menggantinya dengan setara dengan sintaks PowerShell, $env:USER .

    • Sebagai tambahan: --% mencoba untuk mengkompensasinya dengan - terutama _invariably_ - mengembangkan cmd.exe -gaya referensi variabel lingkungan seperti %USERNAME% , tetapi perhatikan bahwa itu tidak hanya tidak ' t mendukung referensi gaya Bash ( $USER ) diteruskan _verbatim_ ke program target, tetapi juga secara tidak terduga memperluas referensi gaya cmd.exe pada platform mirip Unix, dan tidak mengenali '...' -quoting.
    • Lihat di bawah untuk alternatif bahwa _does_ melibatkan shell asli platform masing-masing.
  • Anda perlu menyadari bahwa PowerShell memiliki _additional_ metacharacters yang memerlukan kutipan / pelolosan untuk penggunaan verbatim; ini adalah (perhatikan bahwa @ hanya bermasalah sebagai karakter _first_ argumen.):

    • untuk kerangka seperti POSIX (mis., Bash): @ { } ` (dan $ , jika Anda ingin mencegah ekspansi di muka oleh PowerShell)
    • untuk cmd.exe : ( ) @ { } # `
    • Satu ` satu printf %s `@list.txt ).

Contoh yang agak dibuat-buat:

Ambil baris perintah Bash berikut:

# Bash
$ printf '"%s"\n' "3\" of snow"
"3" of snow"        # output

Dengan perbaikan yang diusulkan di tempat, semua yang diperlukan adalah mengganti \" instance di dalam "..." argumen tertutup dengan `" :

# PowerShell - WISHFUL THINKING
PS> printf '"%s"\n' "3`" of snow"
"3" of snow"        # output

Artinya, Anda tidak perlu khawatir tentang menyematkan " di dalam '...' , dan di dalam "..." Anda hanya perlu menghindarinya untuk membuat _PowerShell_ bahagia (yang juga dapat Anda lakukan dengan "" sini).

Fungsi iep mengimplementasikan perbaikan ini (sebagai stopgap), sehingga iep printf '"%s"\n' "3`" of snow" berfungsi sebagaimana mestinya.


Bandingkan ini dengan perilaku rusak saat ini, di mana Anda perlu melompati lingkaran berikut untuk mendapatkan perintah agar berfungsi seperti di Bash (secara tidak dapat dijelaskan membutuhkan putaran _additional_ untuk melarikan diri dengan \ ):

# PowerShell - messy workaround to compensate for the current, broken behavior.
PS> printf '\"%s\"\n' "3\`" of snow"
"3" of snow"        # output

Dengan perbaikan di tempat, mereka yang ingin menggunakan baris perintah yang diberikan _as-is_ melalui _default shell_ platform, akan dapat menggunakan verbatim _di sini-string_ untuk meneruskan ke sh -c (atau bash -c ) / cmd /c ; misalnya:

# PowerShell - WISHFUL THINKING
PS> sh -c @'
printf '"%s"\n' "3\" of snow"
'@
"3" of snow"  # output

Perhatikan bahwa penggunaan --% tidak _not_ berfungsi di sini ( printf --% '"%s"\n' "3\" of snow" ), dan keuntungan tambahan dari pendekatan berbasis string di sini adalah bahwa berbagai batasan --% tidak berlaku , terutama ketidakmampuan untuk menggunakan pengalihan keluaran ( > ).

Jika Anda beralih ke string-_double_-dikutip di sini ( @"<newline>....<newline>"@ ), Anda bahkan dapat menyematkan variabel dan ekspresi _PowerShell_, tidak seperti --% ; namun, Anda kemudian perlu memastikan bahwa nilai yang diperluas tidak merusak sintaks shell target.

Kita dapat memikirkan tentang cmdlet khusus dengan alias yang ringkas (mis., Invoke-NativeShell / ins ) untuk panggilan semacam itu (sehingga sh -c / cmd /c tidak perlu ditentukan), tetapi untuk meneruskan baris perintah kompleks sebagaimana adanya, saya tidak memikirkan cara untuk menggunakan string di sini:

# PowerShell - WISHFUL THINKING
# Passes the string to `sh -c` / `cmd /c` for execution, as appropriate.
# Short alias: ins
PS> Invoke-NativeShell @'
printf '"%s"\n' "3\" of snow"
'@
"3" of snow"  # output

Tentu saja, jika Anda mengandalkan fitur-fitur shell platform-native, panggilan tersebut menurut definisi akan menjadi platform [-family] -specific -

Inilah alasan mengapa mengandalkan PowerShell _alone_, dengan sintaks _own-nya_ lebih disukai dalam jangka panjang : ini memberikan pengalaman lintas platform yang dapat diprediksi untuk memanggil program eksternal - bahkan jika itu berarti Anda tidak dapat menggunakan baris perintah yang dibuat untuk shell lain sebagai- adalah; ketika PowerShell semakin populer, saya berharap rasa sakit menemukan dan mengetahui modifikasi yang diperlukan berkurang, dan saya mengharapkan lebih banyak dokumentasi untuk menunjukkan versi baris perintah PowerShell (juga).

  • Anda perlu mengganti \" dengan `" , tetapi _hanya di dalam "..." _.

Ini tidak bekerja di semua tempat.

# working everywhere but polluted with \
❯ node -e 'console.log(\"hey\")'
hey

# working in Node:
❯ node -e 'console.log(`"hey`")'
hey

# not working in Julia:
❯ julia -e 'print(`"hey`")'
`hey`

# not working anywhere:
❯ node -e "console.log(`"hey`")"
❯ node -e "console.log("hey")"
❯ node -e "console.log(""hey"")"
❯ node -e 'console.log(""hey"")'

Sintaks Bash:

❯ node -e 'console.log("hey")'
hey

Saran Powershell:

Jika tim PowerShell hanya mencari simbol, itu tidak merusak sintaks sebelumnya, mengapa tidak menggunakan sesuatu seperti backticks `untuk perilaku seperti Bash, yang lolos literal secara otomatis, dan memungkinkan interpolasi string. Ini mirip dengan sintaks JavaScript juga.

❯ node -e `console.log("hey")`
hey

❯ $a=hey 
❯ node -e `console.log($hey)`
hey

mengapa tidak menggunakan sesuatu seperti backticks `untuk perilaku seperti Bash

Backticks sudah digunakan untuk meng-escape item, tujuan yang sama seperti backslahes di shell POSIX. Komentar yang Anda maksud sudah menunjukkan hal itu. Kami telah menggunakan semua hal seperti kutipan ASCII. Menambahkan awalan $ ke string literal normal mungkin berhasil, tetapi menurut saya itu tidak cukup masuk akal.


Cara Windows mem-parsing argumen baris perintah dapat ditemukan di ...

Masalahnya adalah bahwa Windows MSVCR tidak hanya melakukan itu: ia menangani kasus sudut dengan cara yang tidak terdokumentasi . Barang "" telah diatur sedemikian rupa sehingga mereka bahkan memasukkannya ke dalam CoreFX ketika mereka memindahkan .NET ke Unix. Tapi bagaimanapun, itu selalu cukup baik untuk melarikan diri, setidaknya sampai seseorang meminta globbing.

Ada juga masalah klasik setiap orang melakukannya secara berbeda, tetapi kita tidak perlu khawatir tentang itu karena kita selalu memiliki .NET untuk cmdline mentah.

mengapa tidak menggunakan sesuatu seperti backticks `untuk perilaku seperti Bash

Backticks sudah digunakan untuk meng-escape item, tujuan yang sama seperti backslahes di shell POSIX. Kami telah menggunakan semua hal seperti kutipan ASCII.

Ada kemungkinan untuk menggunakan kombinasi simbol jika parser tidak dapat mendeteksi bahwa di sini `memasukkan string. Sesuatu seperti '' bahkan mungkin berhasil.

@aminya berikut adalah solusinya jika Anda tidak menggunakan windows lama Powershell 5.1 dan 6+:
https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606

Karena saya harus berurusan dengan PowerShell 5,1 di windows dan 6+ di linux / mac, saya memiliki implementasi saya sendiri yang telah bekerja tanpa masalah selama bertahun-tahun yang memungkinkan untuk bekerja dengan alat-alat seperti kubectl, helm, terraform dan lainnya melalui JSON yang kompleks objek dalam parameter:
https://github.com/choovick/ps-invoke-externalcommand

GitHub
Berkontribusi pada pengembangan choovick / ps-invoke-externalcommand dengan membuat akun di GitHub.

@choovick implementasi yang agak lebih pendek diberikan di atas di utas ini , saya masih menemukan kasus di mana itu akan gagal bagi saya. Ini bekerja di PS mulai dari v3.

Fungsi Run-Native @AndrewSav @TSlivede sangat pintar dan ringkas, dan juga bekerja dengan andal di _Windows PowerShell_; beberapa hal yang perlu diperhatikan: kebutuhan, proses anak melihat aux. commandlineargumentstring variabel lingkungan (mungkin jarang, jika pernah, menjadi masalah dalam praktik), pemanggilan tanpa argumen saat ini tidak ditangani dengan benar (dengan mudah diperbaiki dan bahkan bukan masalah, jika Anda memastikan bahwa Anda hanya pernah menggunakan fungsi _with_ argumen, untuk apa), _all_ argumen dikutip ganda (misalnya, sesuatu seperti 42 diteruskan sebagai "42" ), yang pada Windows dapat (sayangnya) memiliki efek samping program yang menafsirkan argumen kutip ganda (atau sebagian dikutip ganda, seperti dalam argumen msiexec ) secara berbeda.

@aminya , satu-satunya alasan mengapa node -e 'console.log(`"hey`")' (semacam) berfungsi adalah karena perilaku rusak saat ini (lihat di bawah). Saya berasumsi yang ingin Anda sampaikan adalah _verbatim_ console.log("hey") , yang, jika PowerShell di Windows lolos dengan benar seperti yang diusulkan di sini, Anda akan meneruskan apa adanya dalam tanda kutip tunggal: node -e 'console.log("hey")' . Ini harus _automatically_ diterjemahkan ke node -e "console.log(\"hey\")" (kutipan ganda, \ -escaped verbatim " ) _di belakang adegan_.

Mengingat sudah berapa lama utas ini, izinkan saya mencoba merangkum:
Anda hanya perlu mengkhawatirkan tentang persyaratan sintaks _PowerShell_, dan ini adalah tugas PowerShell _ sebagai shell_ untuk memastikan bahwa nilai argumen _verbatim_ yang dihasilkan dari penguraian PowerShell sendiri diteruskan ke program eksternal _as-is_.

  • Pada platform mirip Unix, sekarang kita memiliki dukungan .NET Core untuk itu, melakukan ini sepele, karena nilai verbatim hanya dapat diteruskan apa adanya sebagai elemen dari _array_ argumen, yang merupakan cara program menerima argumen secara asli di sana .
  • Pada Windows, program eksternal menerima argumen sebagai _single baris perintah string_ (keputusan desain historis yang disesalkan) dan harus melakukan penguraian baris perintah sendiri. Meneruskan beberapa argumen sebagai bagian dari string tunggal memerlukan aturan kutip dan parsing untuk menggambarkan argumen dengan benar; sementara ini pada akhirnya gratis untuk semua (program bebas untuk diurai sesuka mereka), konvensi sintaks yang paling banyak digunakan (seperti yang dinyatakan sebelumnya dan juga diusulkan dalam RFC yang sayangnya apa yang diimplementasikan oleh kompiler C / C ++ Microsoft , jadi masuk akal untuk melakukannya.

    • _Update_: Bahkan di Windows kita dapat memanfaatkan koleksi-of-verbatim-token ArgumentList properti System.Diagnostics.ProcessStartInfo di .NET Core: di Windows secara otomatis diterjemahkan ke perintah yang dikutip dan lolos dengan benar -line string saat proses dimulai; namun, untuk _batch files_ kami mungkin masih memerlukan penanganan khusus - lihat https://github.com/PowerShell/PowerShell-RFC/pull/90#issuecomment -552231174

Menerapkan hal di atas tidak diragukan lagi merupakan perubahan besar yang menghancurkan, jadi mungkin ini membutuhkan keikutsertaan.
Saya pikir melanjutkan ini adalah suatu keharusan, jika kita ingin menyingkirkan semua sakit kepala mengutip saat ini, yang terus muncul dan menghambat adopsi PowerShell, terutama di dunia Unix.


Adapun node -e 'console.log(`"hey`")' : di dalam '...' , jangan gunakan ` - kecuali Anda ingin karakter itu diteruskan sebagaimana adanya. Karena PowerShell saat ini _tidak_ lolos dari karakter " verbatim. dalam argumen Anda sebagai \" belakang layar, yang dilihat node pada baris perintah adalah console.log(`"hey`") , yang diuraikan sebagai dua literal string yang bersebelahan langsung: unquoted console.log(` dan kutipan ganda "hey`" . Setelah menghapus " , yang memiliki fungsi _syntactic_ karena tidak menjadi \ -escaped, kode JavaScript yang dieksekusi pada akhirnya adalah console.log(`hey`) , dan itu hanya berfungsi karena `....` token terlampir adalah bentuk string literal dalam JavaScript, yaitu _template literal_.

@AndrewSav Saya telah menguji dengan objek uji gila saya dan itu berhasil untuk saya! Solusi yang sangat elegan, teruji pada Windows 5.1 dan PS 7 di linux. Saya baik-baik saja dengan semua kutipan ganda, saya tidak berurusan dengan msiexec atau sqlcmd juga dikenal memperlakukan " secara eksplisit.

Penerapan pribadi saya juga memiliki logika pelolosan sederhana yang mirip dengan yang Anda sebutkan: https://github.com/choovick/ps-invoke-externalcommand/blob/master/ExternalCommand/ExternalCommand.psm1#L278

tetapi saya telah menulis banyak kode untuk menampilkan dan menangkap utas STDOUT dan STDERR secara realtime dalam modul itu ... Mungkin dapat sangat disederhanakan, tetapi saya tidak perlu ...

@ mklement0 utas ini tidak akan pernah berakhir (: Kita perlu menyediakan modul PowerShell yang diterbitkan di galeri ps yang akan menyesuaikan sebagian besar kasus penggunaan dan akan cukup sederhana untuk digunakan, atau menunggu peningkatan shell yang akan datang.

GitHub
Berkontribusi pada pengembangan choovick / ps-invoke-externalcommand dengan membuat akun di GitHub.

@ mklement0 utas ini tidak akan pernah berakhir (: Kita perlu menyediakan modul PowerShell yang diterbitkan di galeri ps yang akan menyesuaikan sebagian besar kasus penggunaan dan akan cukup sederhana untuk digunakan, atau menunggu peningkatan shell yang akan datang.

Jika tim PowerShell memutuskan untuk tidak memperbaikinya dalam program itu sendiri, saya kemudian akan mengatakan bahwa shell yang tidak dapat menjalankan program eksternal secara asli dan benar tidak akan menjadi shell pilihan saya! Ini adalah hal-hal dasar yang hilang dari PowerShell.

@aminya ya, menurut saya masalah itu sangat mengganggu diri saya sendiri ketika saya harus pindah ke sana. Tetapi fitur seperti di bawah ini membuatnya sepadan.

  • Kerangka parameter yang fleksibel, CMDlet andal yang mudah dibuat.
  • Modul dan repositori modul internal untuk berbagi logika umum di seluruh organisasi, menghindari banyak duplikasi kode dan sentralisasi fungsi inti yang dapat difaktor ulang di satu tempat.
  • Lintas platform. Saya memiliki orang-orang yang menjalankan alat saya di Windows di PS 5.1, dan di linux / mac di PS 6/7

Saya sangat berharap tim PS meningkatkan ini di masa depan agar tidak terlalu berbelit-belit.

Menerapkan hal di atas tidak diragukan lagi merupakan perubahan besar yang menghancurkan, jadi mungkin ini membutuhkan keikutsertaan.

Apakah yang Anda maksud adalah menerapkan https://github.com/PowerShell/PowerShell-RFC/pull/90?

@aminya , saya setuju bahwa memanggil program eksternal dengan argumen adalah mandat inti dari sebuah shell dan harus berfungsi dengan baik.

Sebuah modul, seperti yang disarankan oleh @choovick , masih masuk akal untuk _Windows PowerShell_, yang berada dalam mode pemeliharaan hanya-perbaikan-perbaikan-keamanan.
Sebagai pelengkap, jika / ketika topik bantuan konseptual yang diusulkan tentang memanggil program eksternal ditulis - lihat https://github.com/MicrosoftDocs/PowerShell-Docs/issues/5152 - fungsi pembantu yang memperbaiki masalah di versi sebelumnya / Windows PowerShell, seperti @TSlivede di atas, bisa langsung diposting disana.

@iSazonov Ya, menerapkan https://github.com/PowerShell/PowerShell-RFC/pull/90 adalah yang saya maksud.

Adapun utas tidak pernah berakhir dan masalah pemecahan-perubahan:

Tanggapan resmi terakhir untuk RFC terkait adalah komentar ini oleh @joeyaiello dari 8 Juli 2019 (penekanan ditambahkan):

tetapi kami berpikir bahwa [RFC] ini sangat masuk akal terlepas dari perilaku yang ada dan tanpa memperhatikan pemecahannya . Sekarang setelah kami memiliki fitur eksperimental, menurut kami sangat masuk akal untuk pergi dan menerapkannya hari ini di belakang tanda fitur eksperimental, dan kami dapat mencari tahu lebih jauh apakah ini perilaku ikut serta vs menyisih , apakah ada beberapa transisi jalur, dan jika variabel preferensi adalah mekanisme yang tepat untuk mengaktifkan dan menonaktifkannya.

_Personally_, saya tidak keberatan memperbaiki perilaku _by default_, meskipun itu merupakan perubahan yang mengganggu; RFC memang mengusulkan hal itu dan menyarankan keikutsertaan jika Anda menginginkan perilaku _old_ (rusak).

Saya menduga mereka yang memiliki kode warisan untuk dipelihara akan menolak, namun, karena _ semua solusi yang ada akan berhenti berfungsi_ - lihat di atas - dan mempertahankan kompatibilitas ke belakang tampaknya masih menjadi tujuan utama.

Jika perilaku baru dan tetap dibuat keikutsertaan, Anda masih merasa canggung karena harus melakukan sesuatu hanya untuk mendapatkan perilaku yang benar, tetapi setidaknya kode yang ada tidak akan rusak.

Tetapi mekanisme keikutsertaan yang ada sendiri bermasalah:
Sekarang fitur opsional @KirkMunro RFC telah ditolak, yang cukup banyak meninggalkan variabel _preference_, dan tantangannya adalah cakupan dinamis PowerShell: kode pihak ketiga dipanggil dari lingkup yang diikutsertakan yang tidak dirancang untuk menggunakan yang baru implementasi kemudian bisa rusak (kecuali variabel preferensi untuk sementara disetel ulang).

Pelingkupan _Lexical_ dari keikutsertaan diperlukan di sini, yang saat ini tidak kami miliki. RFC untuk pelingkupan _lexical_ mode ketat mengusulkan implementasi fitur yang using (yang, khususnya, secara umum mencakup _dynamically_). Mengikuti pola ini, pernyataan using ProperExternalArgumentQuoting dengan cakupan leksikal (namanya adalah WIP :) - jika memungkinkan secara teknis - layak dipertimbangkan.

Kami membutuhkan komite PowerShell untuk mempertimbangkan (lagi) dan untuk memberikan panduan yang jelas tentang jalan ke depan, dengan umpan balik _timely_ pada pertanyaan yang muncul. @ SteveL-MS?


Perhatikan bahwa solusi mirip --% diisyaratkan oleh posting blog 7.1 (lihat di atas ) (yang menurut saya pribadi tidak layak untuk dikejar - lihat di atas dan komentar ini ), akan menjadi fitur _separate_ - memperbaiki asli PowerShell (non-emulasi) perilaku masih merupakan keharusan.

Secara pribadi, saya tidak keberatan memperbaiki perilaku secara default, meskipun itu adalah perubahan yang merusak; RFC memang mengusulkan hal itu dan menyarankan untuk ikut serta jika Anda menginginkan perilaku lama (rusak).

Jika perilaku baru dan tetap dibuat keikutsertaan, Anda masih merasa canggung karena harus melakukan sesuatu hanya untuk mendapatkan perilaku yang benar, tetapi setidaknya kode yang ada tidak akan rusak.

Setuju, pada kenyataannya saya berpendapat bahwa tidak masuk akal untuk memiliki default yang rusak , kemudian default yang diterapkan dengan benar. Mengingat fakta bahwa implementasi saat ini sebenarnya adalah bug , perilaku baru harus ikut serta, bukan menyisih, karena benar-benar tidak masuk akal untuk terus mendorong panggilan shell eksternal rusak yang cenderung rusak secara tidak terduga. cara. Bagaimanapun, PowerShell 7 harus berusaha meningkatkan Windows PowerShell warisan.

@ SteveL-MSFT dan saya setuju kita harus menutup yang satu ini demi # 13068. Apa pun yang kami sentuh di sini terlalu banyak merupakan perubahan yang mengganggu, dan kami harus mengatasi masalah tersebut dengan operator baru yang berfungsi sebagai mode keikutsertaan.

Saya benar-benar tidak melihat bagaimana # 13068 akan menyelesaikan ini: Jika operator itu diperkenalkan sebagaimana dimaksud, kami masih tidak memiliki cara untuk memanggil executable asli dengan benar dengan berbagai argumen atau dengan beberapa argumen eksplisit yang isinya berasal dari variabel.

Contoh, yang diberikan @JustinGrote di utas itu saat ini tidak berfungsi dengan andal (jika kutipan yang disematkan dimungkinkan dalam payload argumen) dan menambahkan operator itu tidak akan memberikan alternatif apa pun yang meningkatkan apa pun.

@joeyaiello Dapatkah Anda setidaknya membiarkan masalah ini terbuka sampai operator tersebut benar-benar ada dan seseorang dapat menunjukkan, bagaimana operator itu akan memperbaiki sesuatu, yang disebutkan di utas ini?

Oh dan juga bagaimana dengan Linux? Masalah ini bodoh dan tidak terduga di Windows, tetapi di Linux bahkan jauh lebih tidak masuk akal, terutama karena tidak ada riwayat panjang skrip PowerShell Linux, yang akan rusak.

Membuat operator khusus untuk ini sama sekali tidak masuk akal untuk shell baris perintah, karena tugas utamanya adalah meluncurkan program dan meneruskan argumen kepada mereka. Memperkenalkan operator baru yang menghasilkan system() untuk pekerjaan ini seperti Matlab memperkenalkan cara memanggil calc.exe karena ada bug dalam aritmatikanya. Yang seharusnya dilakukan adalah:

  • Tim pwsh bersiap untuk rilis utama baru yang memperbaiki hal-hal baris perintah, memindahkan perilaku saat ini ke belakang cmdlet bawaan.
  • Sebagai solusi stop-gap, versi pwsh yang akan datang mendapatkan cmdlet bawaan yang menggunakan perilaku baru yang benar untuk penyampaian baris perintah.

Hal yang sama berlaku untuk Start-Process . (Sebenarnya ini adalah kandidat yang cukup bagus untuk cmdlet "baru" dengan beberapa opsi seperti -QuotingBehavior Legacy ...) Lihat # 13089.

Mengapa Powershell berperilaku berbeda dalam dua situasi ini? Secara khusus, ini tidak konsisten membungkus arg yang berisi spasi dalam tanda kutip ganda.

Saya mendapatkan hasil yang konsisten di v.7. Sepertinya sudah diperbaiki.

PING 'A \"B'

Permintaan ping tidak dapat menemukan host A "B.

PING 'A\" B'

Permintaan ping tidak dapat menemukan host A "B.

Itu 'tidak diperbaiki, karena nama host verbatim yang seharusnya dilihat ping adalah A \"B dan A\" B - _dengan karakter \ .

PowerShell, sebagai shell, harus mengurai argumen sesuai _its_ rules - only - dan kemudian secara transparan memastikan bahwa proses target melihat nilai verbatim yang sama yang merupakan hasil penguraian PowerShell sendiri.

Kerang _other_ itu - dan program-program buruk yang berjalan di Windows yang harus bertindak seperti shell mereka sendiri, dengan cara berbicara, dengan harus mengurai _command line_ hanya untuk mengekstrak argumen individu yang diteruskan - gunakan \ sebagai pelarian karakter tidak boleh masuk ke gambar di sini - mengakomodasinya (perlu di Windows saja, di Unix Anda hanya meneruskan argumen verbatim langsung sebagai array) adalah tugas PowerShell sebagai shell, _di belakang scene_.

Selain itu, seperti PowerShell itu sendiri tidak memerlukan pelolosan " _ di dalam '...' _ (string yang dikutip tunggal), begitu pula shell yang kompatibel dengan POSIX seperti bash : dieksekusi dari bash , misalnya, /bin/echo 'A \"B' (secara masuk akal) mencetak A \"B ( \ diperlakukan sebagai literal dalam string yang dikutip tunggal) - yang mengeksekusi perintah yang sama dari PowerShell (secara tidak terduga) menghasilkan
A "B - \ hilang - adalah manifestasi dari masalah yang dibahas di sini.

Saya harus menjelaskan:

  • Dari perspektif keinginan untuk memberikan A "B _verbatim_, Anda harus dapat menggunakan 'A "B' dari PowerShell.

  • Baris perintah yang saat ini dibuat oleh PowerShell di belakang layar berisi "A "B" - yang proses targetnya dilihat sebagai A B - yaitu, penutup buta di "..." , tanpa keluar dari _embedded_ " mengakibatkan kerugian efektif dari " disematkan. Apa yang digunakan PowerShell _should_ dalam baris perintah di belakang layar dalam hal ini adalah "A \"B" - yaitu, " disematkan membutuhkan \ -escaping.

  • Demikian pula, penutup buta yang sama menyebabkan 'A \"B' direpresentasikan sebagai "A \"B" pada baris perintah di belakang layar, yang kebetulan mengubah _embedded_ \" menjadi _escaped_ " karakter, yang oleh karena itu proses target dilihat sebagai A "B ; yaitu, kurangnya _automatic_ escaping mengakibatkan hilangnya \ disematkan. Apa yang digunakan PowerShell _should_ dalam baris perintah di belakang layar dalam hal ini adalah "A \\\"B" - artinya, \ dan " perlu keluar.

Itu 'tidak diperbaiki, karena nama host verbatim yang seharusnya dilihat ping adalah A \"B dan A\" B - _dengan karakter \ .

"Itu" di sini mengacu pada keluhan yang dikutip, yang untungnya tidak dapat saya tiru.

@ yecril71pl , saya melihat: asumsi saya (salah) adalah bahwa "argumen yang membungkus secara tidak konsisten yang berisi spasi dalam tanda kutip ganda" mengacu pada _lack dari pelolosan otomatis dari " dan \ karakter_ yang disematkan, karena dijelaskan dalam komentar saya sebelumnya - dan itulah inti dari masalah ini.

Ada perbaikan kecil di PowerShell Core yang tidak dimiliki Windows PowerShell; Saya hanya bisa memikirkan satu sekarang:

  • Windows PowerShell menggunakan kutipan ganda buta jika ada \ : 'A B\' berubah menjadi (rusak) "A B\" - PS Core menanganinya dengan benar ( "A B\\" ) .

Mengingat bahwa repo ini hanya untuk PS Core, cukup fokus pada apa yang masih rusak di PS Core (akan sangat membantu untuk menyebutkan perbedaan _sebagai tambahan_, tetapi yang terbaik adalah membuat aspek itu eksplisit).


Dan bahkan skenario yang ada dalam pikiran Anda _is_ masih rusak di PS Core - tetapi hanya _jika Anda menghilangkan \ dari argumen_:

Meneruskan 'A" B' masih menghasilkan _non_-double-quote A" B belakang layar (sedangkan 'A\" B' menghasilkan "A\" B" - yang juga rusak, seperti yang dibahas - saja berbeda).

Mengingat bahwa repo ini hanya untuk PS Core, cukup fokus pada apa yang masih rusak di PS Core (akan sangat membantu untuk menyebutkan perbedaan _sebagai tambahan_, tetapi yang terbaik adalah membuat aspek itu eksplisit).

Selain meta: Saya merasa berguna untuk mengetahui bahwa perilaku salah yang disebutkan dalam komentar pengguna lain tidak berlaku. Tentu saja, kami dapat mengabaikan komentar tersebut hanya karena pengguna tidak repot-repot memeriksa versi saat ini. Baik. Reporter nakal sedang nakal, masih lebih baik memastikan. MENURUT OPINI SAYA.

Tidak ada argumen di sana, tetapi memberikan _pembingkaian yang tepat dan konteks_ ke _asides_ semacam itu penting, terutama di utas looooong di mana komentar asli - diperlukan untuk konteks - telah diposting lama dan, pada kenyataannya, sekarang _hidden_ ​​secara default (tidak ada salahnya untuk benar-benar _link_ ke komentar asli yang dikutip).

Saya ingin tahu apakah diskusi ini membuat perbedaan. Jelas keputusan komite tidak tergantung pada apa yang diinginkan komunitas. Lihat tagnya: Resolution- won't fix. Tetapi karena PowerShell adalah open source (MIT), komunitas dapat memperbaikinya di garpu terpisah, dan menyingkat PowerShellCommunity ( pwshc ) ini.

Ini menghilangkan kebutuhan akan kompatibilitas ke belakang. Nanti di PowerShell 8, panitia mungkin mengintegrasikan garpu.

Tentang kompatibilitas mundur: PowerShell tidak diinstal sebelumnya pada sistem operasi apa pun, dan bagi saya, kesulitan menginstal PowerShellCommunity sama dengan PowerShell . Saya lebih suka menginstal versi komunitas dan langsung menggunakannya daripada menunggu beberapa versi 8 di masa mendatang (atau membuat kode lebih kompleks dengan operator baru).

Karena Komite telah memutuskan untuk tidak memperbaikinya, lebih baik mengetahui seberapa parah kerusakannya. Saya pikir komunitas dapat hidup dengan Invoke-Native yang melakukan hal yang benar. Bukan tugas komunitas untuk menyelamatkan wajah Microsoft yang bertentangan dengan keinginan mereka.

lebih baik mengetahui seberapa parah mereka rusak

Saya sepenuhnya setuju - bahkan jika masalah tidak dapat diatasi pada saat itu, mengetahui bagaimana melakukan hal-hal dengan benar _ pada prinsipnya, jika dan ketika saatnya tiba_ itu penting - bahkan jika waktu _tidak ada_ dalam konteks bahasa tertentu.

komunitas dapat hidup dengan Invoke-Native yang melakukan hal yang benar

Untuk lebih jelasnya:

  • Sesuatu seperti Invoke-NativeShell mengalamatkan _different use case_ - yang merupakan subjek dari # 13068 - dan untuk kasus penggunaan tersebut cmdlet seperti itu _not_ stopgap: ini adalah solusi yang tepat - lihat https://github.com/ PowerShell / PowerShell / issues / 13068 # issue -656781439

  • Masalah _This_ adalah tentang memperbaiki _PowerShell itu sendiri_ (ini bukan tentang fungsionalitas platform-_native_), dan solusi _stopgap_ untuk itu adalah menyediakan upacara rendah _opt-in_ - maka proposal untuk mengirimkan fungsi iep sebagai -in, menunggu perbaikan _proper_ di versi mendatang yang diizinkan untuk merusak kompatibilitas ke belakang secara substansial.

Bukan tugas komunitas untuk menyelamatkan wajah Microsoft

Saya tidak berpikir bahwa perhatian

Karena itu, saya tidak yakin bahwa memecah ekosistem PowerShell dengan garpu adalah cara yang tepat.

Saya tidak punya waktu untuk menanggapi semua ini saat ini, tapi saya pikir ini masuk akal, jadi saya membuka kembali:

Bisakah Anda setidaknya membiarkan masalah ini terbuka sampai operator itu benar-benar ada dan seseorang dapat menunjukkan, bagaimana operator itu akan meningkatkan sesuatu, yang disebutkan di utas ini?

Seperti yang saya katakan dalam masalah terkait, operator panggilan asli menambahkan kompleksitas di UX dan itu salah arah.
Pada saat yang sama, seluruh pembahasan di sini adalah tentang menyederhanakan interaksi dengan aplikasi asli.

Satu-satunya hal yang menghentikan kita adalah bahwa itu adalah perubahan yang menghancurkan. Kami telah berkali-kali mengatakan bahwa ini terlalu banyak kerusakan, tetapi mari kita pertimbangkan.

Mari kita lihat sesi interaktif. Pengguna akan menginstal versi PowerShell baru dan menemukan perilaku baru saat menjalankan aplikasi asli. Apa yang akan dia katakan? Saya akan berspekulasi bahwa dia akan berkata - "Terima kasih - akhirnya saya bisa mengetik dan berhasil!".

Mari kita lihat skenario eksekusi / hosting skrip. Versi baru aplikasi apa pun (bahkan dengan satu perubahan kecil!) Dapat merusak proses bisnis. Kami selalu mengharapkan ini dan memeriksa proses kami setelah pembaruan apa pun. Jika kami menemukan masalah, maka kami memiliki beberapa cara:

  • kembalikan pembaruan
  • persiapkan perbaikan cepat untuk skrip
  • matikan fitur yang merusak skrip kita sampai hal-hal ini diperbaiki atau mati seiring waktu.

_Sejak pembaruan versi selalu merusak sesuatu, opsi terakhir adalah yang terbaik yang dapat kami miliki dan terima._

(Saya ingin menunjukkan bahwa saat ini PowerShell Core bukan komponen Windows dan oleh karena itu tidak dapat merusak aplikasi hosting secara langsung, hanya skrip yang terpengaruh secara langsung.)

Saya ingin mengingatkan Anda apa yang dikatakan Jason di atas - ini adalah perbaikan bug.
Mari kita perbaiki dan sederhanakan semuanya untuk semua orang. Biarkan pengguna bekerja powershell-y .

Seperti yang saya katakan dalam masalah terkait, operator panggilan asli menambahkan keterlibatan di UX dan itu salah arah.

kompleksitas ❓

Mari kita lihat sesi interaktif. Pengguna akan menginstal versi PowerShell baru dan menemukan perilaku baru saat menjalankan aplikasi asli. Apa yang akan dia katakan? Saya akan berspekulasi bahwa dia akan berkata - "Terima kasih - akhirnya saya bisa mengetik dan berhasil!".

Saya memiliki skenario berbeda: pengguna baru mencoba PowerShell, menyadari bahwa PowerShell gagal secara misterius, pindahkan ke Recycle Bin dan jangan pernah kembali. Itulah yang akan saya lakukan.

persiapkan perbaikan cepat untuk skrip

Itu adalah sesuatu yang harus kita lakukan untuk menutupi kasus yang jelas dalam skrip pengguna.

menyadari bahwa itu gagal secara misterius

Seluruh diskusi di sini hanyalah tentang menyingkirkan "kegagalan misterius" ini. Dan yang terbaik adalah melakukan ini dengan menyederhanakan tetapi tidak menambahkan hal-hal misterius baru.

Seluruh diskusi di sini hanya tentang menyingkirkan ini. Dan yang terbaik adalah melakukan ini dengan menyederhanakan tetapi tidak menambahkan hal-hal misterius baru.

Kami tidak ingin menghilangkan kemampuan untuk secara langsung menjalankan program yang dapat dieksekusi.

Doa lama juga tidak langsung ke cmdline dengan menebak apakah sesuatu sudah dikutip. Selain itu, kemampuan tersebut akan tetap dipertahankan dengan -%.

Selain itu, kemampuan tersebut akan tetap dipertahankan dengan -%.

--% membutuhkan baris perintah yang sudah ditentukan sebelumnya, jadi utilitasnya terbatas.

@ yuliarrrr

Kami tidak ingin menghilangkan kemampuan untuk secara langsung menjalankan program yang dapat dieksekusi.

Menurut saya @iSazonov berarti menyingkirkan _ perilaku buggy_, yaitu memperbaiki bug dengan benar (tanpa ikut serta melalui sintaks tambahan), meskipun itu berarti merusak solusi yang ada. Benar, @iSazonov?

@ Artoria2e5 :

Doa lama juga tidak langsung ke cmdline dengan menebak apakah sesuatu sudah dikutip

[_Update_: Saya salah membaca baris yang dikutip, tapi semoga informasinya masih menarik.]

  • Anda tidak perlu _guess_, dan aturannya sederhana:

    • _Hanya jika_ nama perintah atau jalur Anda adalah _quoted_ - '/foo bar/someutil '- dan / atau berisi _variabel referensi (atau ekspresi) - $HOME/someutil - & adalah _required_.
  • Meskipun Anda mungkin menganggap kebutuhan ini tidak menguntungkan, hal ini merupakan inti dari bahasa PowerShell, dan diharuskan dengan kemampuan untuk membedakan secara sintaks antara dua mode penguraian fundamental, mode argumen dan mode ekspresi.

    • Perhatikan bahwa masalah ini tidak khusus untuk memanggil _external program_; Perintah PowerShell asli juga memerlukan & untuk pemanggilan jika ditentukan melalui referensi string / variabel yang dikutip.
  • Jika Anda tidak ingin menghafal aturan sederhana ini, aturan yang lebih sederhana adalah: _selalu_ gunakan & , dan Anda akan baik-baik saja - ini agak kurang nyaman daripada _tidak_ membutuhkan apa pun, tetapi tidak terlalu sulit (dan lebih pendek dari --% , yang merupakan solusi yang salah - lihat di bawah)

kemampuan tersebut akan tetap dipertahankan dengan -%.

[_Update_: Saya salah membaca baris yang dikutip, tapi semoga informasinya masih menarik.]

Tidak, terlepas dari penggabungan yang tidak menguntungkan dari # 13068 dengan masalah _this_, --% - kemampuan untuk memanggil _native shell_, menggunakan _its_, selalu sintaks _platform-spesifik_ - sama sekali bukan solusi untuk masalah yang sedang dihadapi dan berdiskusi itu hanya menambah kebingungan .

--% adalah kasus penggunaan yang sangat berbeda dan, seperti yang saat ini diusulkan, memiliki keterbatasan yang parah; Jika ada sesuatu yang _not_ layak untuk diperkenalkan _operator_ untuk (atau mengubah perilaku yang sudah ada), itu adalah kemampuan untuk meneruskan baris perintah verbatim ke shell asli (yang, tentu saja, Anda sudah bisa melakukannya sendiri, dengan sh -c '...' pada Unix, dan cmd /c '...' , _tetapi hanya kuat jika masalah ini diperbaiki_; implementasi biner Invoke-NativeShell / ins cmdlet, sementara mengabstraksi rincian shell target Sintaks CLI, akan menghindari masalah yang dihadapi (dengan penggunaan langsung System.Diagnostics.ProcessStartInfo.ArgumentList ), dan karena itu dapat diimplementasikan secara independen).

@ yuliarrrr

Saya memiliki skenario berbeda: pengguna baru mencoba PowerShell, menyadari bahwa PowerShell gagal secara misterius, pindahkan ke Recycle Bin dan jangan pernah kembali. Itulah yang akan saya lakukan.

Bisakah Anda menjelaskan: Apakah Anda takut, bahwa perilaku PowerShell saat ini dapat menyebabkan pengalaman pengguna yang tidak menyenangkan ini, atau apakah Anda takut bahwa perubahan yang diusulkan mengarah pada pengalaman pengguna tersebut.

Karena menurut saya perilaku PowerShell saat ini jauh lebih mungkin untuk menghasilkan pengalaman seperti itu. Seperti yang dikatakan di atas: Perilaku PowerShell saat ini harus dianggap sebagai bug.

Mengapa pengguna baru, tanpa pengetahuan tentang PowerShell yang rusak, mengeluarkan perintah dengan argumen yang berubah-ubah?

@ yecril71pl Hanya untuk benar-benar yakin: Anda menganggap formulir yang saat ini diperlukan untuk argumen "berkerut", bukan perbaikan yang disarankan.

Saya menganggap pertanyaan Anda berasal dari anggapan Anda bahwa saya adalah seorang kutu buku yang tidak dapat disembuhkan. Itu benar — tetapi saya telah mempertahankan kemampuan untuk membayangkan apa yang dianggap berkerut oleh orang normal biasa.

@bayu_joo
Saya pikir, @ Artoria2e5 berbicara tentang mengubah array argumen menjadi satu string lpCommandLine ketika mengatakan

Doa lama juga tidak langsung ke cmdline dengan menebak apakah sesuatu sudah dikutip

Karena saat menelepon

echoargs.exe 'some"complicated_argument'

Anda memang harus menebak-nebak, apakah PowerShell menambahkan kutipan sekitar some"complicated_argument .

Contoh 1: Di sebagian besar versi PowerShell
echoarg.exe 'a\" b' dan echoarg.exe '"a\" b"' akan diterjemahkan ke
"C:\path\to\echoarg.exe" "a\" b" (versi teruji 2.0; 4.0; 6.0.0-alpha.15; 7.0.1)
tapi PowerShell default saya di Win10 (versi 5.1.18362.752) diterjemahkan
echoarg.exe 'a\" b' menjadi "C:\path\to\echoarg.exe" a\" b dan
echoarg.exe '"a\" b"' hingga "C:\path\to\echoarg.exe" ""a\" b"" .

Contoh 2: Terjemahan versi PowerShell yang lebih lama
echoarg.exe 'a"b c"' hingga "C:\path\to\echoarg.exe" "a"b c"" (versi teruji 2.0; 4.0;)
sedangkan versi yang lebih baru menerjemahkan
echoarg.exe 'a"b c"' hingga "C:\path\to\echoarg.exe" a"b c" (versi uji 5.1.18362.752; 6.0.0-alpha.15; 7.0.1).


Karena perilakunya jelas sudah diubah beberapa kali, saya tidak mengerti mengapa itu tidak bisa diubah sekali lagi untuk mendapatkan perilaku yang diharapkan.

Begitu ,

Adapun masalah sebenarnya: kami 100% setuju - pada kenyataannya, Anda bahkan tidak perlu memikirkan tentang apa yang akhirnya lpCommandLine digunakan di belakang layar pada Windows; jika PowerShell melakukan hal yang benar, tidak ada yang harus melakukannya (kecuali untuk kasus edge, tetapi itu bukan kesalahan PowerShell, dan saat itulah --% (seperti yang saat ini diterapkan) dapat membantu; dengan perbaikan yang tepat, tidak akan pernah ada menjadi kasus tepi pada platform mirip Unix).

Adapun hanya memperbaiki masalah dengan benar: Anda pasti memiliki suara saya (tetapi solusi yang ada akan rusak).

TL; DR: Asumsi bahwa kami dapat meneruskan nilai apa pun dengan andal sebagai argumen ke program apa pun di subsistem Microsoft Windows NT adalah salah , jadi kami harus berhenti berpura-pura bahwa ini adalah tujuan kami. Namun, masih banyak yang harus diselamatkan jika kita mempertimbangkan sejauh mana argumen tersebut.

Saat menjalankan file executable Windows asli, kita harus mempertahankan kutipan asli. Contoh:

CMD /CSTART="WINDOW TITLE"

Sistem tidak dapat menemukan file WINDOW.

 { CMD /CSTART="WINDOW TITLE" }. Ast. EndBlock. Statements. PipelineElements. CommandElements[1]

StringConstantType
BareWord
Nilai
/ CSTART = JUDUL JENDELA
StaticType
System.String
Tingkat
/ CSTART = "JUDUL JENDELA"
Induk
CMD / CSTART = "JUDUL JENDELA"

Jika kita mengambil luasnya sebagai template, kita tidak akan kehilangan apapun dan kita dapat memanggil executable asli seperti yang diharapkan. Solusi menggunakan argumen string berfungsi di sini, tetapi menurut saya secara teknis tidak perlu untuk melakukannya, asalkan dukungan yang tepat diterapkan dalam PowerShell. Pendekatan ini akan berhasil untuk semua kasus.

Tanda kutip dalam kutip menghadirkan masalah yang tidak dapat diatasi karena ada alat yang menafsirkan pelolosan garis miring terbalik ( TASKLIST "\\\"PROGRAM FILES" ) dan alat yang tidak ( DIR "\""PROGRAM FILES" /B ) dan alat yang tidak mengganggu ( TITLE A " B ). Namun, jika kita melarikan diri, pelarian standar dengan garis miring terbalik meracuni semua alat pengelola file umum karena mereka sama sekali tidak mendukung tanda kutip sama sekali dan garis miring terbalik ganda \\ berarti sesuatu yang sama sekali berbeda bagi mereka (coba DIR "\\\"PROGRAM FILES" /B ), jadi mengirim argumen dengan tanda kutip di dalamnya seharusnya merupakan kesalahan waktu proses. Tapi kita tidak bisa melempar kesalahan karena kita tidak tahu yang mana yang mana. Meskipun menggunakan mekanisme pelolosan normal seharusnya tidak menyebabkan kerusakan pada argumen yang tidak mengandung tanda kutip, kami tidak dapat memastikan bahwa, ketika diterapkan pada argumen yang memuatnya dan dimasukkan ke alat yang tidak mendukung tanda kutip sebagai nilai, itu akan menyebabkan kesalahan pengguguran daripada perilaku tak terduga, dan perilaku tak terduga memang akan sangat buruk. Ini adalah beban serius yang kami bebankan kepada pengguna. Selain itu, kami tidak akan pernah dapat menyediakan alat 'tidak peduli' ( CMD /CECHO='A " B' ).

Perhatikan bahwa variabel lingkungan tidak mewakili nilai dalam CMD , mereka mewakili fragmen kode yang direparsing saat variabel lingkungan diperluas, dan tidak ada ketentuan untuk memperlakukannya secara andal sebagai argumen untuk perintah lain. CMD hanya tidak beroperasi pada objek dalam bentuk apapun, bahkan string, yang tampaknya menjadi akar penyebab dari teka-teki ini.

TL; DR: Asumsi bahwa kami dapat dengan andal meneruskan nilai apa pun sebagai argumen ke program apa pun di subsistem Microsoft Windows NT adalah _wrong_, jadi kami harus berhenti berpura-pura bahwa ini adalah tujuan kami.

Itu seharusnya menjadi tujuan, bukankah seharusnya begitu? Bukan masalah PowerShell jika program tidak dapat menafsirkan argumen yang diterimanya.

Saat menjalankan file executable Windows asli, kita harus mempertahankan kutipan asli. Contoh:

CMD /CSTART="WINDOW TITLE"

Apakah Anda menyarankan memanggil program agar secara dinamis mengubah bahasa dari PowerShell menjadi apa pun yang digunakan oleh program yang dipanggil? Anda menulis contoh itu di PowerShell, yang artinya harus setara dengan salah satu dari berikut ini

CMD "/CSTART=WINDOW TITLE"
CMD '/CSTART=WINDOW TITLE'
CMD /CSTART=WINDOW` TITLE

TL; DR: Asumsi bahwa kami dapat dengan andal meneruskan nilai apa pun sebagai argumen ke program apa pun di subsistem Microsoft Windows NT adalah _wrong_, jadi kami harus berhenti berpura-pura bahwa ini adalah tujuan kami.

Itu seharusnya menjadi tujuan, bukankah seharusnya begitu? Bukan masalah PowerShell jika program tidak dapat menafsirkan argumen yang diterimanya.

Program CMD dapat menafsirkan argumen /CECHO=A " B tetapi PowerShell tidak dapat meneruskannya tanpa mendistorsi.

Saat menjalankan file executable Windows asli, kita harus mempertahankan kutipan asli. Contoh:

CMD /CSTART="WINDOW TITLE"

Apakah Anda menyarankan memanggil program agar secara dinamis mengubah bahasa dari PowerShell menjadi apa pun yang digunakan oleh program yang dipanggil? Anda menulis contoh itu di PowerShell, yang artinya harus setara dengan salah satu dari berikut ini

CMD "/CSTART=WINDOW TITLE"
CMD '/CSTART=WINDOW TITLE'
CMD /CSTART=WINDOW` TITLE

Saya mencoba menyarankan bahwa, saat berinteraksi dengan program eksternal di bawah subsistem Microsoft Windows NT, PowerShell memiliki banyak cara untuk menyandikan argumen yang semuanya setara dengan PowerShell tetapi tidak setara dengan program penerima. Bersikap blak-blakan dan memaksakan One True Way ™ dari argumen pengkodean, tanpa memperhatikan pengaturan kutipan apa yang sebenarnya digunakan pengguna, tidak membantu, untuk mengatakannya secara halus.

@ yecril71pl Saya benar-benar bingung dengan komentar Anda. Apa sebenarnya yang Anda usulkan di sini? Semua kasus penggunaan Anda dilindungi oleh --% . Anda menolaknya sebelumnya dengan mengatakan

--% membutuhkan baris perintah yang telah ditentukan sebelumnya, jadi utilitasnya terbatas.

Namun nyatanya Anda dapat menggunakan variabel lingkungan dengan --% . Coba ini:

PS > $env:mytitle='WINDOW TITLE'
PS > cmd --% /CSTART="%mytitle%"

Jadi apa yang saya lewatkan?

Kami kehilangan sintaks CMD /CSTART="$mytitle" , tanpa membocorkan sesuatu ke ENV: .

Sebagai ide yang buruk, kami memiliki opsi untuk mengganti Environment.ExpandEnvironmentVariables dengan yang lain. Tidak ada implementasi asli di Unix, dan saya tidak percaya hal-hal yang diprosesnya akan menjadi kinerja-kritis ketika ditulis ulang dalam C #.

Karena tanda yang sama tidak diperbolehkan dalam nama env var, kita dapat memiliki %=$a% mean $a . Ini tidak akan merusak apapun yang ada sementara memungkinkan untuk beberapa ekstensi yang sangat fleksibel (dan mungkin buruk) seperti membuatnya bekerja seperti string template JS. Sial, kita dapat mendefinisikan %VARNAME=$var% sebagai semacam sintaks mundur juga.

Adapun dokumentasi sih ini akan menyebabkan ... Saya minta maaf.

  • Kami _not_ memiliki masalah penguraian.

  • Apa yang kami miliki adalah masalah dengan _bagaimana PowerShell meneruskan argumen verbatim dan dirangkai yang dihasilkan dari _its_ parsing ke executable eksternal (asli )_:

    • Di _Windows_, masalahnya adalah bahwa baris perintah untuk menjalankan eksekusi eksternal dengan yang dibangun di belakang layar _not_ mematuhi konvensi yang paling banyak digunakan untuk mengutip argumen , seperti yang dijelaskan dalam perintah Parsing C ++ dokumentasi kompilator Microsoft C / C ++- Bagian

    • Apa yang terjadi saat ini bahkan bukan karena konvensi _different_ digunakan: mungkin karena adanya pengawasan, baris perintah yang dibangun secara situasional _syntactically secara fundamental rusak_, bergantung pada spesifik argumen, yang berkaitan dengan kombinasi tanda kutip ganda dan spasi yang disematkan serta argumen string kosong.

    • Pada akhirnya, masalahnya adalah arsitektur dasar pembuatan proses di Windows: Anda dipaksa untuk menyandikan argumen untuk diteruskan ke proses _sebagai baris perintah_ - string tunggal yang mewakili _all_ argumen - daripada meneruskannya sebagai _array_ argumen (yang mana adalah bagaimana platform mirip Unix melakukannya). Kebutuhan untuk meneruskan baris perintah membutuhkan _quoting dan escaping rules_ untuk diimplementasikan, dan pada akhirnya _untuk setiap program_ cara menafsirkan baris perintah yang diberikan. Akibatnya, ini berarti program yang tidak perlu dipaksa menjadi semacam mini-shell: mereka dipaksa untuk _ melakukan ulang_ tugas yang telah dilakukan oleh shell, yang merupakan tugas yang harus menjadi lingkup dari _shell only_ (sebagaimana adanya kasus di Unix), yaitu mem-parsing baris perintah menjadi argumen individual. Singkatnya, ini adalah anarki yang merupakan argumen yang diteruskan pada Windows .

    • Dalam praktiknya, anarki dimitigasi oleh program _most_ yang mengikuti konvensi yang disebutkan di atas, dan program baru yang sedang dikembangkan kemungkinan besar akan mematuhi konvensi tersebut, terutama karena runtime yang banyak digunakan yang mendukung aplikasi konsol mengimplementasikan konvensi ini (seperti Microsoft C / C ++ / .NET runtime). Karena itu, solusi yang masuk akal adalah:

      • Buat PowerShell mematuhi konvensi ini saat membuat baris perintah di belakang layar.
      • Untuk program "nakal" yang _tidak_ mematuhi konvensi ini - yang terutama mencakup cmd.exe , file batch, dan utilitas Microsoft seperti msiexec.exe dan msdeploy.exe - menyediakan mekanisme untuk secara eksplisit mengontrol baris perintah yang diteruskan ke target yang dapat dieksekusi; inilah yang --% , simbol stop-parsing menyediakan - meskipun agak canggung .
    • Di _Unix_, masalahnya adalah _a baris perintah sedang dibuat_ - sebaliknya, array argumen verbatim harus diteruskan _as-is_ , yang sekarang didukung .NET Core (sejak v2.1, melalui ProcessStartInfo.ArgumentList ; seharusnya _always_ mendukung ini, mengingat - secara masuk akal - _tidak ada baris perintah_, hanya array argumen, ketika proses dibuat pada platform mirip Unix).

    • Setelah kami menggunakan ProcessStartInfo.ArgumentList , semua masalah di Unix akan hilang.

Memperbaiki masalah ini adalah tentang https://github.com/PowerShell/PowerShell-RFC/pull/90 @TSlivede .

Di https://github.com/PowerShell/PowerShell-RFC/pull/90#issuecomment -650242411 saya telah mengusulkan tambahan kompensasi otomatis untuk "ketidaksesuaian" dari file batch, mengingat penggunaannya yang masih sangat luas sebagai titik masuk CLI untuk high -profil perangkat lunak seperti Azure (CLI az diimplementasikan sebagai file batch, az.cmd ).
Demikian pula, kita harus mempertimbangkan melakukan hal yang sama untuk msiexec.exe dan msdeploy.exe dan mungkin Microsoft CLI "nakal" profil tinggi lainnya.


Saya baru saja menerbitkan modul, Native , ( Install-Module Native -Scope CurrentUser ) yang membahas semua hal di atas melalui fungsi ie (kependekan dari i nvoke (external) e xecutable; it adalah implementasi yang lebih lengkap dari fungsi iep diperkenalkan di atas ).

Ini juga termasuk ins ( Invoke-NativeShell ) , yang beralamat # 13068, dan dbea ( Debug-ExecutableArguments ) untuk mendiagnosis kelulusan argumen - lihat https: // github. com / PowerShell / PowerShell / issues / 13068 # Issuecomment -671572939 untuk detailnya.

Dengan kata lain: ie dapat berfungsi sebagai stopgap yang tidak mengganggu sementara kita menunggu masalah ini diperbaiki, cukup dengan mengawali pemanggilan dengan ie sebagai perintah:

Dari pada:

# This command is currently broken, because the '{ "name": "foo" }' argument isn't properly passed.
curl.exe -u jdoe  'https://api.github.com/user/repos' -d '{ "name": "foo" }'

Anda akan menggunakan yang berikut ini:

# OK, thanks to `ie`
ie curl.exe -u jdoe  'https://api.github.com/user/repos' -d '{ "name": "foo" }'

Adapun contoh CMD /CSTART="WINDOW TITLE" (bentuk yang lebih idiomatis adalah cmd /c start "WINDOW TITLE" , yang sudah berfungsi):

Ini pada dasarnya masalah yang sama dengan argumen prop="<value with spaces>" untuk msiexec / msdeploy : PowerShell - dapat dibenarkan - mengubah /CSTART="WINDOW TITLE" menjadi "/CSTART=WINDOW TITLE" , yang, bagaimanapun, mematahkan permintaan cmd.exe .

Ada dua cara untuk mengatasinya:

  • Delegasikan ke ins / Invoke-NativeShell (perhatikan bahwa penggunaan cmd.exe /c secara efektif tersirat):

    • ins 'START="WINDOW TITLE"'
    • Jika Anda menggunakan string _expandable_, Anda dapat menyematkan nilai PowerShell di string perintah.

      • $title = 'window title'; ins "START=`"$title`""

  • Alternatifnya, gunakan implementasi --% , tetapi waspadalah terhadap batasannya :

    • cmd --% /CSTART="WINDOW TITLE"
    • Sebagaimana dibahas, batasan bermasalah --% adalah bahwa satu-satunya cara untuk menyematkan nilai _PowerShell_ adalah dengan menggunakan _aux. variabel lingkungan_ dan referensikan dengan sintaks %...% :

      • $env:_title = 'window title'; cmd --% /CSTART="%_title%"

      • Untuk menghindari batasan ini, --% harus selalu diimplementasikan dengan argumen string _single_ - misalnya,

        cmd --% '/CSTART="WINDOW TITLE"' atau cmd --% "/CSTART=`"$title`"" - tetapi ini tidak dapat diubah tanpa merusak kompatibilitas ke belakang, jadi simbol _new_ harus diperkenalkan - secara pribadi, saya tidak melihat perlunya untuk itu.

  • mereka dipaksa untuk _ melakukan ulang_ tugas yang telah dilakukan oleh shell

Saya tidak berpikir CMD.EXE membagi baris perintah menjadi argumen, satu-satunya hal yang diperlukan adalah untuk mengetahui mana yang dapat dieksekusi untuk dipanggil dan sisanya hanya baris perintah seperti yang ditulis oleh pengguna (setelah substitusi variabel lingkungan, yang mana dilakukan tanpa memperhatikan batasan argumen). Tentu saja, perintah shell internal merupakan pengecualian di sini.

Ini pada dasarnya masalah yang sama dengan argumen prop="<value with spaces>" untuk msiexec / msdeploy

Saya juga bukan pengguna yang percaya diri, jadi saya lebih suka mengemukakan sesuatu yang lebih saya kenal.

Untuk lebih jelasnya: hal-hal berikut tidak berdampak pada poin-poin yang dibuat dalam komentar saya sebelumnya.

Saya tidak berpikir CMD.EXE membagi baris perintah menjadi argumen

  • Itu bisa lolos tanpa pemisahan _explicit_ saat memanggil _external executables_ (perintah dijalankan oleh executable lain dalam proses anak), tapi itu harus melakukannya untuk _batch files_.

  • Bahkan saat memanggil executable eksternal, ia harus _ menyadari_ batas argumen, untuk menentukan apakah metakarakter yang diberikan (misalnya & ) memiliki fungsi _syntactic_ atau apakah itu bagian dari argumen kutip ganda dan karenanya diperlakukan sebagai literal:

:: OK - the "..." around & tells cmd.exe to use it verbatim
C:\>echoArgs.exe one "two & three"
Arg 0 is <one>
Arg 1 is <two & three>

Command line:
"C:\ProgramData\chocolatey\lib\echoargs\tools\EchoArgs.exe" one "two & three"

Juga, cmd.exe mengenali _embedded_ " chars. di "..." string dikenali jika di-escape sebagai "" :

:: OK - the "" is recognized as an escaped "
C:\>echoArgs.exe "3"" of rain & such."
Arg 0 is <3" of rain & such.>

Command line:
"C:\ProgramData\chocolatey\lib\echoargs\tools\EchoArgs.exe" "3"" of rain & such."

Sayangnya, cmd.exe _only_ mendukung (hanya untuk Windows) "" dan tidak juga yang lebih banyak digunakan \" (yang mirip dengan POSIX _shells_ pada Unix _exclusively_ use - catatan: _shells_, bukan _programs_, karena program hanya melihat array argumen kata demi kata yang dihasilkan dari penguraian shell).

Meskipun sebagian besar CLI di Windows mendukung _both_ "" dan \" , beberapa _only_ memahami \" (terutama Perl dan Ruby), dan kemudian Anda mengalami masalah:

:: !! BROKEN: cmd.exe misinterprets the & as *unquoted*, thinks it's the statement-sequencing operator, 
:: !! and tries to execute `such`:
C:\>echoArgs.exe "3\" of rain & such."
Arg 0 is <3" of rain >

Command line:
"C:\ProgramData\chocolatey\lib\echoargs\tools\EchoArgs.exe" "3\" of rain

'such."' is not recognized as an internal or external command,
operable program or batch file.

Karena itu:

  • Hindari menelepon cmd.exe secara langsung, jika memungkinkan.

    • Panggil eksternal yang dapat dieksekusi secara langsung (setelah masalah ini diperbaiki) atau melalui ie (untuk saat ini), menggunakan sintaks _PowerShell's_.
  • Jika Anda memang harus memanggil cmd.exe , gunakan ins / Invoke-NativeShell untuk kesederhanaan umum dan, khususnya, untuk betapa mudahnya menanamkan variabel PowerShell dan nilai ekspresi ke dalam baris perintah .

    • Alasan yang sah untuk tetap memanggil cmd.exe secara langsung adalah untuk mengkompensasi kurangnya dukungan PowerShell untuk data byte mentah di dalam pipeline - lihat jawaban SO ini sebagai contoh.

Saya tahu saya akan menangkap banyak kritik di sini, dan saya sangat menghargai kedalaman diskusi yang terjadi, tapi ... bebek ... apakah ada yang punya contoh dari semua ini yang benar-benar penting dalam skenario dunia nyata ?

Saya menganggap bahwa kami tidak diberdayakan di PowerShell untuk memecahkan "anarki" yang saat ini ada dengan parsing argumen Windows. Dan karena banyak alasan yang sama sehingga kami tidak dapat menyelesaikan masalah, ada alasan bagus bahwa kompiler Windows dan VC ++ telah memilih untuk tidak merusak perilaku ini. Ini merajalela, dan kita hanya akan menciptakan masalah baru (dan sebagian besar tidak dapat dipahami) jika kita mengubah sesuatu.

Untuk utilitas yang sudah lintas platform dan banyak digunakan antara Windows dan Linux (misalnya Docker, k8s, Git, dll.), Saya tidak melihat masalah ini terwujud di dunia nyata.

Dan untuk aplikasi "nakal" yang melakukan pekerjaan buruk: sebagian besar merupakan utilitas lawas, khusus Windows.

Saya setuju bahwa apa yang Anda jelaskan @ mklement0 sebagian besar merupakan solusi yang "benar". Saya hanya tidak tahu bagaimana menuju ke sana tanpa benar-benar mengacaukan segalanya.

Penggunaan yang cukup mendasar rusak:

❯ git commit --allow-empty -m 'this is what we call a "commit message" which contains arbitrary text, often with punctuation'
error: pathspec 'message which contains arbitrary text, often with punctuation' did not match any file(s) known to git
❯ $a = 'this is what we call a "commit message" which contains arbitrary text, often with punctuation'
❯ git commit --allow-empty -m "$a"
error: pathspec 'message which contains arbitrary text, often with punctuation' did not match any file(s) known to git
❯ $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.0.3
PSEdition                      Core
GitCommitId                    7.0.3
OS                             Microsoft Windows 10.0.19042
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

git.exe adalah aplikasi yang berperilaku baik, jadi perbaikan di PowerShell akan mudah, meskipun membutuhkan semua skrip di luar sana untuk mengembalikan pekerjaan cerdas mereka. cmd.exe lebih sulit untuk diadaptasi dan membutuhkan pendekatan yang jauh lebih penuh perhatian yang dapat memecahkan beberapa masalah tetapi mungkin tidak semuanya. Yang sebenarnya mengerikan, mengingat PowerShell dimulai sebagai alat Windows NT. Saya memahami pertanyaan ini sebagai _Apakah ada skenario kehidupan nyata ketika utilitas warisan berperilaku buruk seperti cmd.exe akan dipanggil dari PowerShell dengan cara yang menyebabkan masalah di antarmuka_. PowerShell mencoba mendekati masalah ini dengan menduplikasi sebagian besar fungsionalitas di cmd.exe , sehingga membuat cmd.exe berlebihan. Ini juga dimungkinkan untuk alat lain, misalnya MSI dapat dioperasikan melalui ActiveX, meskipun untuk melakukannya membutuhkan pengetahuan yang cukup. Jadi apakah ada hal penting yang tidak tercakup?

@ PowerShell / PowerShell-committee membahas hal ini. Kami menghargai contoh git yang dengan jelas menunjukkan contoh menarik di dunia nyata. Kami setuju bahwa kami harus memiliki fitur eksperimental di awal 7.2 untuk memvalidasi dampak pengambilan perubahan yang dapat merusak tersebut. Contoh pengujian tambahan menunjukkan bahwa bahkan --% memiliki masalah meskipun seharusnya tidak diurai:

PS> testexe --% -echoargs 'a b c "d e f " g h'
Arg 0 is <'a>
Arg 1 is <b>
Arg 2 is <c>
Arg 3 is <d e f >
Arg 4 is <g>
Arg 5 is <h'>

Ini tampaknya menjadi masalah dalam pengikat parameter perintah asli.

Ya, terima kasih @cspotcode. Contoh itu jelas merupakan momen aha bagi saya (terutama mengingat saya benar-benar pernah melakukannya di dunia nyata).

Saya masih khawatir tentang aspek perubahan yang melanggar, dan menurut saya ini adalah kandidat yang bisa untuk fitur eksperimental yang mungkin tetap eksperimental pada beberapa versi PowerShell, dan itu sama sekali bukan sesuatu yang kami yakin pada akhirnya akan berhasil.

Saya juga perlu menggali lebih dalam untuk memahami aspek allow list / "rouge app" dari RFC Anda, @ mklement0 , karena saya tidak yakin seberapa besar keinginan kami untuk mendaftar untuk mempertahankan daftar seperti itu.

@joeyaiello dan @ SteveL-MSFT, izinkan saya membuat pengamatan meta dulu:

Meskipun baik untuk melihat bahwa contoh @cspotcode memberi Anda _glimpse_ masalah, tanggapan Anda masih menunjukkan kurangnya pemahaman dan penghargaan mendasar dari (besarnya) masalah yang mendasarinya (saya akan memperdebatkan hal ini di komentar nanti) .

Ini bukan penilaian _personal_: Saya sepenuhnya menyadari betapa sulitnya menjadi sangat kurus dan harus membuat keputusan tentang berbagai subjek dalam waktu singkat.

Namun, hal ini menunjukkan masalah _struktural_: Bagi saya tampaknya keputusan dibuat secara rutin oleh komite @ PowerShell / powershell-berdasarkan pemahaman yang dangkal tentang masalah yang sedang dibahas, hingga merugikan komunitas secara luas.

Bagi saya, tanggapan panitia terhadap persoalan yang dibahas di sini adalah contoh paling konsekuensi dari masalah struktural tersebut hingga saat ini.

Karena itu, saya meminta Anda untuk mempertimbangkan ini:

Bagaimana dengan menunjuk _sub_-komite khusus subjek yang dikonsultasikan oleh komite dengan _do_ yang memiliki pemahaman yang diperlukan tentang masalah yang terlibat?

dapatkah anda membagikan konten testexe SteveL-MSFT, hanya ingin memastikan!

@TSlivede meringkas masalah dengan tepat di https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -665125375:

PowerShell di sisi lain mengklaim sebagai shell (sampai # 1995 diselesaikan, saya tidak akan mengatakan bahwa itu _is_ shell)

Seperti yang sering disebutkan sebelumnya, mandat inti dari sebuah shell adalah memanggil executable eksternal dengan argumen.

PowerShell saat ini gagal memenuhi mandat ini, karena argumen dengan tanda kutip ganda yang disematkan dan argumen string kosong tidak diteruskan dengan benar.

Seperti yang dinyatakan sebelumnya, ini mungkin bukan masalah di hari-hari khusus Windows, di mana kurangnya CLI eksternal yang mampu jarang muncul masalah ini, tetapi hari-hari ini hilang, dan jika PowerShell ingin memantapkan dirinya sebagai platform lintas yang kredibel shell, itu harus mengatasi masalah ini.

Contoh git @cspotcode adalah contoh yang bagus; apa pun yang dapat dieksekusi yang ingin Anda berikan string JSON - misalnya, curl - adalah yang lain:

# On Unix; on Windows, 
#   echoArgs.exe '{ "foo": "bar" }' 
# would show the same problem.
PS> /bin/echo '{ "foo": "bar" }'
{ foo: bar }  # !! Argument was incorrectly passed.

Mengesampingkan kompatibilitas ke belakang:

  • Di Unix, masalahnya secara sepele dan _completely_ diselesaikan dengan menggunakan ProcessStartInfo.ArgumentList belakang layar.

  • Di Windows, masalah ini secara sepele dan _mostly_ diselesaikan dengan menggunakan ProcessStartInfo.ArgumentList belakang layar.

    • Untuk kasus edge (CLI "nakal"), ada ( diterapkan dengan buruk ) --%
    • Sebagai _courtesy_, kita dapat mengkompensasi kasus edge terkenal tertentu untuk mengurangi kebutuhan --% - lihat di bawah.

Oleh karena itu, _secepat mungkin_, salah satu pilihan berikut harus dibuat:

  • Sadarilah pentingnya membuat pengoperan argumen berfungsi dengan baik dan _ perbaiki dengan mengorbankan kompatibilitas ke belakang_.

  • Jika kompatibilitas mundur benar-benar terpenting, sediakan operator baru atau fungsi seperti fungsi ie dari modul Native yang memperbaiki masalah, dan publikasikan secara luas sebagai satu-satunya cara yang dapat diandalkan untuk memanggil eksternal dapat dieksekusi.

Mengusulkan fitur _experimental_ untuk menangani fitur fundamental yang rusak parah sama sekali tidak memadai.


@ SteveL-MS

Bahkan mempertimbangkan penggunaan --% sebagai solusi untuk masalah ini pada dasarnya salah arah:

Ini adalah fitur _Windows-only_ yang hanya mengetahui "..." kutipan dan %...% -style referensi variabel lingkungan.

Di Unix, konsep "stopping parsing" pada dasarnya tidak berlaku: _tidak ada baris perintah_ untuk diteruskan ke proses turunan, hanya array argumen.

Jadi, _someone_ harus mengurai baris perintah menjadi argumen pemanggilan _before_, yang secara implisit didelegasikan ke kelas ProcessStartInfo , melalui properti .Arguments , yang pada Unix menggunakan konvensi _Windows_ untuk mengurai baris perintah - dan karena itu hanya mengenali "..." kutipan (dengan keluar dari " disematkan sebagai "" atau \" ) saja.

--% adalah fitur khusus Windows yang satu-satunya tujuan sah adalah untuk memanggil CLI "nakal".


@joeya

bahwa Windows dan kompiler VC ++ telah memilih untuk tidak merusak perilaku ini.

Compiler VC ++ _menerapkan konvensi_ yang masuk akal dan diamati secara luas , untuk menertibkan anarki.

Tepatnya kepatuhan pada konvensi inilah yang diadvokasi di sini , dimana penggunaan ProcessStartInfo.ArgumentList secara otomatis akan memberi kita.

_Ini saja akan mencakup sebagian besar panggilan. Mencakup SEMUA panggilan tidak mungkin dan memang bukan tanggung jawab PowerShell._

Seperti yang dinyatakan, untuk CLI "nakal" yang memerlukan bentuk kutipan non-konvensional, --% harus digunakan (atau ins / Invoke-NativeShell dari modul Native ).

_Sebagai rasa hormat_, kami dapat secara otomatis mengkompensasi skenario "nakal" yang terkenal, yaitu memanggil file batch dan Microsoft CLI profil tinggi tertentu:

  • Kasus file batch adalah yang umum, dan mudah dijelaskan dan dikonseptualisasikan (misalnya, berikan a&b sebagai "a&b" , meskipun seharusnya tidak memerlukan kutipan) - ini akan menghindari kebutuhan untuk digunakan dari --% dengan semua CLI yang menggunakan file batch sebagai titik masuknya (yang cukup umum), seperti Azure az.cmd

  • Alternatif pengecualian hard-coding untuk CLI tertentu - yang memang bisa membingungkan - adalah mendeteksi _pattern_ berikut dalam argumen yang dihasilkan dari penguraian PowerShell - <word>=<value with spaces> - dan, alih-alih meneruskan "<word>=<value with spaces>" , seperti yang saat ini terjadi, melewati <word>="<value with spaces>" ; yang terakhir memenuhi CLI "nakal", sementara juga diterima oleh CLI yang mematuhi konvensi; misalnya, echoArgs "foo=bar baz" akhirnya melihat argumen pertama yang sama sebagai echoArgs --% foo="bar baz"

@musm Anda dapat menemukan kode sumber TestExe di https://github.com/PowerShell/PowerShell/blob/master/test/tools/TestExe/TestExe.cs.

GitHub
PowerShell untuk setiap sistem! Berkontribusi pada pengembangan PowerShell / PowerShell dengan membuat akun di GitHub.

Saya pikir mengakomodasi pengecualian secara default hanya akan mengarah pada situasi yang sama dengan situasi saat ini, di mana orang perlu mengembalikan "kegunaan" PowerShell. Jika ada pengecualian, harus jelas bahwa pengecualian tersebut diterapkan.

Mungkin sesuatu seperti:

# Arguments passed correctly, without regard for the program's ability to handle them
& $program a "" 'c "d e" f'
# Try to pass the arguments intelligently based on the program being called
&[] $program a "" 'c "d e" f'
# Escape the arguments for a batch file, eg) " -> ""
&[bat] $program a "" 'c "d e" f'

Saya benar-benar kesulitan menemukan sintaks untuk ini yang tidak rusak. Setidaknya hal ini masuk akal jika Anda menganggapnya sebagai mentransmisikan program, tetapi mentransmisikan variabel aktual yang berisi program akan memerlukan tanda kurung yang dilampirkan.

Itu, selain memungkinkan orang menambahkan pengecualian untuk perilaku rusak apa pun yang mereka inginkan semoga menghilangkan kebutuhan akan --% . Kelas pengecualian yang mereka daftarkan kemudian akan memiliki metode untuk menentukan apakah itu berlaku untuk program (untuk pemanggilan cerdas), dan metode pelolosan di mana Anda hanya membuang pohon sintaks abstrak perintah ke sana dan mengembalikan argumen array / string.

  • alih-alih meneruskan "<word>=<value with spaces>" , seperti yang saat ini terjadi, meneruskan <word>="<value with spaces>"

Kutipan yang digunakan untuk membuat baris perintah harus mengikuti cara panggilan dikutip di PowerShell. Karena itu:

  1. Panggilan yang tidak mengandung tanda kutip ganda di teksnya harus meletakkan tanda kutip ganda di sekitar nilai keseluruhan.
  2. Panggilan yang berisi tanda kutip ganda harus mempertahankannya seperti yang tertulis dalam skrip _ jika memungkinkan_.

Khususnya:
| argumen skrip | baris perintah |
| ----------------- | ---------------- |
| p=l of v | "p = l dari v" |
| p=l` of` v | "p = l dari v" |
| p="l of v" | p = "l dari" |
| p="l of v"'a s m' | p = "l dari" a "sm" |
| p="l of v"' s m' | p = "l dari vsm" |

Baris terakhir menunjukkan contoh di mana tidak mungkin mempertahankan tanda kutip ganda asli.

Saya tahu saya akan menangkap banyak kritik di sini, dan saya sangat menghargai kedalaman diskusi yang terjadi, tapi ..._ bebek _... apakah ada yang punya contoh dari semua ini yang benar-benar penting dalam skenario dunia nyata ?

Saya sudah menyebutkannya di utas ini setahun yang lalu (sekarang tersembunyi ...) bahwa saya biasanya mendapatkan banyak momen WFT saat menggunakan ripgrep di Powershell. Saya tidak mengerti mengapa saya tidak bisa mencari string yang dikutip. Itu mengabaikan kutipan saya:

rg '"quoted"'

dan di git bash tidak.

Sekarang saya mendapatkan lebih sedikit momen WTF ini karena sayangnya saya menemukan masalah github yang panjang ini dan menemukan bahwa mengirimkan " ke Powershel benar-benar rusak. Contoh "git.exe" terbaru juga bagus.

Sejujurnya, sekarang saya bahkan tidak berani menggunakan Powershell untuk memanggil perintah asli ketika saya tahu saya mungkin mengirimkan " dalam string sebagai parameter. Saya tahu saya mungkin mendapatkan hasil atau kesalahan yang salah.

Sungguh, @ mklement0 menyimpulkannya dengan bagus (ini harus diukir di batu di suatu tempat)

Seperti yang sering disebutkan sebelumnya, mandat inti dari sebuah shell adalah memanggil executable eksternal dengan argumen .
PowerShell saat ini gagal memenuhi mandat ini, karena argumen dengan tanda kutip ganda yang disematkan dan argumen string kosong tidak diteruskan dengan benar.
Seperti yang dinyatakan sebelumnya, ini mungkin bukan masalah di hari-hari khusus Windows, di mana kurangnya CLI eksternal yang mampu jarang muncul masalah ini, tetapi hari-hari ini hilang, dan jika PowerShell ingin memantapkan dirinya sebagai platform lintas yang kredibel shell, itu harus mengatasi masalah ini .

Dan tentang melanggar perubahan.
Baru-baru ini rekan kerja menulis kepada saya bahwa skrip saya tidak berfungsi pada mesinnya. Saya menjalankannya hanya di Powershel Core dan dia menjalankannya di Windows Powershell. Ternyata Out-File -Encoding utf8 file yang disandikan dengan "BOM" di Windows Powershell dan tanpa BOM di Powershel Core. Ini adalah contoh yang entah bagaimana tidak dihapus tetapi menunjukkan bahwa sudah ada perubahan halus yang tidak kentara di Powershel dan ini bagus karena kami menghilangkan kebiasaan dan perilaku intuitif dari bahasa yang terkenal karenanya. Akan sangat bagus jika tim Powershel sedikit lebih lunak dalam hal melanggar perubahan sekarang karena kami memiliki Powershell lintas platform yang dikirimkan ke luar Windows dan bahwa kami tahu bahwa Windows Powershell dalam mode "pemeliharaan" dan akan dapat digunakan selamanya jika Anda benar-benar ingin menjalankan beberapa skrip lama yang rusak di versi terbaru Powershell.

RE: poin terakhir dari perubahan yang melanggar - Saya setuju sepenuhnya. Ada _banyak_ perubahan yang telah kami toleransi karena berbagai alasan. Namun, semakin sering tampaknya menjadi kasus bahwa beberapa perubahan yang melanggar hanya disukai karena alasan preferensi dan tidak diberi pertimbangan gravitasi yang tepat untuk nilai sebenarnya.

Ada beberapa perubahan seperti ini yang _ secara besar-besaran_ akan meningkatkan pengalaman shell secara keseluruhan bagi siapa saja yang perlu menjangkau di luar PowerShell untuk menyelesaikan sesuatu, yang terjadi setiap saat. Telah disepakati berkali-kali bahwa perilaku saat ini tidak dapat dipertahankan dan sebagian besar sudah rusak untuk apa pun kecuali penggunaan yang paling sederhana. Namun, kami masih menghadapi keengganan untuk menghentikan perubahan meskipun ada sejumlah perubahan yang telah diterima, beberapa di antaranya memiliki dampak yang sama besar.

Bagi mereka yang meminta contoh - luangkan waktu sebentar untuk mengunjungi Stack Overflow sekali. Saya yakin @ mklement0 memiliki sejumlah contoh di mana bantuan komunitas diperlukan untuk membantu menjelaskan perubahan yang dapat menyebabkan perubahan pada versi yang lebih baru. Itu terjadi _sepanjang waktu_. Kami tidak memiliki

Setiap kali tim MSFT mengulangi hal yang sama berulang kali, kami yakin mereka tahu lebih banyak daripada yang dapat mereka katakan di depan umum. Kita harus menghormati disiplin batin mereka dan tidak menekan mereka. _Mungkin kita bisa menemukan kompromi._ Saya harap saya punya waktu hari ini untuk menjelaskan jalur alternatif dengan migrasi malas.

Saya menyadari itu, dan itulah mengapa saya jarang mempertanyakannya.

Namun, ini adalah proyek open source; jika tidak ada kemungkinan visibilitas atas keputusan tersebut, orang _will_ pasti akan menjadi frustrasi. Tidak ada kesalahan untuk dilemparkan di kedua sisi koin itu, itulah realitas situasi di sini, IMO. Jadi ya, memiliki jalur migrasi dapat meringankan rasa sakit itu, tetapi kami membutuhkan kebijakan yang jelas yang ditentukan tentang bagaimana hal itu harus bekerja yang akan membuat semuanya bekerja untuk sebanyak mungkin orang. Namun, kompromi sulit dijangkau jika kekurangan informasi.

Saya berharap untuk melihat apa yang Anda miliki. 😉

@ mklement0 Anda benar, dan begitu banyak sehingga saya hanya dapat menanggapi meta-point Anda sekarang. Sayangnya, dalam kasus di mana kami tidak dapat mencapai tingkat kedalaman yang diperlukan untuk menjawab pertanyaan seperti ini, pendekatan yang lebih aman sering kali adalah dengan menunda atau menolak perubahan yang melanggar sampai kami memiliki lebih banyak waktu untuk menjawabnya.

Saya ingin membuat meta-point lain tentang memecah perubahan, meskipun: telemetri kami menyiratkan bahwa sebagian besar pengguna PowerShell 7 tidak mengelola versi mereka sendiri. Mereka menjalankan skrip otomatis dalam lingkungan terkelola yang nyaman misalnya meningkatkan pengguna mereka dari 6.2 menjadi 7.0 (lihat lompatan 2 hari dari 6.2 pengguna menjadi 7,0 pengguna mulai 8/3; ini bukan satu-satunya poin data kami di sini, tetapi itu yang nyaman sekarang yang membuat intinya). Untuk pengguna ini, perubahan yang dapat mengubah skrip yang berfungsi sempurna menjadi skrip yang tidak berfungsi tidak dapat diterima.

Saya juga berhutang budi kepada komunitas tentang cara saya berpikir tentang dampak pemutusan perubahan: yaitu menukar prevalensi penggunaan yang ada dan tingkat keparahan jeda dengan kemudahan mengidentifikasi dan mengoreksi jeda. Yang ini sangat lazim dalam skrip yang ada, membingungkan untuk diidentifikasi dan diperbaiki, dan perilaku melanggar adalah dari kesuksesan total hingga kegagalan total, maka keengganan ekstrim saya untuk melakukan apa pun di sini.

Saya pikir adil untuk mengatakan kami tidak akan melakukan apa pun di sini di 7.1, tetapi saya jelas terbuka untuk menjadikan ini sebagai prioritas investigasi untuk 7.2 (yaitu kami menghabiskan lebih dari sekadar waktu Komite kami untuk membahas ini.)

Bagaimana dengan menunjuk sub-komite khusus subjek yang berkonsultasi dengan komite yang memiliki pemahaman yang diperlukan tentang masalah yang terlibat?

Kami sedang mengerjakan ini. Saya tahu saya telah mengatakan itu sebelumnya, tetapi kami sangat dekat (seperti dalam, saya membuat blog dan Anda mungkin akan melihat beberapa label baru segera muncul yang akan kami mainkan).

Saya menghargai kesabaran semua orang dan saya menyadari bahwa mendapatkan jawaban yang tepat dari Komite setiap beberapa minggu adalah hal yang menjengkelkan ketika orang-orang mencurahkan banyak pemikiran dan pertimbangan ke dalam diskusi. Saya tahu sepertinya itu berarti kami tidak memikirkan secara mendalam tentang berbagai hal, tetapi saya pikir kami hanya tidak mengungkapkan kedalaman diskusi kami sedetail yang dilakukan orang-orang di sini. Dalam backlog saya sendiri, saya memiliki serangkaian topik blog seperti perubahan besar tentang bagaimana saya berpikir tentang membuat keputusan dari dalam Komite, tetapi saya tidak pernah mendapat kesempatan untuk duduk dan memompa mereka. Tetapi saya dapat melihat di sini bahwa mungkin orang akan menemukan banyak manfaat di dalamnya.

Kuharap aku tidak terlalu jauh dalam diskusi ini. Saya tidak ingin masalah ini benar-benar menjadi masalah meta tentang manajemen proyek, tetapi saya ingin mengatasi beberapa frustrasi yang dapat dimengerti yang saya lihat di sini. Saya mohon kepada siapa pun yang ingin membicarakan hal ini secara lebih rinci dengan kami untuk bergabung dengan Panggilan Komunitas minggu depan (tambahkan pertanyaan dan pemikiran Anda di sini dan saya pasti akan menjawabnya melalui panggilan).

Sekadar catatan singkat tentang meta-point: Saya menghargai tanggapan yang bijaksana, @joeyaiello.

Adapun beratnya perubahan yang melanggar: Pernyataan berikut tampaknya bertentangan:

apakah ada yang punya contoh dari semua ini yang benar-benar penting dalam skenario dunia nyata

vs.

Yang ini sangat lazim di skrip yang ada, membingungkan untuk diidentifikasi dan diperbaiki

Jika sudah lazim, kecanggungan dan ketidakjelasan solusi yang diperlukan menjadi alasan utama untuk akhirnya memperbaikinya, terutama mengingat bahwa kita harus mengharapkan jumlah kasus meningkat.

Saya menyadari bahwa semua solusi yang ada akan rusak.

Jika menghindari itu adalah yang terpenting, pendekatan yang disarankan sebelumnya ini adalah cara yang harus dilakukan:

menyediakan operator _new atau fungsi_ seperti fungsi ie dari modul Native yang memperbaiki masalah, dan mempublikasikannya secara luas sebagai satu-satunya cara yang dapat diandalkan untuk menjalankan executable eksternal.

Sebuah _fungsi_ seperti ie akan memungkinkan orang untuk memilih perilaku yang benar dengan _minimal fuss_, _sebagai stopgap_, tanpa membebani _language_ dengan elemen sintaksis baru (operator), yang alasan utamanya adalah untuk mengatasi bug lama yang dianggap terlalu merusak untuk diperbaiki:

  • Stopgap akan memberikan akses _officially sanktion_ ke perilaku yang benar (tidak bergantung pada fitur _experimental_).
  • Selama stopgap diperlukan, itu perlu dipublikasikan secara luas dan didokumentasikan dengan benar.

Jika / saat perilaku default diperbaiki:

  • fungsinya dapat dimodifikasi untuk hanya menundanya, agar tidak merusak kode yang menggunakannya.
  • kode baru dapat ditulis tanpa memerlukan fungsi lagi.

Fungsi seperti ie akan memungkinkan orang untuk ikut serta dalam perilaku yang benar dengan sedikit keributan, sebagai celah sementara

Kita dapat menyederhanakan adopsi melalui # 13428. Kami dapat memasukkan ini dengan investigasi @ mklement0 di Engine secara transparan.

@Tokopedia

Saya pikir mengakomodasi pengecualian secara default hanya akan mengarah pada situasi yang sama dengan situasi saat ini, di mana orang perlu mengembalikan "kegunaan" PowerShell. Jika ada pengecualian, harus jelas bahwa pengecualian tersebut diterapkan.

Akomodasi yang saya usulkan membuat sebagian besar panggilan "hanya berfungsi" - mereka adalah abstraksi berguna dari anarki baris perintah Windows yang harus kami lakukan yang terbaik untuk melindungi pengguna.

Asumsi yang dapat dibenarkan adalah bahwa perisai ini merupakan upaya satu kali untuk _legacy_ yang dapat dieksekusi, dan yang baru dibuat akan mematuhi konvensi Microsoft C / C ++.

Namun, tidak mungkin melakukannya dalam _all_ kasus; bagi mereka yang tidak dapat diakomodasi secara otomatis, ada --% .

Secara pribadi, saya tidak ingin memikirkan apakah utilitas tertentu foo kebetulan diimplementasikan sebagai foo.bat atau foo.cmd , atau apakah itu memerlukan foo="bar none" argumen secara khusus, tanpa juga menerima "foo=bar none" , yang setara dengan executable yang sesuai dengan konvensi.

Dan saya tentunya tidak menginginkan bentuk sintaks terpisah untuk berbagai pengecualian, seperti &[bat]
Alih-alih, --% adalah alat penampung-semua (khusus Windows) untuk merumuskan baris perintah persis seperti yang Anda inginkan - apa pun persyaratan khusus program target yang tidak konvensional.

Secara khusus, akomodasi yang diusulkan adalah sebagai berikut:

catatan:

  • Seperti yang dinyatakan, mereka hanya diperlukan _pada Windows_; di Unix, mendelegasikan ke ProcessStartInfo.ArgumentList sudah cukup untuk menyelesaikan semua masalah.

  • Setidaknya pada tingkat yang tinggi, akomodasi ini mudah dikonsep dan didokumentasikan.

  • Perhatikan bahwa mereka akan diterapkan parsing biasa _setelah_ PowerShell, dalam langkah baris perintah terjemahan (khusus Windows). Artinya, penguraian parameter PowerShell sendiri tidak akan terlibat - dan _shouldn't_ be , @ yecril71pl.

  • Setiap kasus eksotis yang tidak tercakup oleh akomodasi ini harus ditangani oleh pengguna sendiri, dengan
    --% - atau, dengan modul Native terpasang, dengan ins / Invoke-NativeShell , yang membuatnya lebih mudah untuk menyematkan variabel PowerShell dan nilai ekspresi dalam panggilan.

    • Perintah dbea ( Debug-ExecutableArguments ) dari modul Native dapat membantu mendiagnosis dan memahami baris perintah proses apa yang akhirnya digunakan - lihat contoh bagian di bawah ini.

Daftar akomodasi:

  • Untuk file batch (seperti dicatat, pentingnya mengakomodasi kasus ini secara otomatis adalah prevalensi CLI profil tinggi yang menggunakan file batch _sebagai titik masuknya_, seperti az.cmd untuk Azure).

    • Kutipan ganda yang disematkan, jika ada, di-escape sebagai "" (bukan \" ) di semua argumen.
    • Argumen apa pun yang tidak berisi cmd.exe metakarakter seperti & diapit tanda kutip ganda (sedangkan PowerShell secara default hanya menyertakan argumen dengan spasi dalam tanda kutip ganda); misalnya, argumen verbatim dilihat oleh PowerShell sebagai a&b ditempatkan sebagai "a&b" pada baris perintah yang diteruskan ke file batch.
  • Untuk CLI profil tinggi seperti msiexec.exe / msdeploy.exe dan cmdkey.exe (tanpa pengecualian hard-coding untuk mereka):

    • Setiap permintaan yang berisi setidaknya satu argumen dari formulir berikut memicu perilaku yang dijelaskan di bawah ini; <word> dapat terdiri dari huruf, angka, dan garis bawah:

      • <word>=<value with spaces>
      • /<word>:<value with spaces>
      • -<word>:<value with spaces>
    • Jika ada argumen seperti itu:

      • Kutipan ganda yang disematkan, jika ada, di-escape sebagai "" (bukan \" ) di semua argumen. - lihat https://github.com/PowerShell/PowerShell/pull/13482#issuecomment -677813167 untuk alasan mengapa kita _not_ melakukan ini; ini berarti bahwa dalam kejadian yang jarang terjadi bahwa <value with spaces> telah _embedded_ " chars., --% harus digunakan; misalnya,
        msiexec ... --% PROP="Nat ""King"" Cole"
      • Hanya bagian <value with spaces> yang diapit tanda kutip ganda, bukan argumen secara keseluruhan (yang terakhir adalah apa yang PowerShell - dapat dibenarkan - lakukan secara default); misalnya, argumen verbatim dilihat oleh PowerShell sebagai foo=bar none ditempatkan sebagai foo="bar none" pada baris perintah proses (bukan sebagai "foo=bar none" ).
    • catatan:

      • Jika target yang dapat dieksekusi ternyata _not_ menjadi CLI bergaya msiexec , tidak ada kerusakan yang dilakukan, karena CLI yang mengikuti konvensi secara bijaksana mempertimbangkan <word>="<value with spaces>" dan "<word>=<value with spaces>" _equivalent_, keduanya mewakili kata demi kata <word>=<value with spaces> .

      • Demikian pula, sebagian besar executable menerima "" bergantian dengan \" untuk melarikan diri dari karakter " disematkan., Dengan pengecualian khusus CLI, Ruby, dan Perl milik PowerShell (_not_ perform akomodasi ini berguna setidaknya jika PowerShell CLI dipanggil, tapi menurut saya pengkodean keras Ruby dan Perl juga akan masuk akal). https://github.com/PowerShell/PowerShell/pull/13482#issuecomment -677813167 menunjukkan bahwa semua aplikasi yang menggunakan fungsi CommandLineToArgvW WinAPI melakukan _not_ mendukung "" -escaping.

Semua kasus lain di Windows juga dapat ditangani dengan ProcessStartInfo.ArgumentList , yang secara implisit menerapkan konvensi Microsoft C / C ++ (yang secara khusus berarti \" untuk " -escaping).


Fungsi ie dari versi saat ini ( 1.0.7 ) dari modul Native mengimplementasikan akomodasi ini (sebagai tambahan untuk memperbaiki penguraian argumen yang rusak) , untuk PowerShell versi 3 dan lebih tinggi ( Install-Module Native ).

Saya mengundang Anda dan semua orang di sini untuk melakukan langkah-langkahnya guna menguji klaim bahwa itu "berfungsi" untuk sebagian besar panggilan eksternal yang dapat dijalankan

Batasan yang tidak dapat dihindari saat ini:

  • Catatan: Batasan teknis ini berasal dari ie yang diimplementasikan sebagai _function_ (perbaikan yang tepat di mesin itu sendiri _tidak_ akan mengalami masalah ini):

    • Meskipun $LASTEXITCODE disetel dengan benar ke kode keluar proses, $? selalu berakhir $true - kode pengguna saat ini tidak dapat menyetel $? secara eksplisit, meskipun menambahkan kemampuan ini telah menyala hijau - lihat https://github.com/PowerShell/PowerShell/issues/10917#issuecomment -550550490. Sayangnya, ini berarti bahwa saat ini Anda tidak dapat menggunakan ie berarti dengan && dan || , && , operator jaringan pipa .
      Namun, jika _aborting_ skrip untuk mendeteksi kode keluar bukan nol diinginkan, fungsi iee wrapper dapat digunakan.

    • -- sebagai argumen selalu "dimakan" oleh binder parameter PowerShell; cukup berikan _twice_ untuk meneruskan -- melalui target yang dapat dieksekusi ( foo -- -- bukan foo -- ).

    • Token yang tidak dikutip dengan , harus dikutip agar tidak ditafsirkan sebagai array dan diteruskan sebagai beberapa argumen; misalnya, berikan 'a,b' daripada a,b ; sama halnya, berikan -foo:bar (sesuatu yang terlihat seperti argumen PowerShell bernama) sebagai '-foo:bar' (ini seharusnya tidak perlu, tetapi disebabkan oleh bug: # 6360); sama halnya '-foo.bar must be passed as ' -foo.bar'` (bug lain, yang juga mempengaruhi panggilan langsung ke executable eksternal: # 6291)

  • Saya berharap fungsi tersebut bekerja dengan kuat di PowerShell _Core_. Karena perubahan dalam _Windows PowerShell_ dari waktu ke waktu, mungkin ada kasus edge yang tidak ditangani dengan benar, meskipun saya hanya mengetahui dua:

    • Akomodasi kutipan parsial untuk CLI bergaya msiexec tidak dapat diterapkan di versi 3 dan 4, karena versi ini membungkus seluruh argumen dalam kumpulan tanda kutip ganda tambahan; itu berfungsi di v5.1, namun.

    • "" -escaping digunakan secara default, untuk mengatasi masalah, tetapi dalam kasus di mana \" diperlukan (PowerShell CLI, Perl, Ruby), token seperti 3" of snow adalah keliru diteruskan sebagai argumen _3_, karena semua versi Windows PowerShell lalai untuk menyertakan argumen tersebut dalam tanda kutip ganda; ini tampaknya terjadi untuk argumen dengan karakter non-inisial " yang tidak didahului oleh karakter spasi.


Contoh, dengan keluaran dari PowerShell Core 7.1.0-preview.5 di Windows 10:

Catatan: Fungsi dbea ( Debug-ExecutableArguments ) digunakan untuk menggambarkan bagaimana argumen akan diterima oleh file executable / batch eksternal.

Argumen yang rusak saat ini:

  • Memanggil aplikasi yang sesuai dengan konvensi (aplikasi konsol .NET yang digunakan oleh dbea secara default):
# Note the missing 2nd argument and the effective loss of embedded double quotes,
# due to the embedded " chars. not having been escaped.
PS> dbea -- 'a&b' '' '{ "foo": "bar" }'

2 argument(s) received (enclosed in <...> for delineation):

  <a&b>
  <{ foo: bar }>

Command line (helper executable omitted):

  a&b  "{ "foo": "bar" }"
  • Memanggil file batch:

Perhatikan penggunaan -UseBatchFile untuk membuat dbea meneruskan argumen ke helper _batch file_ sebagai gantinya.

# Note that only *part of the first argument* is passed and that the `&` is interpreted as cmd.exe's
# statement separator, causing `b` to be run as a command (which fails).
PS> dbea -UseBatchFile -- 'a&b' '' '{ "foo": "bar" }'

1 argument(s) received (enclosed in <...> for delineation):

  <a>

'b' is not recognized as an internal or external command,
operable program or batch file.
  • Memanggil CLI bergaya msiexec , cmdkey.exe :
# The call fails, because `cmdkey.exe` requires the password argument to 
# to be quoted exactly as `/password:"bar none"` (double-quoting of the option value only), 
# whereas PowerShell - justifiably - passes `"/password:bar none"` (double-quoting of the whole argument).
PS> cmdkey.exe /generic:foo /user:foo /password:'bar none'

The command line parameters are incorrect.

Memperbaiki masalah dengan ie :

Perhatikan penggunaan -ie dalam panggilan dbea , yang menyebabkan penggunaan ie untuk pemanggilan.

  • Memanggil aplikasi yang sesuai dengan konvensi (aplikasi konsol .NET yang digunakan oleh dbea belakang layar secara default):
# OK
# Note that the empty 2nd argument is correctly passed, and that \" is used for embedded "-escaping.
PS> dbea -ie -- 'a&b' '' '{ "foo": "bar" }'

3 argument(s) received (enclosed in <...> for delineation):

  <a&b>
  <>
  <{ "foo": "bar" }>

Command line (helper executable omitted):

  a&b "" "{ \"foo\": \"bar\" }"
  • Memanggil file batch:
# OK
# - `a&b` was enclosed in "...", due to the presence of metacharacter `&`
# - "" is used for escaping of embedded " chars.
# Note that `echo %1`, for instance, prints the argument exactly as passed on the command line, including quoting.
# `echo %~1` strips the surrounding double quotes, but embedded escaped ones still print as "".
# However, if you pass these arguments (`%*`) through to convention-compliant CLIs, they are parsed correctly.
PS> dbea -ie -UseBatchFile -- 'a&b' '' '{ "foo": "bar" }'

3 argument(s) received (enclosed in <...> for delineation):

  <"a&b">
  <"">
  <"{ ""foo"": ""bar"" }">
  • Memanggil CLI bergaya msiexec , cmdkey.exe :
# The call now succeeds, because `ie` ensure the value-only double-quoting that cmdkey.exe requires.
# (Use `cmdkey /del:foo` to remove the credentials again.)
PS> ie cmdkey.exe /generic:foo /user:foo /password:'bar none'

CMDKEY: Credential added successfully.

Untuk menunjukkan bahwa kutip ganda hanya-nilai diterapkan di baris perintah sebenarnya, melalui dbea :

PS> dbea -ie -- cmdkey.exe /generic:foo /user:foo /password:'bar none'

  <cmdkey.exe>
  </generic:foo>
  </user:foo>
  </password:bar none>

Command line (helper executable omitted):

  cmdkey.exe /generic:foo /user:foo /password:"bar none"

Kode berikut menyebabkan kehilangan data:

{ PARAM($A) $A } | OUT-FILE A.PS1
PWSH A.PS1 -A:(1,2)

1

@JamesWTruher memiliki usulan perbaikan dan sedang memvalidasi jika mengatasi masalah yang diangkat dalam masalah ini

Apakah ada permintaan untuk perbaikan yang diusulkan itu? Alangkah baiknya jika kita bisa mengomentari PR. Karena memperbaiki ini IMO tidak pernah menjadi bagian yang rumit. Bagian yang rumit adalah bagaimana menangani kompatibilitas ke belakang. Dan alangkah baiknya, jika kita bisa melihat bagaimana perbaikan yang diusulkan menangani itu ....

Senang mendengarnya, @ SteveL-MSFT - perbaikan untuk _ semua masalah yang dibahas di sini? Untuk v7.1, setelah semua? Dan saya mendukung permintaan

@ yecril71pl , itu adalah penemuan yang bagus, meskipun yang satu ini benar-benar berhubungan dengan penguraian PowerShell sendiri (yang memiliki _some_ special-casing untuk executable eksternal), bukan dengan bagaimana baris perintah asli dibangun _setelah_ parsing (dimana masalah yang dibahas sebelumnya datang dari).

Repro masalah yang lebih ringkas, di Unix:

PS> printf '<%s>\n' -a:(1,2,3)
<-a:1>
<2>
<3>

Artinya, hanya elemen array _first_ yang langsung dilampirkan ke -a: , yang lainnya diberikan sebagai argumen terpisah.

Ada masalah terkait dengan argumen yang _terlihat seperti_ parameter PowerShell, tetapi bukan:

Ada masalah terkait yang hanya memengaruhi panggilan ke perintah _PowerShell_ yang menggunakan $args / @args : # 6360

  • & { $args.Count; $args } -foo:bar menghasilkan 2, '-foo:', 'bar'

Ada juga # 6291, yang memengaruhi perintah PowerShell dan executable eksternal (perhatikan . ).

  • & { $args.Count; $args } -foo.bar menghasilkan 2, '-foo', '.bar'

Satu hal yang perlu diperhatikan adalah bahwa (...) sebagai bagian dari bareword biasanya menghasilkan keluaran (...) secara keseluruhan menjadi argumen _separate_, jadi fakta bahwa elemen pertama _is_ dilampirkan dalam printf perintah di atas adalah bukti casing khusus untuk panggilan yang dapat dieksekusi eksternal; misalnya,
& { $args.Count; $args.ForEach({ "$_" }) } foo('bar', 'baz') menghasilkan 2, 'foo', 'bar baz' , argumen kedua adalah stringifikasi dari array 'bar', 'baz' .

Ketika PowerShell harus mengirimkan -A:(1,2) untuk eksekusi eksternal, ia mengetahui bahwa -A: adalah string dan (1,2) adalah array yang harus disusun sebagai '1 2'. PowerShell mencoba mempertahankan sintaks asli pemanggilan, jadi, menggabungkan semuanya, kita mendapatkan '-A: 1 2', sedangkan hasil yang benar adalah '-A: "1 2"'. Bagi saya, ini tampak seperti kelalaian sepele dalam kode marshalling.

Saya tidak akan mengatakan, bahwa masalah khusus @ yecril71pl terkait dengan parsing (meskipun saya setuju, bahwa itu tidak ada hubungannya dengan masalah "mengubah array ke baris perintah", yang dibahas dalam masalah ini).

Ketika PowerShell harus melewati -A: (1,2) untuk eksekusi eksternal, ia mengetahui bahwa -A: adalah string dan (1,2) adalah array

Almost: -A: adalah parameter bernama dan array adalah nilai parameter itu (Untuk mengujinya: hapus - di depan dan Anda lihat, dikutip secara berbeda). Tapi masalahnya bukan, bahwa array salah dikonversi menjadi string - masalahnya adalah, untuk argumen executable asli (hampir) selalu dicipratkan, bahkan ketika menggunakan $ dan bukan @ dan bahkan jika array tersebut berasal dari ekspresi seperti (1,2) .

Uji misalnya printf '<%s>\n' -a:('a b',2) : Karena string a b berisi spasi, ia dikutip dengan benar, tetapi karena 2 ada di elemen array berikutnya dan array itu berceceran, 2 bukan bagian dari argumen pertama.


Keajaiban terjadi di NativeCommandParameterBinder.cs

Pada baris 170 PowerShell mencoba mendapatkan enumerator untuk nilai argumen saat ini.

IEnumerator list = LanguagePrimitives.GetEnumerator(obj);

Jika list bukan null , PowerShell menambahkan setiap elemen dari daftar (mungkin dikutip jika berisi spasi) ke lpCommandLine.

Elemen dipisahkan dengan spasi ( baris 449 ) secara default. Satu-satunya pengecualian adalah jika array itu literal
(seperti dalam printf '<%s>\n' -a:1,2 ).
Kemudian PowerShell mencoba menggunakan pemisah yang sama di lpCommandLine, yang digunakan di baris skrip.

Saya mengharapkan PR saat siap. Jika berhasil menjadi 7.1, kami akan mengambilnya, jika tidak maka akan menjadi 7.2. Kompatibilitas ke belakang adalah sesuatu yang dia tangani. Mungkin yang akan membantu adalah bantuan dalam menulis tes Pester (menggunakan testexe -echoargs yang dapat dibangun menggunakan publish-pstesttools dari build.psm1).

Saya mengharapkan PR saat siap

Itulah tepatnya yang ingin saya hindari - tolong tunjukkan kode yang belum siap (tandai PR sebagai pekerjaan dalam proses).

Atau setidaknya berkomentar, apa yang ingin dia lakukan.

Alangkah baiknya jika kita bisa mendiskusikan bagaimana dia ingin menangani kompatibilitas ke belakang.

@TSlivede , perhatikan bahwa karena PowerShell _CLI_ dipanggil - eksternal yang dapat dieksekusi - -A:(1,2) diurai _before_ mengetahui bahwa token ini pada akhirnya akan terikat ke _named_ -A parameter - bahwa parameter seperti itu _eventually_ ikut bermain merupakan masalah kebetulan.

@ yecril71 :

Ia mengetahui bahwa -A: adalah string

Tidak, ini dikurung khusus selama parsing, karena kebetulan _look like_ parameter PowerShell.

Kasus khusus ini terjadi untuk panggilan ke perintah PowerShell yang menggunakan $args juga (sebagai lawan dari memiliki parameter yang dideklarasikan aktual yang terikat), tetapi itu terjadi _differently_ untuk executable eksternal (apa yang biasanya argumen terpisah tetap terpasang, tetapi dalam kasus koleksi hanya elemen _first_ nya).

Anda benar-benar dapat memilih keluar dari casing khusus ini jika Anda mengirimkan -- sebelumnya, tetapi, tentu saja, itu juga akan memberikan -- , yang hanya dihapus untuk panggilan ke _PowerShell_ perintah:

PS> printf '<%s>\n' -- -a:(1,2,3)
<-->   # !! not removed
<-a:>
<1>    # array elements are *all* now passed as indiv. arguments, because (...) output is separate (splatted) argument
<2>
<3>

Jika argumen _doesn't_ terlihat seperti parameter PowerShell, perilaku biasa (output dari (...) menjadi argumen terpisah) dijalankan bahkan untuk executable eksternal (dengan perilaku biasa _array_ dicipratkan, yaitu diubah menjadi argumen individu dalam kasus yang dapat dieksekusi eksternal).

# Note: No "-" before "a:" -> output from `(...)` becomes separate argument(s)
PS> printf '<%s>\n' a:(1,2,3)
<a:>
<1>
<2>
<3>

Menerapkan perilaku ini _consistently_ akan masuk akal - ekspresi (...) sebagai bagian dari bareword harus _always_ menjadi argumen terpisah - lihat # 13488.

Untuk meneruskan argumen tunggal '-A:1 2 3' , dengan array _stringified_, gunakan (n implisit) _expandable string_, dalam hal ini Anda membutuhkan $(...) daripada (...) _and_ - secara mengejutkan - saat ini juga "..." :

PS> printf '<%s>\n' "-a:$(1,2,3)"  # quotes shouldn't be needed; `-a:"$(1,2,3)"` would work too.
<a:1 2 3> # SINGLE argument with stringified array.

Anda _harus_ juga tidak memerlukan "..." dalam kasus ini - ini lagi-lagi diperlukan karena anomali yang berkaitan dengan token yang _look_ suka parameter (yang secara umum berlaku untuk _both_ PowerShell dan panggilan yang dapat dieksekusi eksternal - lihat # 13489); jika tidak, Anda tidak perlu mengutip:

# Anomaly due to looking like a parameter: $(...) output becomes separate argument
PS> Write-Output -- -a:$(1,2,3)
-a:
1
2
3

# Otherwise (note the absence of "-"): no quoting needed; treated implicitly like 
# "a:$(1,2,3)"
PS> Write-Output -- a:$(1,2,3)
a:1 2 3  # SINGLE argument with stringified array.

Dunia token majemuk dalam mode argumen adalah dunia yang kompleks, dengan beberapa inkonsistensi - lihat # 6467.

@ SteveL-MS

Dalam bentuknya saat ini, testexe -echoArgs hanya mencetak argumen individual yang diurai oleh .NET Core dari baris perintah mentah (di Windows), bukan baris perintah mentah itu sendiri.

Oleh karena itu, ia tidak dapat menguji akomodasi dengan kutipan selektif untuk file batch dan msiexec -style CLI - dengan asumsi akomodasi semacam itu akan diterapkan, yang sangat saya sarankan; misalnya, Anda tidak akan dapat memverifikasi bahwa PROP='foo bar' dikirimkan sebagai PROP="foo bar" , dengan tanda kutip ganda di sekitar bagian nilai.

Namun, untuk mencetak baris perintah mentah, testexe tidak boleh dieksekusi .NET _Core_, karena .NET Core _membuat kembali baris perintah hipotetis_ yang selalu menggunakan \" -escaping untuk " disematkan "" digunakan, dan secara umum tidak mencerminkan argumen mana yang dikutip ganda dan mana yang tidak - untuk latar belakang, lihat https://github.com/ dotnet / runtime / issues / 11305 # Issuecomment -674554010.

Hanya executable .NET _Framework_-compiled menunjukkan baris perintah yang benar di Environment.CommandLine , jadi testexe harus dikompilasi dengan cara itu (dan diubah untuk (opsional) mencetak baris perintah mentah).

Untuk menguji akomodasi untuk file batch, diperlukan file uji _batch_ terpisah, untuk memverifikasi bahwa 'a&b' diteruskan sebagai "a&b" dan 'a"b' sebagai "a""b" , untuk contoh.

Kompilasi @ mklement0 untuk

Atau, dapatkah kita mengandalkan skrip bash sederhana untuk Linux / macOS untuk mengeluarkan argumen?

#!/bin/bash
for i; do
   echo $i
done

Dan pada Windows sesuatu yang mirip dengan file batch.

Bagaimana kalau menggunakan node dengan skrip .js?

console.log(process.execArgv.join('\n') atau string apa pun yang menangani Anda
ingin lakukan agar hasilnya terlihat bagus?

@cspotcode , untuk mendapatkan baris perintah mentah kita membutuhkan panggilan WinAPI.

@ SteveL-MSFT:

Di Windows , Anda dapat mendelegasikan kompilasi ke _Windows PowerShell_ melalui CLI-nya, yang saya lakukan di dbea ; berikut adalah contoh sederhana yang menghasilkan executable .NET _Framework_ yang menggemakan baris perintah mentah (hanya), ./rawcmdline.exe :

powershell.exe -noprofile -args ./rawcmdline.exe -c {

  param([string] $exePath)

  Add-Type -ErrorAction Stop -OutputType ConsoleApplication -OutputAssembly $exePath -TypeDefinition @'
using System;
static class ConsoleApp {
  static void Main(string[] args) {
    Console.WriteLine(Environment.CommandLine);
  }
}
'@

}

Contoh panggilan:

PS> ./rawcmdline.exe --% "a&b" PROP="foo bar"
"C:\Users\jdoe\rawcmdline.exe"  "a&b" PROP="foo bar"

Adapun file _batch_ yang menggemakan argumennya, dbea juga membuatnya sesuai permintaan .

Di Unix , skrip shell sederhana, seperti yang ditunjukkan dalam komentar Anda, memang cukup, dan Anda bahkan dapat

@ PowerShell / PowerShell-committee membahas hari ini, kami meminta @JamesWTruher untuk memperbarui PR-nya untuk juga memasukkan sebagai bagian dari fitur eksperimentalnya untuk melewati langkah dalam prosesor perintah asli yang merekonstruksi array args kembali ke string dan hanya meneruskan itu ke args array baru di ProcessStartInfo (ada sedikit kode untuk memastikan nama dan nilai parameter cocok dengan tepat). Selain itu, kami menerima bahwa kami mungkin memerlukan daftar izin untuk perintah yang diketahui kasus khusus yang masih gagal dengan perubahan yang diusulkan dan itu adalah sesuatu yang dapat ditambahkan nanti.

Bagi mereka yang mungkin tidak memperhatikan: PR telah dipublikasikan (sebagai WIP) dan sedang dibahas: https://github.com/PowerShell/PowerShell/pull/13482

PS, @ SteveL-MSFT, mengenai mendapatkan baris perintah mentah di Windows: tentu saja, alternatif untuk mendelegasikan kompilasi ke Windows PowerShell / .NET _Framework_ adalah dengan meningkatkan aplikasi konsol .NET _Core_ yang ada untuk membuat (bersyarat platform) P / Panggil panggilan ke fungsi GetCommandLine() WinAPI, seperti yang ditunjukkan di bawah ini.

using System;
using System.Runtime.InteropServices;

namespace demo
{
  static class ConsoleApp
  {
    [DllImport("kernel32.dll")]
    private static extern System.IntPtr GetCommandLineW();

    static void Main(string[] args)
    {
      Console.WriteLine("\n{0} argument(s) received (enclosed in <...> for delineation):\n", args.Length);
      for (int i = 0; i < args.Length; ++i)
      {
        Console.WriteLine("  <{0}>", args[i]);
      }

      // Windows only: print the raw command line.
      if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
      {
        Console.WriteLine("\nCommand line:\n\n  {0}\n", Marshal.PtrToStringUni(GetCommandLineW()));
      }

    }

  }

}

@ SteveL-MS

Kompatibilitas ke belakang adalah sesuatu yang dia tangani.

Saya pikir itu tersirat oleh klarifikasi Anda nanti bahwa ProcessStartInfo.ArgumentList (argumen _collection_ dari _verbatim_ untuk digunakan sebagaimana adanya di Unix dan diterjemahkan ke MS C / C ++ - baris perintah yang sesuai dengan konvensi di Windows oleh .NET Core sendiri ) harus digunakan, tetapi izinkan saya menyatakannya secara eksplisit:

  • Memperbaiki masalah ini dengan benar, sekali dan untuk selamanya, _tutup semua konsesi untuk kompatibilitas ke belakang_.

  • PR @JamesWTruher berada di jalur yang benar saat tulisan ini dibuat, satu-satunya masalah tampaknya adalah bahwa argumen kosong masih belum disampaikan .

    • Setelah itu diatasi, perbaikan selesai di _Unix_ - tetapi tidak memiliki akomodasi penting untuk CLI di _Windows_ (lihat di bawah).

kita mungkin memerlukan allowlist untuk perintah yang diketahui kasus khusus yang masih gagal dengan perubahan yang diusulkan dan itu adalah sesuatu yang dapat ditambahkan nanti.

Saya mendorong Anda untuk tidak menunda ini sampai nanti .

Alih-alih _allowlist_ (casing khusus untuk executable tertentu), aturan umum sederhana dapat mengatur akomodasi , disaring dari yang di atas setelah beberapa diskusi lagi dengan @TSlivede .

Akomodasi ini, yang _dibutuhkan di Windows saja_:

Secara khusus, ini adalah:

  • Sama seperti di Unix, ProcessStartInfo.ArgumentList digunakan secara default - kecuali jika _satu atau keduanya_ dari kondisi berikut terpenuhi, _ dalam hal ini baris perintah proses harus dibuat secara manual_ (dan ditetapkan ke ProcessStartInfo.Arguments sebagai saat ini):

    • File batch ( .cmd , .bat ) atau cmd.exe secara langsung dipanggil:
    • Dalam peristiwa itu, _embedded_ " -escape sebagai "" (bukan sebagai \" ), dan argumen tanpa spasi yang berisi salah satu dari cmd.exe metakarakter berikut juga dikutip ganda (biasanya, hanya argumen _dengan spasi_ yang dikutip ganda): " & | < > ^ , ; - ini akan membuat panggilan ke file batch berfungsi dengan baik, yang penting, karena banyak CLI profil tinggi menggunakan file batch sebagai _entry poin_.
    • Secara independen (dan mungkin tambahan), jika setidaknya ada satu argumen yang cocok dengan regex
      '^([/-]\w+[=:]|\w+=)(.*? .*)$' , semua argumen tersebut harus menerapkan _p Partial_ kutip ganda di sekitar _value part only_ (yang mengikuti : atau = )

      • Misalnya, argumen gaya msiexec.exe / msdeploy.exe dan cmdkey.exe -style yang dilihat oleh PowerShell secara verbatim sebagai
        FOO=bar baz dan /foo:bar baz / -foo:bar baz akan ditempatkan pada baris perintah proses sebagai
        foo="bar baz" atau /foo:"bar baz" / -foo:"bar baz" membuat _any_ CLI yang membutuhkan gaya kutipan seperti ini.
    • Verbatim \ karakter dalam argumen harus ditangani sesuai dengan konvensi MS C / C ++.

Apa yang _tidak_ dicakup oleh akomodasi ini:

  • msiexec.exe (dan mungkin juga msdeploye.exe ) mendukung _only_ "" -escaping dari karakter _embedded_ " . , yang _not_cakup aturan di atas - kecuali jika Anda kebetulan memanggil melalui file batch atau cmd /c .

    • Ini seharusnya cukup langka untuk memulai (misalnya,
      msiexec.exe /i example.msi PROPERTY="Nat ""King"" Cole" ), tetapi mungkin dibuat lebih langka karena fakta bahwa misexec pemanggilan biasanya _dibuat sinkron_ untuk menunggu akhir penginstalan, dalam hal ini Anda dapat menghindari masalah di salah satu dua arah:
    • cmd /c start /wait msiexec /i example.msi PROPERTY='Nat "King' Cole' - mengandalkan panggilan ke cmd.exe (lalu) memicu "" -escaping
    • Start-Process -Wait msiexec '/i example.msi PROPERTY="Nat ""King"" Cole"' - bergantung pada parameter -ArgumentList ( -Args ) meneruskan argumen string tunggal melalui verbatim sebagai baris perintah proses (meskipun itu bukan cara kerja parameter ini - lihat # 5576).
  • CLI non-konvensional lainnya yang tidak cukup akomodasi di atas - saya pribadi tidak mengetahuinya.

Pada akhirnya, selalu ada solusi: telepon melalui cmd /c , atau, untuk aplikasi non-konsol, melalui Start-Process , atau gunakan --% ; jika dan ketika kami memberikan cmdlet ins ( Invoke-NativeShell ), itu adalah opsi lain; dbea ( Debug-ExecutableArguments cmdlet dengan kemampuan mirip echoArgs.exe , tetapi sesuai permintaan juga untuk file batch, juga akan membantu masalah _diagnose_.


Adapun jalur menuju perubahan yang melanggar vs. keikutsertaan:

  • Apakah menerapkan ini sebagai fitur eksperimental berarti bahwa jika minat yang cukup ditunjukkan akan menjadi perilaku _default_ dan oleh karena itu akan menghasilkan perubahan (nontrivial) yang mengganggu?

  • Dapatkah Anda memastikan bahwa fitur eksperimental ini dipublikasikan secara luas, mengingat pentingnya?

    • Perhatian umum yang saya miliki tentang fitur eksperimental adalah bahwa penggunaannya sering kali _tidak disengaja_ dalam versi pratinjau, mengingat bahwa _all_ fitur eksperimental diaktifkan secara default. Kami pasti ingin orang-orang mengetahui dan menggunakan fitur ini dengan sengaja.
Apakah halaman ini membantu?
0 / 5 - 0 peringkat