Powershell: Argumente für externe ausführbare Dateien werden nicht korrekt maskiert

Erstellt am 21. Aug. 2016  ·  170Kommentare  ·  Quelle: PowerShell/PowerShell

Schritte zum Reproduzieren

  1. Schreiben Sie ein C-Programm native.exe das ARGV erwirbt
  2. Führen Sie native.exe "`"a`""

    Erwartetes Verhalten

ARGV [1] == "a"

Tatsächliches Verhalten

ARGV [1] == a

Umgebungsdaten

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

Hilfreichster Kommentar

Einen speziellen Operator dafür zu erstellen, ist für eine Befehlszeilen-Shell überhaupt nicht sinnvoll, da ihre Hauptaufgabe darin besteht, Programme zu starten und ihnen Argumente zu übergeben. Das Einführen eines neuen Operators, der system() für diesen Job ausführt, ist wie das Einführen einer Methode zum Aufrufen von calc.exe durch Matlab, da die Arithmetik einen Fehler aufweist. Was stattdessen getan werden sollte, ist Folgendes:

  • Das pwsh-Team bereitet sich auf eine neue Hauptversion vor, die das Kommandozeilenproblem behebt und das aktuelle Verhalten hinter ein integriertes Cmdlet verschiebt.
  • Als Stop-Gap-Lösung erhält die kommende pwsh-Version ein integriertes Cmdlet, das das neue, korrekte Verhalten für die Befehlszeilenübergabe verwendet.

Gleiches gilt für Start-Process . (Eigentlich ist es ein ziemlich guter Kandidat für das "neue" Cmdlet mit einigen Optionen wie -QuotingBehavior Legacy ...) Siehe # 13089.

Alle 170 Kommentare

Warum denkst du, dass "\"a\"" das erwartete Verhalten ist? Mein Verständnis von PowerShell-Escapes besagt, dass das tatsächliche Verhalten das richtige und erwartete Verhalten ist. " "a "" ist ein Anführungszeichenpaar, das ein Escape-Anführungszeichen um ein a umgibt. PowerShell interpretiert das äußere nicht entflohene Paar also als" Dies ist ein Zeichenfolgenargument "und Lässt sie also fallen, interpretiert das Escape-Paar als Escape-Anführungszeichen und behält sie so bei, sodass Sie "a" . Zu keinem Zeitpunkt wurde der Zeichenfolge ein \ hinzugefügt.

Die Tatsache, dass Bash \ als Fluchtzeichen verwendet, ist irrelevant. In PowerShell ist das Escape-Zeichen ein Backtick. Siehe PowerShell- Escapezeichen .

Wenn Sie buchstäblich "\"a\"" möchten, würden Sie meiner Meinung nach Folgendes verwenden:

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

@andschwa
Ja, Escapezeichen funktionieren gut für interne Cmdlets, aber bei der Kommunikation mit nativen Binärdateien , insbesondere unter Windows, wird es merkwürdig.
Wenn native.exe " "a "" , sollte der ARGV [1] sein

"a"

(drei Zeichen)

Anstatt von

a

(ein Zeichen).

Derzeit müssen Sie diesen seltsamen Aufruf verwenden, damit native.exe ein ARGV mit zwei Anführungszeichen und einem a -Zeichen korrekt erhält:

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

Ah ich sehe. Wiedereröffnung.

Was passiert aus starker Neugier, wenn Sie einen Build mit # 1639 versuchen?

@andschwa Das gleiche. Sie MÜSSEN esacpe verdoppeln, um sowohl PowerShell als auch CommandLineToArgvW zufrieden zu stellen. Diese Linie:

native.exe "`"a`""

ergibt einen StartProcess, der cmd entspricht

native.exe ""a""

@ be5invis @douglaswth Wird dies über https://github.com/PowerShell/PowerShell/pull/2182 behoben

Nein, wir müssen noch einen Backslash hinzufügen, bevor ein Backtick-Escape-Doppelzitat angezeigt wird. Dies löst das Problem der doppelten Flucht nicht. (Das heißt, wir müssen uns für PowerShell und CommandLineToArgvW einem doppelten Anführungszeichen entziehen.)

Schlagen Sie vor, dass native.exe '"a"' zu "\"a\"" sollte, da " "ein "" gleich '"a"' ist?

Dies scheint eine Funktionsanforderung zu sein, die bei ihrer Implementierung eine große Anzahl bereits vorhandener PowerShell-Skripts beschädigen könnte, die das erforderliche doppelte Escapezeichen verwenden. Daher wäre bei jeder Lösung äußerste Vorsicht geboten.

@vors Ja.
@douglaswth Die doppelte Flucht ist wirklich albern: Warum brauchen wir die "inneren" Fluchten, die in der DOS-Ära gemacht wurden?

@vors @douglaswth
Dies ist der C-Code, mit dem die Ergebnisse von GetCommandLineW und CommandLineToArgvW angezeigt werden:

#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);
}

Hier ist das Ergebnis

$ ./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 Ich bin nicht anderer Meinung als Sie, dass die doppelte Flucht ärgerlich ist, aber ich sage nur, dass eine Änderung daran abwärtskompatibel mit den vorhandenen PowerShell-Skripten sein muss.

Wie viele sind sie? Ich glaube nicht, dass es Drehbuchautoren gibt, die über solche doppelten Zitate Bescheid wissen. Es ist ein Fehler, keine Funktion, und es ist nicht dokumentiert.

???? iPhone

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

@ be5i nvishttps: //github.com/be5invis Ich bin nicht anderer Meinung als die ärgerliche doppelte Flucht, aber ich sage nur, dass eine Änderung daran abwärtskompatibel mit den vorhandenen PowerShell-Skripten sein müsste.

Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail, und zeigen Sie sie auf Gi tHubhttps: //github.com/PowerShell/PowerShell/issues/1995#issuecomment -248381045 an oder :

PowerShell gibt es seit 9 Jahren, daher gibt es sehr wahrscheinlich eine gute Anzahl von Skripten. Ich habe viele Informationen über die Notwendigkeit einer doppelten Flucht aus StackOverflow und anderen Quellen gefunden, als ich auf die Notwendigkeit gestoßen bin, sodass ich nicht weiß, ob ich Ihren Behauptungen zustimme, dass niemand von der Notwendigkeit weiß oder dass es nicht dokumentiert ist .

Für den zusätzlichen Kontext möchte ich ein wenig über die Implementierung sprechen.
PowerShell ruft die .NET-API auf, um einen neuen Prozess zu erzeugen, der eine Win32-API (unter Windows) aufruft.

Hier erstellt PS StartProcessInfo, das verwendet wird
https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/NativeCommandProcessor.cs#L1063

Die bereitgestellte API verwendet eine einzelne Zeichenfolge für Argumente und wird dann erneut in ein Array von Argumenten analysiert, um die Ausführung durchzuführen.
Die Regeln für dieses erneute Parsen werden nicht von PowerShell gesteuert. Es ist eine Win32-API (und glücklicherweise konsistent in Dotnet-Core- und Unix-Regeln).
In diesem Vertrag wird insbesondere das Verhalten von \ und " .

Obwohl PowerShell möglicherweise versucht, intelligenter zu sein und eine bessere Benutzererfahrung zu bieten, stimmt das aktuelle Verhalten mit cmd und bash überein: Sie können native ausführbare Zeilen von diesen kopieren und in Powershell verwenden, und es funktioniert genauso.

@ be5invis Wenn Sie einen Weg kennen, um die Erfahrung auf ununterbrochene Weise zu verbessern, richten Sie bitte die Details aus. Für die grundlegenden Änderungen müssten wir den RFC-Prozess verwenden, wie unter https://github.com/PowerShell/PowerShell/blob/master/docs/dev-process/breaking-change-contract.md beschrieben

Dies gilt für Windows, aber wenn Befehle unter Linux oder Unix ausgeführt werden, ist es seltsam, dass man Escape-Anführungszeichen verdoppeln muss.

Unter Linux haben Prozesse keine einzige Befehlszeile, sondern eine Reihe von Argumenten.
Daher sollten Argumente in Powershell dieselben sein wie diejenigen, die an die ausführbare Datei übergeben werden, anstatt alle Argumente zusammenzuführen und dann erneut zu teilen.

Selbst unter Windows ist das aktuelle Verhalten inkonsistent:
Wenn ein Argument keine Leerzeichen enthält, wird es unverändert übergeben.
Wenn ein Argument Leerzeichen enthält, wenn es von Anführungszeichen umgeben ist, um es durch einen Aufruf von CommandLineToArgvW . => Das Argument wird geändert, um die Anforderungen von CommandLineToArgvW erfüllen.
Wenn das Argument jedoch Anführungszeichen enthält, werden diese nicht maskiert. => Das Argument wird nicht geändert, obwohl CommandLineToArgvW erfordert.

Ich denke, Argumente sollten entweder nie oder immer geändert werden, um die Anforderungen von CommandLineToArgvW erfüllen, aber nicht in der Hälfte der Fälle.

In Bezug auf Vertragsbruch:
Da ich keine offizielle Dokumentation über Doppelflucht finden konnte, würde ich dies als Kategorie "Bucket 2: Angemessene Grauzone" betrachten. Es besteht also die Möglichkeit, dies zu ändern, oder irre ich mich?

@vors Dies ist äußerst ärgerlich, wenn Ihr Argument eine Variable oder etwas anderes ist: Sie müssen es manuell maskieren, bevor Sie es an eine native App senden.
Ein "Auto-Escape" -Operator kann helfen. wie ^"a " " -> "a\ " "`

Ich denke, @TSlivede hat es mit der Inkonsistenz im Verhalten richtig gemacht.

Ich denke, Argumente sollten entweder nie oder immer geändert werden, um die CommandLineToArgvW-Anforderungen zu erfüllen, aber nicht in der Hälfte der Fälle.

Ich bin mir über den Eimer nicht sicher, aber selbst der Eimer mit dem "deutlich brechenden Wechselgeld" könnte möglicherweise geändert werden. Wir möchten PowerShell verbessern, aber die Abwärtskompatibilität ist eine unserer höchsten Prioritäten. Deshalb ist es nicht so einfach.
Wir haben eine großartige Community und ich bin zuversichtlich, dass wir einen Konsens finden können.

Möchte jemand einen RFC-Prozess starten?

Es lohnt sich, die Verwendung von P / Invoke anstelle von .Net zu untersuchen, um einen Prozess zu starten, wenn PowerShell keine Anführungszeichen zu Argumenten hinzufügen muss.

@lzybkr Soweit ich das beurteilen kann, würde PInvoke nicht helfen.
Und hier unterscheiden sich Unix- und Windows-APIs:

https://msdn.microsoft.com/en-us/library/20y988d2.aspx (behandelt Leerzeichen als Trennzeichen)
https://linux.die.net/man/3/execvp (behandelt Leerzeichen nicht als Trennzeichen)

Ich habe nicht vorgeschlagen, die Windows-Implementierung zu ändern.

Ich würde versuchen, plattformspezifisches Verhalten hier zu vermeiden. Dies beeinträchtigt die Portabilität von Skripten.
Ich denke, wir können erwägen, das Verhalten von Fenstern ohne Unterbrechung zu ändern. Dh mit Präferenzvariable. Und dann können wir verschiedene Standardeinstellungen oder ähnliches haben.

Wir sprechen über das Aufrufen externer Befehle - sowieso etwas plattformabhängig.

Nun, ich denke, es kann nicht wirklich plattformunabhängig sein, da Windows und Linux nur unterschiedliche Möglichkeiten haben, ausführbare Dateien aufzurufen. Unter Linux erhält ein Prozess ein Argumentarray, während unter Windows ein Prozess nur eine einzige Befehlszeile (eine Zeichenfolge) erhält.
(Vergleiche die grundlegenderen
CreateProcess -> Befehlszeile (https://msdn.microsoft.com/library/windows/desktop/ms682425)
und
execve -> Befehlsarray (https://linux.die.net/man/2/execve)
)

Wenn Powershell diese Anführungszeichen hinzufügt, wenn Argumente Leerzeichen enthalten, scheint es mir, dass Powershell ** versucht, die Argumente so zu übergeben, dass CommandLineToArgvW die Befehlszeile auf die Argumente aufteilt, die ursprünglich in Powershell angegeben wurden. (Auf diese Weise erhält ein typisches c-Programm die gleichen Argumente in seinem argv-Array wie eine Powershell-Funktion wie $ args.)
Dies würde perfekt dazu passen, nur die Argumente an den Linux-Systemaufruf zu übergeben (wie über p / invoke vorgeschlagen).

** (und schlägt fehl, da es Anführungszeichen nicht entgeht)

PS: Was ist notwendig, um einen RFC-Prozess zu starten?

Genau - PowerShell versucht sicherzustellen, dass CommandLineToArgvW den richtigen Befehl erzeugt und nach dem erneuten Analysieren, was PowerShell bereits analysiert hat.

Dies ist seit langem ein Problem unter Windows. Ich sehe einen Grund, diese Schwierigkeit auf * nix zu übertragen.

Für mich ist dies ein Implementierungsdetail, für das kein RFC erforderlich ist. Wenn wir das Verhalten in Windows PowerShell geändert haben, ist möglicherweise ein RFC erforderlich, aber selbst dann kann die richtige Änderung als (möglicherweise riskante) Fehlerbehebung angesehen werden.

Ja, ich denke auch, dass eine Änderung unter Linux zur Verwendung eines direkten Systemaufrufs alle glücklicher machen würde.

Ich denke immer noch, dass es auch unter Windows geändert werden sollte,
(Vielleicht durch Hinzufügen einer Präferenzvariablen für diejenigen, die ihre Skripte nicht ändern möchten)
weil es jetzt einfach falsch ist - es ist ein Fehler. Wenn dies korrigiert würde, wäre ein direkter Systemaufruf unter Linux nicht einmal erforderlich, da jedes Argument unverändert zum nächsten Prozess gelangen würde.

Da es jedoch ausführbare Dateien gibt, die die Befehlszeile auf eine Weise aufteilen, die nicht mit CommandLineToArgvW kompatibel ist, gefällt mir @ be5invis 'Idee eines Operators für Argumente - aber ich würde keinen Auto-Escape-Operator erstellen (sollte es sein) Standard für alle Argumente), aber fügen Sie stattdessen einen Operator hinzu, um einem Argument nicht zu entkommen (fügen Sie keine Anführungszeichen hinzu, entkommen Sie nichts).

Dieses Problem trat heute bei uns auf, als jemand den folgenden Befehl in PowerShell versuchte und PowerShell ablehnte, wenn es nicht funktionierte, CMD jedoch:

wmic useraccount where name='username' get sid

In PSCX-Echoargs sieht wmic.exe Folgendes:

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

Welche API verwendet CMD.exe, um den Prozess / die Befehlszeile aufzurufen? Was macht -%, damit dieser Befehl funktioniert?

@rkeithhill CreateProcessW . Direktwahl. Ja wirklich.

Warum verhält sich Powershell in diesen beiden Situationen unterschiedlich? Insbesondere werden Argumente, die Leerzeichen enthalten, inkonsistent in doppelte Anführungszeichen gesetzt.

# 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...

Es scheint keinen Reim oder Grund zu geben. In der ersten Situation wird mein Argument in doppelte Anführungszeichen gesetzt, in der zweiten Situation jedoch nicht. Ich muss genau wissen, wann es in doppelte Anführungszeichen gesetzt wird und wann nicht , damit ich mein Skript manuell umbrechen kann (oder nicht).

.echoargs.exe wird erstellt, indem Folgendes mit cl echoargs.c kompiliert wird

// 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: Hier ist meine $ PSVersionTable:

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

Das Verhalten in Bezug auf Anführungszeichen hat sich mehrfach geändert, daher würde ich empfehlen, Folgendes zu verwenden:

Bearbeiten: Aktualisiertes Formular unten
Alte Version:

# 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\\" \'

Ausgabe:

<"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\\" \>

Das erste -replace verdoppelt Backslashes vor Anführungszeichen und fügt einen zusätzlichen Backslash hinzu, um der Qoute zu entkommen.
Das zweite -replace verdoppelt die Backslashes am Ende des Arguments, sodass das abschließende Zitat nicht maskiert wird.

Dies verwendet --% (PS v3 und höher). Dies ist AFAIK die einzige zuverlässige Möglichkeit, Anführungszeichen an native ausführbare Dateien zu übergeben.


Bearbeiten:

Aktualisierte Version von Run-Native , jetzt Invoke-NativeCommand (wie vorgeschlagen )

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%
    }
}

(mit etwas Inspiration von iep )

  • zitiert keine einfachen Schalter
  • funktioniert immer noch mit leeren Argumenten
  • funktioniert, wenn keine Argumente vorhanden sind
  • sollte meistens mit msiexec, cmdkey usw. funktionieren ...
  • funktioniert immer noch für Programme, die den allgemeinen Regeln folgen
  • verwendet nicht nicht standardmäßige "" als maskierte " - funktioniert daher immer noch nicht für eingebettete Anführungszeichen in .bat oder msiexec-Argumenten

Danke, ich wusste nichts über --% . Gibt es eine Möglichkeit, dies zu tun, ohne die Umgebungsvariable an den nativen Prozess zu verlieren? (und zu allen Prozessen, die es aufrufen könnte)

Gibt es ein PowerShell-Modul, das ein Run-Native Cmdlet implementiert, das jeder verwenden kann? Das klingt nach etwas, das in der Powershell Gallery sein sollte. Wenn es gut genug wäre, könnte es die Basis für einen RFC sein.

"undicht" klingt so, als ob Sie sich Sorgen um die Sicherheit machen. Beachten Sie jedoch, dass die Befehlszeile für untergeordnete Prozesse ohnehin sichtbar ist. (Zum Beispiel: gwmi win32_process |select name,handle,commandline|Format-Table unter Windows und ps -f unter Linux)

Wenn Sie dennoch eine Umgebungsvariable vermeiden möchten, können Sie möglicherweise mithilfe des Aufrufausdrucks etwas erstellen.

In Bezug auf den RFC:
Ich denke nicht, dass ein solches Commandlet notwendig sein sollte, stattdessen sollte dies das Standardverhalten sein:

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

Ich bin damit einverstanden, dass das Standardverhalten von PowerShell behoben werden sollte. Ich war pessimistisch davon ausgegangen, dass es sich aus Gründen der Abwärtskompatibilität niemals ändern würde, weshalb ich vorgeschlagen habe, ein Modul zu schreiben. Mir gefällt jedoch sehr, wie Ihr RFC es ermöglicht, das alte Escape-Verhalten über eine Präferenzvariable wieder zu aktivieren.

Lassen Sie mich die Diskussion mit genau der richtigen Dosis Meinung zusammenfassen:

  • Es ist klar, dass wir ein Problem mit der Abwärtskompatibilität haben, daher muss das alte Verhalten weiterhin verfügbar bleiben.

  • Der RFC-Vorschlag von @TSlivede berücksichtigt dies und weist lobenswerterweise den Weg in die Zukunft.
    Leider ist sein Vorschlag zum jetzigen Zeitpunkt ein PR, und er wurde noch nicht einmal als RFC-Entwurf akzeptiert.


Mit der Zukunft meine ich:

  • PowerShell ist eine eigenständige Shell, die hoffentlich bald ihr mit cmd.exe verbundenes Gepäck ablegen wird. Die einzigen Überlegungen, die beim Aufrufen externer Dienstprogramme (ausführbare Dateien, die (normalerweise) Konsolen- / Terminalanwendungen sind) von Bedeutung sind, sind ::

    • Zu übergebende Argumente sollten durch die Regeln von _PowerShell_s Argument-Modus-Analyse _nur_ angegeben werden.

    • Was auch immer _literale_ aus diesem Prozess resultieren, muss _as-is_ als _individuelle_ Argumente an die ausführbare Zieldatei übergeben werden.

    • Mit anderen Worten: Als Benutzer sollten Sie sich nur auf das Ergebnis der Analyse von _PowerShell_ konzentrieren und sich darauf verlassen können, dass dieses Ergebnis unverändert übergeben wird, wobei PowerShell sich um alle Hintergründe kümmert -Szenencodierung - falls erforderlich.


Umsetzung der Zukunft:

  • Auf Fenstern:

    • Aus historischen Gründen erlaubt Windows nicht, Argumente als Array von Literalen an die ausführbare Zieldatei zu übergeben. Stattdessen wird eine einzelne Zeichenfolge benötigt, die alle Argumente mit der Pseudo-Shell-Syntax codiert. Was noch schlimmer ist, es liegt letztendlich an der einzelnen ausführbaren Zieldatei, diese einzelne Zeichenfolge zu interpretieren und in Argumente aufzuteilen.

    • Das Beste, was PowerShell tun kann, ist, diese einzelne Zeichenfolge - hinter den Kulissen, nachdem die Befehlszeile in einzelne Argumente aufgeteilt wurde - auf vorhersehbare, standardisierte Weise zu bilden.

    • Der RFC-Vorschlag von @TSlivede schlägt genau das vor, indem er vorschlägt, dass PowerShell die Pseudo-Shell-Befehlszeile so synthetisiert, dass die Windows C / C ++ - Laufzeit die Eingabeargumente beim Parsen unverändert wiederherstellt :

      • Angesichts der Tatsache, dass es letztendlich an jeder ausführbaren Zieldatei liegt, die Befehlszeile zu interpretieren, gibt es keine Garantie dafür, dass dies in allen Fällen funktioniert. Diese Regeln sind jedoch die sinnvollste Wahl, da die meisten vorhandenen Dienstprogramme diese Konventionen verwenden.

      • Die einzigen bemerkenswerten Ausnahmen sind Batch-Dateien, die eine Sonderbehandlung erhalten könnten, wie der RFC-Vorschlag vorschlägt.

  • Auf _Unix_-Plattformen:

    • Genau genommen treten die Probleme, die das Parsen von Windows-Argumenten plagen, nie auf, da die plattformeigenen Aufrufe das Erstellen neuer Prozesse erfordern. Akzeptieren Sie Argumente als Arrays von Literalen .
      Um @lzybkr zu zitieren: "Ich sehe keinen Grund, diese Schwierigkeit auf * nix zu übertragen."

    • Leider kommen diese Probleme aufgrund der aktuellen Einschränkungen von .NET Core (CoreFX) ins Spiel, da die CoreFX-API die Anarchie des Windows-Arguments unnötig auf die Unix-Welt überträgt, indem sie sogar die Verwendung einer Pseudo-Befehlszeile erfordert Unix.

    • Ich habe dieses CoreFX- Problem erstellt, um die Behebung dieses Problems zu

    • In der Zwischenzeit sollte der Vorschlag von @TSlivede auch auf Unix-Plattformen funktionieren, da CoreFX die Pseudo-Befehlszeile wieder in Argumente

Da https://github.com/PowerShell/PowerShell/issues/4358 als Duplikat davon geschlossen wurde, hier eine kurze Zusammenfassung dieses Problems:

Wenn ein Argument einer externen ausführbaren Datei mit einem abschließenden Backslash ein Leerzeichen enthält, wird es derzeit naiv in Anführungszeichen gesetzt (Zitat vor und nach dem Argument hinzufügen). Jede ausführbare Datei, die den üblichen Regeln folgt
Aus dem Kommentar von @ mklement0 :

Das 2. " in ".\test 2\" , das von \ vorangestellt wird, wird als Escapezeichen interpretiert, wodurch der Rest der Zeichenfolge - trotz eines damals fehlenden Abschlusses "als Teil des interpretiert wird gleiches Argument.

Beispiel:
(aus @akervinens Kommentar )

PS X:\scratch> .\ps-args-test.exe '.\test 2\'
Erhaltenes Argument: .\test 2"

Das Problem tritt sehr häufig auf, da PSReadLine bei der automatischen Vervollständigung von Verzeichnissen einen abschließenden Backslash hinzufügt.

Da corefx offen für die Produktion der von uns benötigten API zu sein scheint, verschiebe ich dies auf 6.1.0. Für 6.0.0 werde ich sehen, ob wir # 4358 reparieren können

@TSlivede Ich habe Ihre Funktion übernommen, sie in Invoke-NativeCommand (da Run kein gültiges Verb ist) und einen Alias ^ hinzugefügt und als Modul in PowerShellGallery veröffentlicht:

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

@ SteveL-MSFT:

Es ist schön, eine Notlösung zu haben, aber eine weniger umständliche wäre - während wir auf eine CoreFX-Lösung warten - die genau definierten offiziellen Regeln für das Zitieren / Parsen von Argumenten zu implementieren,

Wenn wir nur das Problem \" beheben , ist die Argumentübergabe auch in einfachen Szenarien wie den folgenden immer noch grundlegend fehlerhaft:

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

Ich denke, zu diesem Zeitpunkt besteht eine ausreichende Übereinstimmung darüber, wie das Verhalten aussehen soll, damit wir keinen vollständigen RFC-Prozess benötigen, oder?

Die einzig offene Entscheidung ist, wie mit Abwärtskompatibilitätsproblemen in Windows umgegangen werden soll.

@ mklement0 @ SteveL-MSFT
Sind wir bereits mit der Kompatibilität pleite?

Die einzig offene Entscheidung ist, wie mit Abwärtskompatibilitätsproblemen in Windows umgegangen werden soll.

Ja, aber das ist der schwierige Teil, oder?

@ be5invis was meinst du mit "bereits

Wenn CoreFX kurz vor einer Korrektur in seiner Ebene steht, würde ich lieber keine Notlösung in unserer Ebene erstellen, bevor dies der Fall ist.

Und wie jemand oben im Thread sagte, ist das ärgerlich, aber es ist auch in der Community ziemlich gut dokumentiert. Ich bin mir nicht sicher, ob wir es in den nächsten beiden Veröffentlichungen zweimal brechen sollten.

@joeyaiello :

Ist das Update für # 4358 nicht bereits eine bahnbrechende Änderung für diejenigen, die das Problem umgangen haben, indem sie das endgültige \ verdoppelt haben "c:\tmp 1\\" ? Mit anderen Worten: Wenn Sie die Änderungen an diesem Fix einschränken, sind zwei wichtige Änderungen garantiert: diese jetzt und eine andere später nach dem Wechsel zur zukünftigen CoreFx-API; und während dies auch passieren könnte, wenn jetzt eine vollständige Notlösung implementiert würde, ist es angesichts dessen, was wir über diese bevorstehende Änderung wissen, unwahrscheinlich.

Umgekehrt kann es die Einführung unter Unix behindern, wenn häufig zitierte Szenarien wie z
bash -c 'echo "hi there"' funktioniert nicht richtig.

Mir ist jedoch klar, dass die Behebung dieses Problems eine viel größere Änderung darstellt.

@ PowerShell / Powershell-Komitee diskutierte dies und stimmte zu, dass die Verwendung von --% mindestens das gleiche Verhalten wie Bash haben sollte, da die Anführungszeichen maskiert werden, damit der native Befehl sie empfängt. Was noch zur Debatte steht, ist, ob dies das Standardverhalten ohne Verwendung von --%

Hinweis:

  • Ich gehe davon aus, dass ein Aufruf einer tatsächlichen ausführbaren Shell-Datei erforderlich ist, wenn --% unter _Unix_ verwendet wird, anstatt zu versuchen, _ das Shell-Verhalten zu emulieren, was unter _Windows_ der Fall ist. Das Emulieren ist unter Windows nicht schwierig, unter Unix jedoch viel schwieriger, da viele weitere Funktionen emuliert werden müssten.

  • Die Verwendung einer tatsächlichen Shell wirft dann die Frage auf, welche Shell verwendet werden soll: Während bash allgegenwärtig ist, ist das Standardverhalten weder POSIX-konform noch muss es von POSIX vorhanden sein, sodass für die Portabilität andere Skriptsprachen erforderlich sind /bin/sh , die von POSIX verfügte ausführbare Shell-Datei (die Bash sein kann, die im Kompatibilitätsmodus ausgeführt wird (z. B. unter macOS), muss es aber sicherlich nicht (z. B. Dash unter Ubuntu)).

Wir sollten wohl auch auf /bin/sh abzielen - was jedoch bedeutet, dass einige Bash-Funktionen - insbesondere die Erweiterung der Klammern, bestimmte automatische Variablen, ... - nicht verfügbar sind


Verwendung von --%

Ich werde den Befehl echoargs --% 'echo "hi there"' als Beispiel unten verwenden.

Das gleiche Verhalten wie bei Bash, da die Anführungszeichen maskiert werden, damit der native Befehl sie empfängt.

Der Weg, dies in Zukunft zu tun, nachdem die CoreFX-API erweitert wurde, besteht darin, überhaupt kein Escape auszuführen und stattdessen Folgendes zu tun:

  • Erstellen Sie einen Prozess wie folgt:

    • /bin/sh als ausführbare Datei, (effektiv) ProcessStartInfo.FileName zugewiesen.

    • Das folgende Array von _literal_, _individual_ Argument-Token als ProcessStartInfo.ArgumentList :

    • -c als erstes Argument

    • echoargs 'echo "hi there"' als zweites Argument - dh die ursprüngliche Befehlszeile verwendete _literally_ genau wie angegeben, außer dass --% entfernt wurde.

Tatsächlich wird die Befehlszeile über _as-is_ an die ausführbare Shell-Datei übergeben, die dann ihre Analyse durchführen kann.

Ich verstehe, dass wir in Ermangelung einer Array-basierten Methode zum Übergeben von Literalargumenten -c und echoargs 'echo "hi there"' zu einer einzigen Zeichenfolge kombinieren müssen, wobei die Flucht bedauerlicherweise nur zum Nutzen der CoreFX API_, das beim Erstellen des eigentlichen Prozesses diesen Schritt umkehrt und die einzelne Zeichenfolge wieder in Literal-Token aufteilt - und sicherzustellen, dass diese Umkehrung immer zur ursprünglichen Liste der Literal-Token führt, ist der herausfordernde Teil.

Nochmals: Der einzige Grund, hier überhaupt zu fliehen, liegt in der aktuellen CoreFX-Einschränkung.
Um mit dieser Einschränkung zu arbeiten, muss die folgende einzelne Zeichenfolge mit Escapezeichen der Eigenschaft .Arguments einer Instanz von ProcessStartInfo zugewiesen werden, wobei die Escapezeichenfolge wie in Parsing C ++ - Befehlszeilenargumente angegeben ausgeführt wird:

  • /bin/sh als ausführbare Datei, (effektiv) ProcessStartInfo.FileName zugewiesen.
  • Die folgende einzelne Zeichenfolge mit Escapezeichen als Wert von ProcessStartInfo.Arguments :
    -c "echoargs 'echo \"hi there\"'"

## Standardverhalten

Was noch zur Debatte steht, ist, ob dies das Standardverhalten ohne Verwendung von -% sein sollte

Das Standardverhalten unter Unix sollte sehr unterschiedlich sein:

  • Keine anderen Überlegungen als die von PowerShell sollten jemals ins Spiel kommen (außer bei Windows, wo dies leider nicht vermieden werden kann; aber dort sind die MS C ++ - Regeln der richtige Weg, um hinter den Kulissen angewendet zu werden; andernfalls --% bietet eine Notluke).

  • Alle Argumente, mit denen _PowerShell_ nach seiner eigenen Analyse endet, müssen über die bevorstehende Eigenschaft ProcessStartInfo.ArgumentList als _array of literals_ übergeben werden .

Auf das Beispiel angewendet ohne --% : echoargs 'echo "hi there"' :

  • PowerShell führt die übliche Analyse durch und führt zu den folgenden zwei Argumenten:

    • echoargs
    • echo "hi there" (einfache Anführungszeichen - die nur eine syntaktische Funktion für _PowerShell_ hatten, entfernt)
  • ProcessStartInfo wird dann wie folgt mit der kommenden CoreFX-Erweiterung gefüllt:

    • echoargs als (effektiver) .FileName Immobilienwert
    • _Literal_ echo "hi there" als einziges Element, das der Instanz Collection<string> hinzugefügt wird, die durch .ArgumentList verfügbar gemacht wird.

Auch in Abwesenheit von .ArgumentList ist dies noch keine Option, aber in der Zwischenzeit könnte dieselbe MS C ++ - konforme Hilfsflucht wie oben beschrieben verwendet werden.

@ SteveL-MSFT
Wie ich bereits unter Damit das Stop-Parsing-Symbol (-%) unter Unix funktioniert (# 3733) erwähnt habe, würde ich dringend davon abraten, das Verhalten von --% ändern.

Wenn eine spezielle Funktionalität für /bin/sh -c benötigt wird, verwenden Sie bitte ein anderes Symbol und lassen Sie --% wie es ist!

@TSlivede :

_Wenn_ etwas --% -_ähnliches_ unter Unix implementiert ist - und mit nativem Globbing und einer allgemein befehlszeilenfähigeren Menge unter Unix sehe ich weniger Bedarf dafür - als ein _differentes Symbol_ zu wählen - wie --$ - macht wahrscheinlich Sinn (Entschuldigung, ich hatte alle Aspekte dieser langwierigen Debatte über mehrere Themen aus den Augen verloren).

Verschiedene Symbole würden auch als visuell auffällige Erinnerung daran dienen, dass nicht portierbares plattformspezifisches Verhalten aufgerufen wird.

Das lässt die Frage offen, was PowerShell tun soll, wenn es unter Unix auf --% und unter Windows auf --$ stößt.

Mir geht es gut, wenn ich --% . Die Einführung von --$ das / bin / sh aufruft, und ich denke, cmd.exe unter Windows kann ein guter Weg sein, dies zu lösen.

Keine Chance, ein Cmdlet für diese Verhaltensweisen zu erstellen?

@iSazonov Schlagen Sie so etwas wie Invoke-Native ? Ich bin mir nicht sicher, ob ich ein Fan davon bin.

Ja, wie Start-Native .
Als Witz :-) Magst du keine Cmdlets in PowerShell?

In Build.psm1 haben wir Start-NativeExecution mit Link zu https://mnaoumov.wordpress.com/2015/01/11/execution-of-external-commands-in-powershell-done-right/

@ SteveL-MSFT

Mir geht es gut, wenn ich gehe -% wie es ist.

Ich denke, wir sind uns alle einig, dass sich --% weiterhin so verhalten muss, wie es auf _Windows_ der Fall ist.

Auf _Unix_ macht dieses Verhalten dagegen keinen Sinn, wie ich hier zu demonstrieren versucht habe - kurz:

  • einfache Anführungszeichen werden nicht korrekt behandelt
  • Die einzige Möglichkeit, auf Umgebungsvariablen zu verweisen, ist _cmd.exe-style_ ( %var% )
  • Wichtige native Funktionen wie Globbing und Wortteilung funktionieren nicht.

Die Hauptmotivation für die Einführung von --% war, wenn ich das richtig verstehe, die Wiederverwendung vorhandener cmd.exe Befehlszeilen wie sie sind zu ermöglichen.

  • Daher ist --% unter Unix mit seinem aktuellen Verhalten nutzlos.
  • _Wenn wir ein _analoges_ Feature unter Unix haben wollten, müsste es wie oben vorgeschlagen auf /bin/sh -c basieren, wahrscheinlich unter Verwendung eines anderen Symbols.

Ich glaube nicht, dass unter Windows eine cmd /c -basierte Funktion erforderlich ist, da --% diese _ größtenteils_ abgedeckt hat, wohl auf eine Weise, die _gut genug_ ist.
@TSlivede hat darauf hingewiesen, dass nicht alle Shell-Funktionen emuliert werden, aber in der Praxis scheint dies kein %envVar:old=new% nicht unterstützt, ^ ist kein Escape-Zeichen, und die Verwendung von --% ist auf einen _single_-Befehl beschränkt - keine Verwendung der Umleitungs- und Steuerungsoperatoren von cmd.exe , das heißt, ich glaube nicht, dass --% sollte jemals den gesamten Befehl _lines_) emulieren.

Als solches wäre so etwas wie --$ - falls implementiert - das Unix-Gegenstück zu --% .

In jedem Fall verdient zumindest das about_Parsing eine auffällige Warnung, dass --% unter allen Unix unbrauchbar sein wird.

@iSazonov Es mag sinnvoll sein, Start-Native zu haben, um bestimmte Szenarien zu behandeln, aber wir sollten versuchen, PowerShell zu verbessern, damit die Verwendung nativer Exes natürlicher und vorhersehbarer ist

@ PowerShell / Powershell-Committee hat dies überprüft und ist sich einig, dass --% bedeuten sollte, die Argumente wie auf ihren relevanten Plattformen zu behandeln, was bedeutet, dass sie sich unter Windows und Linux unterschiedlich verhalten, aber unter Windows konsistent und unter Linux konsistent sind. Es wäre für den Benutzer verwirrender, ein neues Siegel einzuführen. Wir überlassen die Implementierung dem Ingenieur, wie dies ermöglicht werden kann.

Plattformübergreifende Skripte müssten sich dieses unterschiedlichen Verhaltens bewusst sein, aber es ist unwahrscheinlich, dass Benutzer davon betroffen sind. Wenn das Feedback der Benutzer lautet, dass aufgrund einer stärkeren plattformübergreifenden Nutzung ein Bedarf besteht, können wir die Einführung eines neuen Siegels erneut prüfen.

Zum zweiten Mal wurde ich von diesen nativen vs Cmdlets-String-Argumenten gebissen, die Unterschiede bei der Verwendung von ripgrep analysieren .

Hier ist das Ergebnis von Powershell-Aufrufen (echo.exe stammt aus "C: \ Programme \ Git \ usrbin \ echo.exe")

Jetzt weiß ich, dass ich auf diese " Eigenart achten sollte:

> echo.exe '"test'
test

Aber diese Eigenart ist mir ein Rätsel ...

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

Im zweiten Fall muss ich das doppelte \ , um \ an den nativen Befehl zu übergeben, im ersten Fall muss ich es nicht tun 😑

Ich verstehe, dass dies eine bahnbrechende Änderung wäre, um dieses Verhalten zu ändern, sodass es keinen Unterschied zwischen Cmlets und nativen Befehlen gibt. Ich denke jedoch, dass solche Macken die Leute davon abhalten, Powershell als Standard-Shell zu verwenden.

@mpawelski Ich habe dies gerade auf meiner Windows-Box mit git 2.20.1.vfs.1.1.102.gdb3f8ae versucht und es wird für mich mit 6.2-RC.1 nicht reproduziert. Lief es mehrmals und es hallt durchweg ^\+.+; wider

@ SteveL-MSFT, ich denke, dass @mpawelski versehentlich denselben Befehl zweimal angegeben hat. Sie werden das Problem sehen, wenn Sie beispielsweise '^\"+.*;' - beachten Sie den Teil \" - mit der - vernünftigen - Erwartung, dass der Inhalt der Zeichenfolge in einfachen Anführungszeichen unverändert durchlaufen wird , so dass das externe Zielprogramm ^\"+.*; als Wert des Arguments sieht:

# 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"' - '^\"+.*;'
^\"+.*; 

-% sollte, wenn ich das richtig verstehe, die Wiederverwendung vorhandener cmd.exe-Befehlszeilen unverändert ermöglichen.

Das ist es nicht ganz. --% wurde eingeführt, weil viele Windows-Befehlszeilen-Utils Argumente verwenden, die von PowerShell interpretiert werden und den Aufruf des Dienstprogramms vollständig unterstützen. Dies kann Leute schnell sauer machen, wenn sie ihre bevorzugten einheimischen Utensilien nicht mehr einfach verwenden können. Wenn ich für jedes Mal ein Viertel hätte, wenn ich Fragen zu SO und anderswo beantwortet habe, könnte ich die Familie wahrscheinlich zum Abendessen nach Qoba mitnehmen. :-) Zum Beispiel erlaubt tf.exe ; als Teil der Angabe eines Arbeitsbereichs. Git erlaubt {} und @~1 usw. usw. usw. usw.

--% wurde hinzugefügt, um PowerShell anzuweisen, den Rest der Befehlszeile NICHT zu analysieren - senden Sie ihn einfach "wie besehen" an die native Exe. Erlauben Sie mit dem einen Rubel Variablen mit der Syntax cmd env var. Es ist ein bisschen hässlich, aber Mann, es ist wirklich, wirklich nützlich, immer noch unter Windows.

Wenn ich dies zu einem Cmdlet mache, bin ich mir nicht sicher, wie das funktioniert. --% ist ein Signal an den Parser, die Analyse bis EOL herunterzudrehen.

Ehrlich gesagt ist es für mich als langjähriger Benutzer von PowerShell und insbesondere dieser Funktion sinnvoll, denselben Operator auf anderen Plattformen zu verwenden, um einfach zu bedeuten, dass das Parsen bis EOL heruntergefahren wird. Es gibt die Frage, wie eine Form der Variablensubstitution zugelassen werden kann. Obwohl es sich etwas hässlich anfühlt, könnten Sie unter macOS / Linux% envvar% nehmen und den Wert einer entsprechenden env var ersetzen. Dann könnte es zwischen Plattformen portierbar sein.

Die Sache ist, wenn Sie dies nicht tun, erhalten Sie bedingten Code - nicht genau das, was ich als tragbar bezeichnen würde:

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

Ich würde diese Arbeit auf allen Plattformen bevorzugen:

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

Das Verhalten unter Windows muss unverändert bleiben, da es kompatibel ist.

@ Rkeithhill

Das ist es nicht ganz.

Durch den Eliminierungsprozess wurden Befehlszeilen, die vor PowerShell in der Windows-Welt erstellt wurden, für cmd.exe - und genau das beabsichtigt --% zu emulieren , wie Folgendes zeigt:

  • --% erweitert cmd.exe -Stil %...% Verweise %USERNAME% (wenn keine Shell und keine spezielle Logik beteiligt wären, würden solche Token übergeben _wörtlich_)

  • direkt aus dem Mund der PowerShell (wenn Sie so wollen; Hervorhebung hinzugefügt):

Das Web ist voll von Befehlszeilen, die für Cmd.exe geschrieben wurden . Diese Befehlszeilen funktionieren in PowerShell häufig genug. Wenn sie jedoch bestimmte Zeichen enthalten, z. B. ein Semikolon (;), ein Dollarzeichen ($) oder geschweifte Klammern, müssen Sie einige Änderungen vornehmen und möglicherweise einige Anführungszeichen hinzufügen. Dies schien die Ursache vieler kleinerer Kopfschmerzen zu sein.

Um dieses Szenario zu lösen, haben wir eine neue Methode hinzugefügt, um das Parsen von Befehlszeilen zu umgehen. Wenn Sie einen magischen Parameter -% verwenden, stoppen wir das normale Parsen Ihrer Befehlszeile und wechseln zu etwas viel Einfacherem. Wir stimmen nicht mit Anführungszeichen überein. Wir hören nicht beim Semikolon auf. Wir erweitern keine PowerShell-Variablen. Wir erweitern Umgebungsvariablen, wenn Sie die Cmd.exe-Syntax verwenden (z. B.% TEMP%). Ansonsten werden die Argumente bis zum Ende der Zeile (oder Pipe, wenn Sie Piping verwenden) unverändert übergeben. Hier ist ein Beispiel:

Beachten Sie, dass dieser Ansatz für die _Unix_-Welt schlecht geeignet ist (um es von oben zusammenzufassen):

  • Das Globbing von nicht zitierten Argumenten wie *.txt funktioniert nicht.

  • Herkömmliche (POSIX-ähnliche) Shells in der Unix-Welt verwenden _nicht %...% , um auf Umgebungsvariablen zu verweisen. Sie erwarten $... Syntax.

  • Es gibt (zum Glück) keine _raw_-Befehlszeile in der Unix-Welt: Jeder externen ausführbaren Datei muss ein _array_ von _literals_ übergeben werden, daher muss PowerShell oder CoreFx die Befehlszeile zuerst in Argumente analysieren.

  • Herkömmliche (POSIX-ähnliche) Shells in der Unix-Welt akzeptieren '...' (in einfachen Anführungszeichen) Zeichenfolgen, die --% nicht erkennen - siehe # 10831.

Aber selbst in der Windows-Welt hat --% schwerwiegende, nicht offensichtliche Einschränkungen :

  • Am offensichtlichsten ist, dass Sie in Ihrer Befehlszeile nicht direkt auf PowerShell-Variablen verweisen können, wenn Sie --% . Die einzige - umständliche - Problemumgehung besteht darin, vorübergehend _umvironment_-Variablen zu definieren, auf die Sie dann mit %...% verweisen müssen.
  • Sie können die Befehlszeile nicht in (...) einschließen, da das schließende ) als wörtlicher Teil der Befehlszeile interpretiert wird.
  • Sie können der Befehlszeile nicht mit ; und einer anderen Anweisung folgen, da ; als wörtlicher Teil der Befehlszeile interpretiert wird.
  • Sie können --% in einem einzeiligen Skriptblock verwenden, da das schließende } als wörtlicher Teil der Befehlszeile interpretiert wird.
  • Sie können keine Umleitungen verwenden, da diese als wörtlicher Teil der Befehlszeile behandelt werden. Sie können jedoch cmd --% /c ... > file , damit cmd.exe die Umleitung übernimmt.
  • Sie können keine Zeilenfortsetzungszeichen verwenden - weder PowerShell-Zeichen ( ` ) noch cmd.exe-Zeichen ( ^ ) - sie werden als _literals_ behandelt.

    • --% analysiert immer (höchstens) bis zum Ende der Zeile.


Glücklicherweise haben wir bereits eine plattformübergreifende Syntax: PowerShells eigene Syntax.

Ja, um dies zu erreichen, müssen Sie wissen, was PowerShell als Metazeichen betrachtet. Dies ist eine Obermenge sowohl von cmd.exe als auch von POSIX-ähnlichen Shells wie Bash, die Metazeichen berücksichtigen. Agnostische Kommandozeilenerfahrung.

Wie bedauerlich ist es also, dass PowerShell das Zitieren von " -Zeichen so schlecht handhabt - das ist das eigentliche Thema dieser Ausgabe und das in dieser Dokumentausgabe zusammengefasst ist .

@rkeithhill , ich habe ein bisschen tangential angefangen; Lassen Sie mich versuchen, es zu schließen:

  • --% tatsächlich bereits ab PowerShell Core 6.2.0 implementiert. z.B,
    /bin/echo --% %HOME% den Wert der Umgebungsvariablen HOME ; im Gegensatz,
    /bin/ls --% *.txt funktioniert nicht wie erwartet, da *.txt als Literal übergeben wird.

  • Wenn wir --% , müssen wir den Benutzern helfen, zu diagnostizieren, wie das Befehlszeilen- / Argumentarray, das hinter den Szenen aufgebaut ist, letztendlich aussieht (was uns zurück zum ehrwürdigen # 1761 führt):

    • Ihr hilfreiches echoArgs.exe macht genau das, und in der verknüpften Ausgabe haben Sie vernünftigerweise gefordert, dass solche Funktionen Teil von PowerShell selbst sind.
    • @ SteveL-MSFT überlegte, ob die resultierende Befehlszeile in den Fehlerdatensatz aufgenommen werden sollte.

Um mein vorheriges Argument - unter Verwendung der PowerShell-eigenen Syntax als inhärent portable - auf Ihr Beispiel anzuwenden:

# 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

Ja, Sie müssen wissen, dass eine Token-Initiale @ ein Metazeichen ist, das Sie daher zitieren müssen. Mit zunehmender Verbreitung von PowerShell sollte jedoch auch das Bewusstsein für solche Anforderungen weiter verbreitet werden.

Zu Ihrer Information, dieses Problem sollte unter Windows und Unix fast gleich sein. Die CoreFx-Implementierung von Unix SDProcess hat eine Funktion namens ParseArgumentsIntoList , die CommandLineToArgvW nahezu genau ohne _setargv -Schalter implementiert (und mit den undokumentierten "" in Anführungszeichen → " feature). Unix sollte dabei kein zusätzlicher Schmerzpunkt sein, da es in der aktuellen Form genauso kaputt ist wie Windows.

_setargv ist schließlich nicht etwas, das jedes Programm verwendet, und es lohnt sich wahrscheinlich nicht, darüber nachzudenken, da es mit Verhaltensänderungen zwischen CRT-Versionen behaftet ist. Das Beste, was wir tun können und sollten, ist, alles mit doppelten Anführungszeichen zu umgeben, ein paar nette Backslahes hinzuzufügen, und das ist alles.

Ein weiteres Beispiel, bei dem Argumente nicht richtig analysiert werden:

az "myargs&b"

In diesem Fall erhält az myargs und b wird versucht, als neuer Befehl ausgeführt zu werden.

Problemumgehung ist: az -% "myargs & b"

@ SteveL-MSFT, wir können das Label Waiting - DotNetCore entfernen, da die erforderliche Funktion - die sammlungsbasierte Eigenschaft ProcessStartInfo.ArgumentList - seit .NET Core 2.1 verfügbar ist

@TSlivede ist sich der neuen Methode bewusst und plant, sie zu verwenden, aber der zugehörige RFC, https://github.com/PowerShell/PowerShell-RFC/pull/90 , leidet leider.

Ich schlage vor, wir setzen die Implementierungsdiskussion dort fort.

In der RFC-Diskussion spricht @joeyaiello davon, die Änderungen zu einer experimentellen Funktion zu machen, aber mir wird zunehmend klar, dass Sie das

Jeder, der herumarbeiten musste:

  • die Unfähigkeit, ein Argument mit leeren Zeichenfolgen zu übergeben ( foo.exe "" übergibt derzeit _no_ Argumente)
  • das unerwartete effektive Entfernen von doppelten Anführungszeichen aufgrund des Fehlens des automatischen Escapings eingebetteter doppelter Anführungszeichen ( foo.exe '{ "foo": "bar" }' wird als nicht ordnungsgemäß entkommenes "{ "foo": "bar" }" )
  • die Macken bestimmter CLIs wie msiexec , die bestimmte Argumente nicht in doppelten Anführungszeichen akzeptieren ( foo.exe foo="bar none" werden als "foo=bar none" ).
    Hinweis: Hier ist msiexec schuld, und wenn die vorgeschlagenen Änderungen angewendet werden, erfordert das Bestehen der erforderlichen foo="bar none" -Zitatform --% .

wird in Schwierigkeiten sein, weil die Problemumgehungen mit den vorgeschlagenen Änderungen _break_ brechen.

Daher ist eine zusätzliche Frage:

  • Wie können wir korrektes Verhalten zumindest als _opt-in_-Funktion verfügbar machen?

  • Ein grundlegendes Problem bei solchen Mechanismen - normalerweise nach Präferenzvariablen, aber zunehmend auch nach using -Anweisungen - ist das dynamische Scoping von PowerShell. Das heißt, das aktivierte Verhalten wird standardmäßig auch auf Code angewendet, der als _von_ dem aktivierten Code bezeichnet wird, was problematisch ist.

    • Vielleicht ist es an der Zeit, das _lexical__ Scoping von Features allgemein einzuführen, eine Verallgemeinerung des lexikalisch definierten using strict -Vorschlags in dem von @lzybkr verfassten - ebenso schmachtenden - lexikalischen RFC im strengen Modus .

    • So etwas wie ein lexikalisch using preference ProperArgumentQuoting ? (Name offensichtlich verhandelbar, aber ich habe Mühe, einen zu finden).

Angesichts all dieser Einschränkungen und der Tatsache, dass dies auch ein Problem beim direkten Aufruf ist, bei dem wir nicht einfach einen neuen Parameter hinzufügen können, bin ich entschieden dafür, das alte Verhalten zu brechen.

Ja, es wird wahrscheinlich viel kaputt gehen. Aber wirklich, nur weil es von Anfang an so gründlich kaputt war. Ich denke nicht, dass es besonders machbar ist, die Beibehaltung eines riesigen Haufens von Problemumgehungen für fehlerhaftes Verhalten gegenüber einer Funktion zu priorisieren, die tatsächlich funktioniert.

Als neue Hauptversion denke ich, dass v7 wirklich die einzige Chance ist, diese Situation für einige Zeit richtig zu korrigieren, und wir sollten die Gelegenheit nutzen. Vorausgesetzt, wir machen die Benutzer darauf aufmerksam, denke ich nicht, dass der Übergang insgesamt schlecht aufgenommen wird.

Wenn wir der Meinung sind, dass bahnbrechende Änderungen unvermeidlich sind, sollten wir vielleicht die ideale Lösung entwerfen, wobei zu berücksichtigen ist, dass das Drucken in einer interaktiven Sitzung einfach sein sollte und es möglich ist, eine Skriptversion zu haben, die auf allen Plattformen besser funktioniert.

Einverstanden, @ vexx32 : Das Beibehalten des vorhandenen Verhaltens, auch wenn dies nur standardmäßig der

Eine Shell, die Argumente nicht zuverlässig an externe Programme weitergibt, erfüllt eines ihrer Kernmandate nicht.

Sie haben sicherlich meine Stimme für die bahnbrechende Änderung, aber ich befürchte, dass andere sich anders fühlen werden, nicht zuletzt, weil v7 angepriesen wird, dass langfristige WinPS-Benutzer auf PSCore migrieren können.


@iSazonov : https://github.com/PowerShell/PowerShell-RFC/pull/90 beschreibt die richtige Lösung.

Um seinen Geist zu rekapitulieren:

PowerShell muss als Shell Argumente gemäß den Regeln von _its_ analysieren und dann die resultierenden erweiterten Argumentwerte _verbatim_ an das Zielprogramm übergeben. Benutzer sollten niemals darüber nachdenken müssen, wie PowerShell dies ermöglicht. Alles, worüber sie sich jemals Sorgen machen sollten, ist die richtige PowerShell-Syntax.

  • Auf Unix-ähnlichen Plattformen bietet ProcessStartInfo.ArgumentList jetzt eine Möglichkeit, dies perfekt zu implementieren, da das Array erweiterter Argumentwerte so wie es ist an das Zielprogramm übergeben werden kann, da das Übergeben von Argumenten - sinnvoll - so funktioniert in dieser Welt.

  • Unter Windows müssen wir uns mit der unglücklichen Realität der Anarchie auseinandersetzen, die Windows-Befehlszeilenanalyse ist , aber als Shell müssen wir sie so weit wie möglich hinter den Kulissen arbeiten lassen, wie es der RFC tut beschreibt - obwohl ich gerade eine Falte entdeckt habe , bei der die alleinige Verwendung von ProcessStartInfo.ArgumentList leider nicht gut genug ist (aufgrund der weit verbreiteten Batch-Dateien_ als CLI-Einstiegspunkte, wie @ SteveL-MSFTs az[.cmd] Beispiel oben). Für die Randfälle, in denen das Vernünftige nicht gut genug ist, gibt es --% .

Möglicherweise kann PSSA dazu beitragen, die Änderung zu verringern, indem Benutzer gewarnt werden, dass sie ein Argumentformat verwenden, das geändert wird.

Ich denke, wir müssen darüber nachdenken, so etwas wie optionale Funktionen zu übernehmen, um diese bahnbrechende Änderung gemeinsam mit einigen anderen voranzutreiben.

Gibt es wirklich einen Wert darin, das bestehende Verhalten beizubehalten? Abgesehen von der Abwärtskompatibilität meine ich.

Ich denke nicht, dass es sich lohnt, zwei Codepfade dafür zu pflegen, nur um eine fehlerhafte Implementierung beizubehalten, da einige alte Codes sie gelegentlich benötigen. Ich denke nicht, dass es unvernünftig ist zu erwarten, dass folx ab und zu ihren Code aktualisiert. 😅

Doppelt so, wenn wir erwarten, dass PS7 ein Ersatz für WinPS ist; Der einzige Grund, warum ich das Verhalten für v7 beibehalten kann, ist, wenn wir erwarten, dass die Leute dasselbe Skript verwenden, um Befehle auf 5.1 und 7 auszuführen. Dies sollte (hoffentlich) ein ziemlich seltener Fall sein, wenn PS7 ein guter Ersatz für 5.1 ist.

Und selbst dann wäre es für Benutzer nicht allzu schwierig, beides zu berücksichtigen. Vorausgesetzt, wir ändern die tatsächliche Sprachsyntax nicht, sollte es ziemlich einfach sein, so etwas zu tun:

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

Vorausgesetzt, wir machen die Benutzer auf den Unterschied aufmerksam, wäre es meiner Meinung nach eine willkommene Erleichterung von dem Schmerz, mit seltsamen nativen ausführbaren Dateien in PS bisher umzugehen. 😄

Wie @TylerLeonhardt in der Diskussion der optionalen Features erwähnt hat - Implementierung bedeutet, dass Sie jetzt mehrere unterschiedliche Implementierungen verwalten, die jeweils gewartet und getestet werden müssen, sowie das optionale Feature-Framework warten und testen. Scheint das nicht wirklich wert zu sein, tbh.

@ vexx32 Abwärtskompatibilität ist hier ein großes Problem. Dies ist nicht das einzige Problem für Argumente für native ausführbare Dateien. Ich möchte sie alle zusammenfassen und sie alle zu einer "optionalen Funktion" machen. Zum Beispiel https://github.com/PowerShell/PowerShell/issues/1761 und https://github.com/PowerShell/PowerShell/issues/10675. Die Interoperabilität mit nativen Befehlen "reparieren" möchte ich für vNext beheben. Wenn also jemand vorhandene oder neue Probleme in dieser Kategorie sieht, melden Sie sich bei mir und ich werde sie entsprechend kennzeichnen (oder wenn Sie die Triage-Berechtigung haben, kennzeichnen Sie sie wie die anderen).

Optionale Funktionen sind Module :-) Optionale Funktionen in Engine zu haben, bereitet der Unterstützung große Kopfschmerzen, insbesondere der Windows-Unterstützung. Wir könnten Engine modularisieren, indem wir interne Abhängigkeiten reduzieren und interne APIs durch öffentliche ersetzen. Danach könnten wir auf einfache Weise optionale Funktionen in Engine implementieren.

@iSazonov Eines der Dinge, die mein Team in vNext betrachten wird, ist, die Engine modularer zu gestalten :)

Was ist hier die empfohlene Lösung für Endbenutzer?

Dies ist erfunden, aber es ist der einfachste Weg, um vom .NET Framework selbst zur richtigen * ArgumentList-Behandlung zu gelangen:

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'

Natürlich können Sie die Verwendung hier weniger kompliziert gestalten, indem Sie Positionsparameter und einige get-command Tricks verwenden.

* HAFTUNGSAUSSCHLUSS: Es gibt keine einzige korrekte Methode zum Parsen einer Cmdline unter Windows. Mit "korrekt" meine ich den MSVCRT-Stil der cmdline-Konvertierung von / nach argv, der von .NET auf allen Plattformen für die ArgumentList-Verarbeitung, die Verarbeitung von main (string[] args) und externe Spawn-Aufrufe unter Unix implementiert wird. Dieses Beispiel wird wie besehen ohne Garantie für die allgemeine Interoperabilität bereitgestellt. Siehe auch den Abschnitt "Windows-Befehlszeile" in der vorgeschlagenen Dokumentation zu NodeJS child_process .

@ Artoria2e5 Genau zu diesem Schluss bin ich gekommen. System.Diagnostics.Process ist die einzige zuverlässige Methode zum Ausführen einer externen ausführbaren Datei, aber das Entkommen von Argumenten kann aufgrund der stdlib-Regeln schwierig werden:

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

Als Ergebnis habe ich mir die folgende Logik ausgedacht, um Argumenten zu entgehen, die sie in doppelte Anführungszeichen setzen " für die Prozessausführung:
https://github.com/choovick/ps-invoke-externalcommand/blob/master/ExternalCommand/ExternalCommand.psm1#L244

Es kann auch schwierig werden, STDOUT und STDERR in Echtzeit abzurufen, während eine externe ausführbare Datei ausgeführt wird. Daher habe ich dieses Paket erstellt

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

dass ich stark unter Windows, Linux und Mac benutze und bisher ohne Probleme und ich Argumente mit Zeilenumbruch und anderen Sonderzeichen darin übergeben kann.

GitHub
Tragen Sie zur Entwicklung von choovick / ps-invoke-externalcommand bei, indem Sie ein Konto auf GitHub erstellen.
GitHub
Tragen Sie zur Entwicklung von choovick / ps-invoke-externalcommand bei, indem Sie ein Konto auf GitHub erstellen.

@choovick die ganze stdio redirect sache ist

Ich bin jedoch ein wenig anderer Meinung als der entkommende Teil, da es bereits eine Sache gibt, die dies für Sie erledigt, nämlich ArgumentList. Ich verstehe, dass es sich um eine relativ neue (?) Ergänzung handelt, und es wird enttäuschend, da MS vergessen hat, einen String, String [] -Initialisierer für SDProcessStartInfo einzufügen. (Gibt es einen Platz für diese… .NET-Schnittstellenvorschläge?)


Geplauder

Meine Escape-Funktion aus diesem NodeJS-Beispiel unterscheidet sich ein wenig von Ihrer: Sie verwendet das undokumentierte (aber in .NET Core und MSVCRT enthaltene) "" Escape für Anführungszeichen. Dies vereinfacht die Backslash-Picking-Arbeit. Ich habe dies hauptsächlich getan, weil es für das mächtige Cmd verwendet wurde, was nicht versteht, dass \" den Rest der Zeichenfolge nicht aus dem Zitat entfernen sollte. Anstatt mit \^" kämpfen, dachte ich mir, dass ich mit etwas besser dran sein werde, das seit Beginn der Zeit im geheimen Gebrauch ist.

@ Artoria2e5 ArgumentList ist in PowerShell 5.1 unter erhalte ich Folgendes:

`` `Sie können keine Methode für einen nullwertigen Ausdruck aufrufen.
Bei C: Benutzer \ yser \ dev \ test.ps1: 7 char: 37

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

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

    • FullyQualifiedErrorId: InvokeMethodOnNull

      `` `

Da benutzerdefinierte Argumente Escape-Logik ....

Geplauder

In Bezug auf `\ ^" `in NodeJS denke ich, dass ich das vor einigen Jahren tun musste :) und ich denke, es hat funktioniert

System.Diagnostics.Process ist die einzige zuverlässige Möglichkeit, eine externe ausführbare Datei auszuführen

Das Problem ist, dass Sie keine Integration in die Ausgabestreams von PowerShell erhalten und kein Streaming-Verhalten in der Pipeline erhalten.

Hier ist die Zusammenfassung der erforderlichen Problemumgehungen, wenn PowerShell den Aufruf weiterhin ausführen soll (was definitiv vorzuziehen ist) :

  • Wenn Sie Argumente mit _embedded_ " Zeichen übergeben müssen, verdoppeln Sie sie unter Windows, wenn möglich, oder \ -scape sie:

    • Wenn Sie unter Windows _batch files_ aufrufen und wissen, dass das Zielprogramm "" als maskiertes " , verwenden Sie $arg -replace '"', '""'

      • Die Verwendung von "" ist unter Windows vorzuziehen (dies vermeidet das Windows PowerShell-Problem und funktioniert mit CLIs, die Batchdateien wie _stubs_ verwenden, wie Node.js und Azure), aber nicht alle ausführbaren Dateien unterstützen dies (insbesondere nicht Ruby und Perl).
    • Andernfalls (immer unter Unix) verwenden Sie $arg -replace '"', '\"'

    • Hinweis: In _Windows PowerShell_ funktioniert dies immer noch nicht ordnungsgemäß, wenn der Wert auch _spaces_ enthält, da das Vorhandensein von Literal \" im Wert im Gegensatz zu PowerShell Core situativ keine doppelten Anführungszeichen auslöst. zB Übergeben von '3\" of snow' Pausen.

    • Darüber hinaus müssen Sie vor dem Entstehen der obigen Instanz die Instanz \ unmittelbar vor " verdoppeln, wenn sie als Literale behandelt werden sollen:

      • $arg = $arg -replace '(\\+)"', '$1$1"'
  • Wenn Sie ein leeres Argument übergeben müssen, übergeben Sie '""' .

    • '' -eq $arg ? '""' : $arg (WinPS-Alternative: ($arg, '""')['' -eq $arg]
  • Tun Sie dies nur in Windows PowerShell nicht in PS Core (wo das Problem behoben wurde):

    • Wenn Ihr Argument Leerzeichen enthält und in (einem oder mehreren) \ endet, verdoppeln Sie die nachfolgenden Instanzen von \ .

      • if ($arg -match ' .*?(\\+)$') { $arg = $arg + $Matches[1] }
  • Wenn cmd / eine Batchdatei mit Argumenten aufgerufen wird, die _nicht_ Leerzeichen enthalten (daher _nicht_ ein automatisches Anführungszeichen durch PowerShell auslösen), aber &|<>^,; (z. B. a&b ), verwenden Sie _embedded mit doppelten Anführungszeichen_, um sicherzustellen, dass PowerShell ein Token in doppelten Anführungszeichen übergibt und daher den Aufruf von cmd / Batch-Datei nicht unterbricht:

    • $arg = '"' + $arg + '"'
  • Wenn Sie sich mit schlecht benommenen ausführbaren Dateien wie msiexec.exe befassen müssen, zitieren Sie das Argument in einfachen Anführungszeichen:

    • 'foo="bar none"'

Wie bereits erwähnt, brechen diese Problemumgehungen, sobald das zugrunde liegende Problem behoben ist.


Unten ist eine einfache (nicht erweiterte) Funktion iep (für "externes Programm aufrufen"), die:

  • Führt alle oben beschriebenen Escape-Aktionen aus, einschließlich der automatischen Spezialhülle für msiexec und der Bevorzugung von "" Escape gegenüber \" abhängig vom Zielprogramm.

    • Die Idee ist, dass Sie jedes Argument übergeben können, indem Sie sich nur auf die String-Syntax von _PowerShell_ konzentrieren und sich auf die Funktion verlassen, um die erforderliche Escape-Funktion auszuführen, damit der wörtliche Wert, den PowerShell sieht, auch vom Zielprogramm gesehen wird.

    • In PowerShell _Core_ sollte dies ziemlich robust funktionieren. In Windows PowerShell gibt es immer noch Randfälle mit eingebetteten doppelten Anführungszeichen, die unterbrochen werden, wenn \" Escape verwendet werden muss (wie oben erläutert).

  • Erhält die Aufrufsyntax für Shell-Befehle.

    • Stellen Sie Ihrer Befehlszeile einfach iep  voran.

  • Als direkter Aufruf würde es:

    • lässt sich in die Streams von PowerShell integrieren

    • Sendet die Ausgabe zeilenweise durch die Pipeline

    • setzt $LASTEXITCODE basierend auf dem Exit-Code des externen Programms; Auf $? kann man sich jedoch nicht verlassen.

Hinweis: Die Funktion ist absichtlich minimalistisch (keine Parameterdeklarationen, keine Befehlszeilenhilfe, kurzer (unregelmäßiger) Name), da sie so unauffällig wie möglich sein soll: Stellen Sie einfach iep vor Ihre Befehlszeile und andere Dinge sollte arbeiten.

Beispielaufruf mit EchoArgs.exe (installierbar über Chocolatey aus einer _elevated_ Sitzung mit 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"

Das Obige zeigt die PowerShell Core-Ausgabe. Beachten Sie, wie alle Argumente korrekt durchlaufen wurden, wie von PowerShell wörtlich gesehen, einschließlich des leeren Arguments.

In Windows PowerShell wird das Argument 3" of snow nicht korrekt übergeben, da das Escapezeichen \" verwendet wird, weil eine unbekannte ausführbare Datei aufgerufen wird (wie oben erläutert).

Um zu überprüfen, ob Batch-Dateien Argumente korrekt weitergeben, können Sie echoargs.cmd als Wrapper für echoargs.exe erstellen:

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

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

Da jetzt eine Batchdatei aufgerufen wird, wird "" -escaping verwendet, wodurch das Problem 3" of snow beim Aufrufen von Windows PowerShell behoben wird.

Die Funktion funktioniert auch auf Unix-ähnlichen Plattformen, die Sie überprüfen können, indem Sie ein sh Shell-Skript mit dem Namen echoargs erstellen:

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

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


Wichtig : Eine vollständigere Version dieser Funktion wurde inzwischen als ie ( I nvoke (external) E xecutable) veröffentlicht. Ich habe gerade ein Modul Native , zu dessen Verwendung ich Sie ermutige stattdessen. Installieren Sie das Modul mit
Install-Module Native -Scope CurrentUser .
Das Modul enthält außerdem einen Befehl ins ( Invoke-NativeShell ), der den in # 13068 beschriebenen Anwendungsfall behandelt - siehe https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -671572939 für Details.

Quellcode der Funktion iep (verwenden Sie stattdessen das Modul Native - siehe oben):

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 Beeindruckend, aber hier sind ein paar, die für mich unter Windows nicht funktionieren:

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

Hier ist mein Testarray von Argumenten :)

$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 hat Einschränkungen bei der Verwendung. Deshalb musste ich meinen eigenen Runner mit den Fähigkeiten schreiben, STDOUT STDERR zu erfassen und dann in Variablen zu kombinieren, was für meine Anwendungsfälle ausreichend, aber nicht perfekt ist.

Bearbeiten: Wie Sie sagten, scheint es in Windows Poweshell 5 Randfälle zu haben. Ich habe auf Powershell 6 getestet und es funktioniert großartig! Leider muss ich mich mit Poweshell 5 beschäftigen, bis 7 es in Windows übernimmt ...

Beeindruckend, aber hier sind ein paar, die unter Windows bei mir nicht funktionieren:

Ja, diese Einschränkungen gelten für _Windows PowerShell_ mit ausführbaren Dateien, für die keine Unterstützung für das Entkommen von "" aus eingebettetem " angenommen werden kann, wie in meinem vorherigen Kommentar beschrieben.
Wenn Sie bereit sind, die Unterstützung für "" anzunehmen, die in all Ihren Aufrufen nicht vorhanden ist (_most_, aber nicht alle ausführbaren Dateien unter Windows unterstützen dies), können Sie die Funktion problemlos optimieren.

Und um zu bestätigen, was Sie in Ihrer Bearbeitung gesagt haben: In PowerShell _Core_:

  • iep echoargs 'somekey="value with spaces"' 'te\" st' funktioniert gut.
  • Ihr Testarray von Argumenten scheint auch gut zu funktionieren.

Wenn Sie bereit sind, die Unterstützung für "" anzunehmen, die in all Ihren Aufrufen maskiert wird (_most_, aber nicht alle ausführbaren Dateien unter Windows unterstützen dies), können Sie die Funktion problemlos optimieren.

Danke, ich werde es in naher Zukunft versuchen. Ich beschäftige mich gelegentlich mit sqlcmd und es unterstützt "" sicher nicht. In diesem Fall ist es einfach, eine Option zum Überspringen der Escape-Logik für bestimmte Argumente bereitzustellen.

Die Art und Weise, wie Windows Befehlszeilenargumente analysiert, finden Sie unter Parsing C ++ - Befehlszeilenargumente :

Der Microsoft C / C ++ - Startcode verwendet die folgenden Regeln, wenn Argumente in der Befehlszeile des Betriebssystems interpretiert werden:

  • Argumente werden durch Leerzeichen begrenzt, die entweder ein Leerzeichen oder eine Registerkarte sind.

  • Das Caret-Zeichen (^) wird nicht als Escape-Zeichen oder Trennzeichen erkannt. Das Zeichen wird vollständig vom Befehlszeilenparser im Betriebssystem verarbeitet, bevor es an das Array argv im Programm übergeben wird.

  • Eine Zeichenfolge, die von doppelten Anführungszeichen (" Zeichenfolge ") umgeben ist, wird unabhängig vom darin enthaltenen Leerraum als einzelnes Argument interpretiert. Eine Zeichenfolge in Anführungszeichen kann in ein Argument eingebettet werden.

  • Ein doppeltes Anführungszeichen mit vorangestelltem Backslash ("" "wird als wörtliches doppeltes Anführungszeichen (") interpretiert.

  • Backslashes werden wörtlich interpretiert, es sei denn, sie stehen unmittelbar vor einem doppelten Anführungszeichen.

  • Wenn auf eine gerade Anzahl von Backslashes ein doppeltes Anführungszeichen folgt, wird für jedes Paar von Backslashes ein Backslash in das Array argv eingefügt, und das doppelte Anführungszeichen wird als Zeichenfolgenbegrenzer interpretiert.

  • Wenn auf eine ungerade Anzahl von Backslashes ein doppeltes Anführungszeichen folgt, wird für jedes Paar von Backslashes ein Backslash in das Array argv eingefügt, und das doppelte Anführungszeichen wird durch den verbleibenden Backslash "maskiert", wodurch ein Literal verursacht wird doppeltes Anführungszeichen (") in argv .

Dies wird auch unter _exec, _wexec Funktionen erläutert :

In Zeichenfolgen eingebettete Leerzeichen können zu unerwartetem Verhalten führen. Wenn Sie beispielsweise _exec die Zeichenfolge "hi there" erhält der neue Prozess zwei Argumente: "hi" und "there" . Wenn der neue Prozess eine Datei mit dem Namen "hi there" öffnen soll, schlägt der Prozess fehl. Sie können dies vermeiden, indem Sie die Zeichenfolge in Anführungszeichen setzen: "\"hi there\"" .

Wie Python native ausführbare Dateien aufruft

Pythons subprocess.Popen kann das Escape-Verfahren korrekt verarbeiten, indem args in eine Zeichenfolge konvertiert wird, wie unter Konvertieren einer Argumentsequenz in eine Zeichenfolge unter Windows beschrieben . Die Implementierung ist subprocess.list2cmdline .

Ich hoffe, dies kann Aufschluss darüber geben, wie PowerShell dies auf elegantere Weise handhaben kann, anstatt --% oder doppeltes Escapezeichen zu verwenden (gemäß PowerShell- und CMD-Syntax). Die aktuellen Problemumgehungen stören wirklich Azure CLI-Kunden (die auf python.exe basieren).

Wir haben immer noch keine klare und präzise Methode, um zu verstehen, wie mit diesem Problem umgegangen werden soll. Kann jemand aus dem Powershell-Team Licht ins Dunkel bringen, wenn dies mit v7 vereinfacht wird?

Die gute Nachricht ist, dass einer der Schwerpunkte für PS 7.1 darin besteht, das Aufrufen nativer Befehle zu vereinfachen. Aus einem Blogbeitrag zu 7.1-Investitionen :

Die meisten nativen Befehle funktionieren in PowerShell einwandfrei. In einigen Fällen ist die Argumentanalyse jedoch nicht ideal (z. B. die ordnungsgemäße Behandlung von Anführungszeichen). Benutzer sollen Beispielbefehlszeilen für jedes gängige native Tool ausschneiden, in PowerShell einfügen und ohne PowerShell-spezifische Escape-Funktion arbeiten können.

Vielleicht wird dies (hoffentlich) in 7.1 behoben

Danke, @rkeithhill , aber nicht:

Benutzer sollen Beispielbefehlszeilen für jedes gängige native Tool ausschneiden, in PowerShell einfügen und ohne PowerShell-spezifische Escape-Funktion arbeiten können.

klingt wie eine andere Einstellung zu dem von Natur aus konzeptionell problematischen --% (Stop-Parsing-Symbol)?

@ SteveL-MSFT, können Sie uns mehr über diese bevorstehende Funktion erzählen?


Um es noch einmal zusammenzufassen: Die hier vorgeschlagene Korrektur besteht darin, dass Sie sich nur darauf konzentrieren müssen, die Syntaxanforderungen von PowerShell zu erfüllen, und dass PowerShell dafür sorgt, dass alle hinter den Szenen entweichen (unter Windows; unter Unix ist dies nicht mehr erforderlich, da .NET dies zulässt Wir müssen eine Reihe von wörtlichen Token an das Zielprogramm übergeben. Es besteht jedoch kein Zweifel, dass das Update aktiviert werden muss, wenn die Abwärtskompatibilität beibehalten werden muss.

Mit diesem Fix ist es weiterhin erforderlich, mit Befehlszeilen für andere Shells herumzuspielen, dies ist jedoch einfacher - und im Vergleich zu --% behalten Sie die volle Leistung der PowerShell-Syntax und der variablen Erweiterungen (Ausdrücke mit (...) , Weiterleitungen, ...):

  • Sie müssen \" durch `" ersetzen, aber _nur innerhalb von "..." _.

  • Sie müssen sich bewusst sein, dass _keine (andere) Shell_ beteiligt ist, damit Verweise auf Umgebungsvariablen im Bash-Stil wie $USER nicht funktionieren (sie werden als _PowerShell_-Variablen interpretiert), es sei denn, Sie ersetzen sie durch die äquivalente PowerShell-Syntax: $env:USER .

    • Nebenbei bemerkt: --% versucht dies zu kompensieren, indem - insbesondere _variabel_ - Verweise auf Umgebungsvariablen im Stil von cmd.exe -Stil wie %USERNAME% werden. Beachten Sie jedoch, dass dies nicht nur nicht der Fall ist. t Die Unterstützung von Referenzen im Bash-Stil ( $USER ) wird _verbatim_ an das Zielprogramm übergeben, erweitert aber auch unerwartet Referenzen im Stil von cmd.exe auf Unix-ähnlichen Plattformen und erkennt '...' -Zitat.
    • Unten finden Sie eine Alternative, bei der _does_ die jeweilige plattformeigene Shell betrifft.
  • Sie müssen sich bewusst sein, dass PowerShell über zusätzliche Metazeichen verfügt, für deren wörtliche Verwendung Anführungszeichen / Escapezeichen erforderlich sind. Dies sind (beachten Sie, dass @ nur als erstes Zeichen eines Arguments problematisch ist):

    • für POSIX-ähnliche Shells (z. B. Bash): @ { } ` (und $ , wenn Sie eine vorherige Erweiterung durch PowerShell verhindern möchten)
    • für cmd.exe : ( ) @ { } # `
    • Individuell ` - solche Zeichen entkommen. ist ausreichend (zB printf %s `@list.txt ).

Ein etwas erfundenes Beispiel:

Verwenden Sie die folgende Bash-Befehlszeile:

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

Mit dem vorgeschlagenen fix an Ort und Stelle, alles , was benötigt wird , ist die ersetzen \" Instanzen innerhalb des "..." -enclosed Arguments mit `" :

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

Das heißt, Sie müssen sich keine Gedanken über eingebettete " in '...' , und innerhalb von "..." Sie ihnen nur entkommen, um _PowerShell_ glücklich zu machen (was Sie auch tun könnten mit "" hier).

Die obige iep -Funktion implementiert diesen Fix (als Notlösung), sodass iep printf '"%s"\n' "3`" of snow" wie beabsichtigt funktioniert.


Vergleichen Sie dies mit dem aktuellen, fehlerhaften Verhalten, bei dem Sie durch die folgenden Rahmen springen müssen, damit der Befehl wie in Bash funktioniert (unerklärlicherweise ist eine zusätzliche Fluchtrunde mit \ erforderlich):

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

Mit dem Fix können diejenigen, die eine bestimmte Befehlszeile _as-is_ über die _default-Shell_ der Plattform verwenden möchten, einen wörtlichen _here-string_ verwenden, um an sh -c (oder bash -c ) / cmd /c ; z.B:

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

Beachten Sie, dass die Verwendung von --% hier nicht funktioniert ( printf --% '"%s"\n' "3\" of snow" ). Der zusätzliche Vorteil des hier auf Zeichenfolgen basierenden Ansatzes besteht darin, dass die verschiedenen Einschränkungen von --% nicht gelten , insbesondere die Unfähigkeit, eine Ausgabeumleitung zu verwenden ( > ).

Wenn Sie zu einem _double_-zitierten Here-String ( @"<newline>....<newline>"@ ) wechseln, können Sie im Gegensatz zu --% sogar _PowerShell-Variablen und -Ausdrücke_ einbetten. Sie müssen dann jedoch sicherstellen, dass die erweiterten Werte die Syntax der Zielshell nicht beeinträchtigen.

Wir können uns ein dediziertes Cmdlet mit einem prägnanten Alias ​​(z. B. Invoke-NativeShell / ins ) für solche Anrufe sh -c / cmd /c nicht benötigt wird angegeben werden), aber um komplexe Befehlszeilen unverändert zu übergeben, denke ich nicht daran, Here-Strings zu verwenden:

# 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

Wenn Sie sich auf Funktionen der plattformeigenen Shell verlassen, sind solche Aufrufe per Definition plattformspezifisch - sie funktionieren nicht auf Windows- und Unix-ähnlichen Plattformen.

Dies ist der Grund, warum es auf lange Sicht vorzuziehen ist, sich auf PowerShell _alone_ mit seiner _own-Syntax_ zu verlassen : Es bietet eine vorhersehbare plattformübergreifende Erfahrung beim Aufrufen externer Programme - auch wenn dies bedeutet, dass Sie keine Befehlszeilen verwenden können, die für andere Shells erstellt wurden ist; Mit zunehmender Beliebtheit von PowerShell erwarte ich, dass der Aufwand, die erforderlichen Änderungen zu entdecken und zu kennen, abnimmt, und ich erwarte, dass immer mehr Dokumentation auch die PowerShell-Versionen von Befehlszeilen anzeigt.

  • Sie müssen \" durch `" ersetzen, aber _nur innerhalb von "..." _.

Das funktioniert nicht überall.

# 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"")'

Bash-Syntax:

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

Powershell-Vorschlag:

Wenn das PowerShell-Team nur nach einem Symbol sucht, das die vorherige Syntax nicht verletzt, verwenden Sie einfach Backticks für ein Bash-ähnliches Verhalten, das Literalen automatisch entgeht und die Zeichenfolgeninterpolation ermöglicht. Dies ähnelt auch der JavaScript-Syntax.

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

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

Warum nicht einfach so etwas wie ein Backtick für ein Bash-ähnliches Verhalten verwenden?

Backticks werden bereits zum Entkommen von Inhalten verwendet, genau wie Backslahes in der POSIX-Shell. Der Kommentar, auf den Sie sich beziehen, weist bereits darauf hin. Wir haben alle ASCII-Anführungszeichen aufgebraucht. Das Hinzufügen eines $ -Präfixes zu normalen String-Literalen könnte funktionieren, aber ich denke nicht, dass es genug Sinn macht.


Die Art und Weise, wie Windows Befehlszeilenargumente analysiert, finden Sie unter ...

Das Problem ist, dass Windows MSVCR dies nicht nur tut: Es behandelt Eckfälle auf undokumentierte Weise . Das "" Zeug ist so solide festgelegt, dass sie es sogar in CoreFX einfügen, wenn sie .NET auf Unix portieren. Aber es ist immer gut genug, um zu entkommen, zumindest bis jemand nach Globbing fragt.

Es gibt auch das klassische Problem, dass jeder es anders macht, aber wir müssen uns darüber keine Sorgen machen, da wir immer .NET für rohe cmdline haben.

Warum nicht einfach so etwas wie ein Backtick für ein Bash-ähnliches Verhalten verwenden?

Backticks werden bereits zum Entkommen von Inhalten verwendet, genau wie Backslahes in der POSIX-Shell. Wir haben alle ASCII-Anführungszeichen aufgebraucht.

Es besteht die Möglichkeit, eine Kombination von Symbolen zu verwenden, wenn der Parser nicht erkennen kann, dass hier eine Zeichenfolge eingeführt wird. So etwas wie '' könnte sogar funktionieren.

@aminya hier ist eine Lösung, wenn Sie nicht alte Windows Powershell 5.1 und auf 6+ verwenden:
https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606

Da ich mich mit PowerShell 5.1 unter Windows und 6+ unter Linux / Mac befassen muss, habe ich meine eigene Implementierung, die seit Jahren ohne Probleme funktioniert und die es ermöglicht, mit Tools wie Kubectl, Helm, Terraform und anderen zu arbeiten, die komplexes JSON bestehen Objekte innerhalb von Parametern:
https://github.com/choovick/ps-invoke-externalcommand

GitHub
Tragen Sie zur Entwicklung von choovick / ps-invoke-externalcommand bei, indem Sie ein Konto auf GitHub erstellen.

@choovick etwas kürzere Implementierung wurde oben in diesem Thread angegeben , ich muss noch einen Fall finden, in dem es für mich fehlschlagen würde. Dies funktioniert in PS ab Version 3.

Die Run-Native -Funktion von @AndrewSav @TSlivede ist sehr clever und prägnant und funktioniert lobenswerterweise auch zuverlässig in _Windows PowerShell_; ein paar bemerkenswerte Dinge: Notwendigerweise sieht der Kinderprozess den Aux. commandlineargumentstring Umgebungsvariable (wahrscheinlich selten, wenn überhaupt, ein Problem in der Praxis), argumentlose Aufrufe werden derzeit nicht korrekt behandelt (einfach zu beheben und nicht einmal ein Problem, wenn Sie sicherstellen, dass Sie die Funktion immer nur verwenden _mit_ Argumenten, wofür es ist), _alle_ Argumente werden in doppelte Anführungszeichen gesetzt (z. B. wird so etwas wie 42 als "42" ), was unter Windows (leider) Nebenwirkungen haben kann Programme, die Argumente in doppelten Anführungszeichen (oder teilweise in doppelten Anführungszeichen, wie im Fall msiexec ) unterschiedlich interpretieren.

@aminya , der einzige Grund, warum node -e 'console.log(`"hey`")' (irgendwie) funktioniert, ist das aktuelle, kaputte Verhalten (siehe unten). Ich gehe davon aus, dass Sie _verbatim_ console.log("hey") übergeben wollten. Wenn PowerShell unter Windows den hier vorgeschlagenen Vorgaben korrekt entkommen würde, würden Sie in Anführungszeichen unverändert übergeben: node -e 'console.log("hey")' . Dies sollte _automatisch_ in node -e "console.log(\"hey\")" (doppeltes Zitat, \ -escaped wörtlich " ) _ hinter den Szenen.

Lassen Sie mich angesichts der Länge dieses Threads noch einmal zusammenfassen:
Sie sollten sich immer nur um die Syntaxanforderungen von PowerShell kümmern müssen, und es ist PowerShells Aufgabe als Shell, sicherzustellen, dass die _verbatim-Argumentwerte_, die sich aus PowerShells eigener Analyse ergeben, an das externe Programm _as-is_ übergeben werden.

  • Auf Unix-ähnlichen Plattformen ist dies jetzt, da wir .NET Core-Unterstützung haben, trivial, da die wörtlichen Werte so wie sie sind als Elemente eines _array_ von Argumenten übergeben werden können. Auf diese Weise erhalten Programme dort nativ Argumente .
  • Unter Windows erhalten externe Programme Argumente als einzelne Befehlszeilenzeichenfolge (eine bedauerliche historische Entwurfsentscheidung) und müssen ihre eigene Befehlszeilenanalyse durchführen. Das Übergeben mehrerer Argumente als Teil einer einzelnen Zeichenfolge erfordert Anführungszeichen und Analyseregeln, um Argumente richtig abzugrenzen. Während dies letztendlich für alle kostenlos ist (Programme können nach Belieben analysiert werden), implementieren die C / C ++ - Compiler von Microsoft die am weitesten verbreitete Syntaxkonvention (wie bereits erwähnt und auch in dem leider jetzt aufgegebenen RFC vorgeschlagen ). es macht also Sinn, damit zu gehen.

    • _Update_: Selbst unter Windows können wir die Eigenschaft ArgumentList für die Sammlung wörtlicher Token von System.Diagnostics.ProcessStartInfo in .NET Core nutzen: Unter Windows wird sie automatisch in einen korrekt zitierten und maskierten Befehl übersetzt -line string, wenn der Prozess gestartet wird; Für _batch-Dateien_ benötigen wir jedoch möglicherweise noch eine spezielle Behandlung - siehe https://github.com/PowerShell/PowerShell-RFC/pull/90#issuecomment -552231174

Die Implementierung des oben genannten ist zweifellos eine massive bahnbrechende Änderung, weshalb vermutlich ein Opt-In erforderlich ist.
Ich denke, dass dies ein Muss ist, wenn wir alle aktuellen Probleme mit Zitaten beseitigen möchten, die immer wieder auftreten und die Einführung von PowerShell behindern, insbesondere in der Unix-Welt.


Was node -e 'console.log(`"hey`")' : Verwenden Sie innerhalb von '...' nicht ` - es sei denn, Sie möchten, dass dieses Zeichen so wie es ist durchlaufen wird. Weil PowerShell derzeit den wörtlichen " -Zeichen nicht entgeht. In Ihrem Argument als \" hinter den Kulissen sieht node in der Befehlszeile console.log(`"hey`") , das als zwei direkt benachbarte String-Literale analysiert wird: nicht zitierte console.log(` und doppelt "hey`" . Nach dem Entfernen der " , die eine _syntaktische_ Funktion haben, weil sie nicht \ entkommen, ist der ausgeführte JavaScript-Code letztendlich console.log(`hey`) , und das funktioniert nur, weil ein `....` eingeschlossenes Token ist eine Form von String-Literal in JavaScript, nämlich ein _template-Literal_.

@ AndrewSav Ich habe mit meinem verrückten Testobjekt getestet und es hat bei mir funktioniert! Sehr elegante Lösung, getestet unter Windows 5.1 und PS 7 unter Linux. Ich bin damit einverstanden, dass alles doppelt zitiert wird. Ich beschäftige mich nicht mit msiexec oder sqlcmd auch bekannt ist, dass sie " explizit behandeln.

Meine persönliche Implementierung hat auch eine einfache Escape-Logik, ähnlich der von Ihnen erwähnten: https://github.com/choovick/ps-invoke-externalcommand/blob/master/ExternalCommand/ExternalCommand.psm1#L278

Aber ich habe eine Menge Code geschrieben, um STDOUT- und STDERR-Threads in Echtzeit innerhalb dieses Moduls anzuzeigen und zu erfassen ... Es kann wahrscheinlich stark vereinfacht werden, aber ich hatte keine Notwendigkeit ...

@ mklement0 Dieser Thread wird niemals enden (: Wir müssen entweder ein veröffentlichtes PowerShell-Modul in der ps-Galerie bereitstellen, das den meisten Anwendungsfällen entspricht und einfach zu verwenden ist, oder auf bevorstehende Shell-Verbesserungen warten.

GitHub
Tragen Sie zur Entwicklung von choovick / ps-invoke-externalcommand bei, indem Sie ein Konto auf GitHub erstellen.

@ mklement0 Dieser Thread wird niemals enden (: Wir müssen entweder ein veröffentlichtes PowerShell-Modul in der ps-Galerie bereitstellen, das den meisten Anwendungsfällen entspricht und einfach zu verwenden ist, oder auf bevorstehende Shell-Verbesserungen warten.

Wenn das PowerShell-Team beschließt, dies nicht im Programm selbst zu beheben, würde ich sagen, dass eine Shell, die externe Programme nicht nativ und korrekt ausführen kann, nicht die Shell meiner Wahl ist! Dies sind die grundlegenden Dinge, die in PowerShell fehlen.

@aminya ja, ich fand es, dass das Problem selbst sehr ärgerlich ist, als ich

  • Flexibles Parameter-Framework, einfach zu erstellende zuverlässige CMDlets.
  • Module und interne Modul-Repositorys, um die gemeinsame Logik im gesamten Unternehmen gemeinsam zu nutzen, wodurch viel Code-Duplizierung und Zentralisierung der Kernfunktionen vermieden werden, die an einem Ort überarbeitet werden können.
  • Plattformübergreifend. Ich habe Leute, die meine Tools unter Windows in PS 5.1 und unter Linux / Mac in PS 6/7 ausführen

Ich hoffe wirklich, dass das PS-Team dies in Zukunft verbessert, um es weniger kompliziert zu machen.

Die Implementierung des oben genannten ist zweifellos eine massive bahnbrechende Änderung, weshalb vermutlich ein Opt-In erforderlich ist.

Meinen Sie die Implementierung von https://github.com/PowerShell/PowerShell-RFC/pull/90?

@aminya , ich stimme zu, dass das Aufrufen externer Programme mit Argumenten ein

Ein Modul, wie von @choovick vorgeschlagen , ist für _Windows PowerShell_ immer noch sinnvoll, das sich im Wartungsmodus nur für sicherheitskritische Korrekturen befindet.
Wenn das vorgeschlagene konzeptionelle Hilfethema zum Aufrufen externer Programme geschrieben wird - siehe https://github.com/MicrosoftDocs/PowerShell-Docs/issues/5152 - eine Hilfsfunktion, die die Probleme in früheren Versionen / Windows PowerShell's behebt, wie @TSlivede oben, könnte dort direkt gepostet werden.

@iSazonov Ja, die Implementierung von https://github.com/PowerShell/PowerShell-RFC/pull/90 habe ich gemeint.

Was den nie endenden Faden und die bahnbrechenden Bedenken betrifft:

Die letzte offizielle Antwort auf den verknüpften RFC war dieser Kommentar von @joeyaiello vom 8. Juli 2019 (Hervorhebung hinzugefügt):

Wir sind jedoch der Meinung, dass dieser [RFC] unabhängig vom bestehenden Verhalten und ohne Rücksicht auf dessen Bruch eine Menge Sinn ergibt. Nachdem wir nun über experimentelle Funktionen verfügen, halten wir es für durchaus sinnvoll, diese heute hinter einem experimentellen Feature-Flag zu implementieren, und wir können später herausfinden, ob es sich um ein Opt-In- oder ein Opt-Out-Verhalten handelt und ob es einen Übergang gibt Pfad, und wenn eine Präferenzvariable der richtige Mechanismus zum Ein- und Ausschalten ist.

Persönlich würde es mir nichts ausmachen, das Verhalten standardmäßig zu korrigieren, obwohl es sich um eine bahnbrechende Änderung handelt. Der RFC schlägt dies tatsächlich vor und schlägt ein Opt-In vor, wenn Sie das _old_ (defekte) Verhalten wünschen.

Ich vermute jedoch, dass diejenigen mit zu wartendem Legacy-Code Einwände erheben werden, da alle vorhandenen Problemumgehungen nicht mehr funktionieren - siehe oben - und die Aufrechterhaltung der Abwärtskompatibilität immer noch das übergeordnete Ziel zu sein scheint.

Wenn das neue, feste Verhalten aktiviert wird, ist es immer noch unangenehm, etwas zu tun, um das richtige Verhalten zu erzielen, aber zumindest vorhandener Code wird nicht beschädigt.

Die bestehenden Opt-In-Mechanismen sind jedoch selbst problematisch:
Nachdem die optionalen RFC-Funktionen von @KirkMunro abgelehnt wurden, bleibt so ziemlich eine Präferenzvariable übrig, und die Herausforderung besteht darin, dass PowerShell dynamisch arbeitet: Code von Drittanbietern, der aus einem Bereich aufgerufen wird, der sich für die Verwendung des neuen Bereichs entschieden hat Die Implementierung könnte dann unterbrochen werden (es sei denn, die Präferenzvariable wird vorübergehend zurückgesetzt).

Hier ist ein _Lexical_ Scoping des Opt-Ins erforderlich, das wir derzeit nicht haben. Der RFC für das komplexe Scoping im strengen Modus schlägt die Implementierung eines Features mit lexikalischem Scoping (oder eines anderen Zwecks) über eine using -Anweisung vor (die insbesondere im Allgemeinen dynamisch ist). Nach diesem Muster ist eine lexikalisch festgelegte using ProperExternalArgumentQuoting -Anweisung (der Name ist ein WIP :) - wenn technisch machbar - eine Überlegung wert.

Wir brauchen das PowerShell-Komitee, um (erneut) abzuwägen und klare Anweisungen für den weiteren Weg zu geben, mit zeitnahem Feedback zu auftretenden Fragen. @ SteveL-MSFT?


Beachten Sie, dass eine --% -ähnliche Lösung, auf die im 7.1-Blogbeitrag hingewiesen wird (siehe oben ) (die meiner Meinung nach nicht wert ist, weiter verfolgt zu werden - siehe oben und dieser Kommentar ), eine separate Funktion ist, mit der PowerShells native Lösung repariert wird (Nicht-Emulations-) Verhalten ist immer noch ein Muss.

Persönlich würde es mir nichts ausmachen, das Verhalten standardmäßig zu korrigieren, obwohl es sich um eine bahnbrechende Änderung handelt. Der RFC schlägt dies tatsächlich vor und schlägt ein Opt-In vor, wenn Sie das alte (kaputte) Verhalten wünschen.

Wenn das neue, feste Verhalten aktiviert wird, ist es immer noch unangenehm, etwas zu tun, um das richtige Verhalten zu erzielen, aber zumindest vorhandener Code wird nicht beschädigt.

Einverstanden, in der Tat würde ich argumentieren, dass es weniger sinnvoll ist, einen defekten Standard zu haben, als einen korrekt implementierten Standard. Angesichts der Tatsache, dass die aktuelle Implementierung tatsächlich ein Fehler ist , muss das neue Verhalten opt-in und nicht opt-out sein, da es wirklich nicht sinnvoll ist, weiterhin unterbrochene externe Shell-Aufrufe zu fördern, die dazu neigen, unerwartet einzubrechen Wege. In jedem Fall sollte PowerShell 7 eine Verbesserung gegenüber der alten Windows PowerShell anstreben.

@ SteveL-MSFT und ich waren uns einig, dass wir diesen zugunsten von # 13068 schließen sollten. Alles, was wir hier berühren, ist einfach eine zu große Änderung, und wir sollten das Problem mit einem neuen Operator angehen, der als Opt-In-Modus dient.

Ich sehe absolut nicht ein, wie # 13068 dies lösen würde: Wenn dieser Operator wie beabsichtigt eingeführt wird, haben wir immer noch keine Möglichkeit, eine native ausführbare Datei mit einem bestimmten Array von Argumenten oder mit einigen expliziten Argumenten, deren Inhalt von Variablen stammt, ordnungsgemäß aufzurufen.

Das Beispiel, das @JustinGrote in diesem Thread angegeben hat, funktioniert derzeit nicht zuverlässig (wenn eingebettete Anführungszeichen im Argument payload möglich sind), und das Hinzufügen dieses Operators bietet keine Alternative, die etwas verbessert.

@joeyaiello Kannst du dieses Problem zumindest offen lassen, bis dieser Operator tatsächlich existiert und jemand zeigen kann, wie dieser Operator etwas verbessern würde, was in diesem Thread erwähnt wurde?

Oh und was ist mit Linux? Dieses Problem ist unter Windows dumm und unerwartet, aber unter Linux macht es noch viel weniger Sinn, zumal es keine lange Geschichte von Linux-Powershell-Skripten gibt, die kaputt gehen könnten.

Einen speziellen Operator dafür zu erstellen, ist für eine Befehlszeilen-Shell überhaupt nicht sinnvoll, da ihre Hauptaufgabe darin besteht, Programme zu starten und ihnen Argumente zu übergeben. Das Einführen eines neuen Operators, der system() für diesen Job ausführt, ist wie das Einführen einer Methode zum Aufrufen von calc.exe durch Matlab, da die Arithmetik einen Fehler aufweist. Was stattdessen getan werden sollte, ist Folgendes:

  • Das pwsh-Team bereitet sich auf eine neue Hauptversion vor, die das Kommandozeilenproblem behebt und das aktuelle Verhalten hinter ein integriertes Cmdlet verschiebt.
  • Als Stop-Gap-Lösung erhält die kommende pwsh-Version ein integriertes Cmdlet, das das neue, korrekte Verhalten für die Befehlszeilenübergabe verwendet.

Gleiches gilt für Start-Process . (Eigentlich ist es ein ziemlich guter Kandidat für das "neue" Cmdlet mit einigen Optionen wie -QuotingBehavior Legacy ...) Siehe # 13089.

Warum verhält sich Powershell in diesen beiden Situationen unterschiedlich? Insbesondere werden Argumente, die Leerzeichen enthalten, inkonsistent in doppelte Anführungszeichen gesetzt.

Ich erhalte konsistente Ergebnisse in Version 7. Scheint behoben.

PING 'A \"B'

Ping-Anfrage konnte Host A "B nicht finden.

PING 'A\" B'

Ping-Anfrage konnte Host A "B nicht finden.

Es ist nicht behoben, da die wörtlichen Hostnamen, die ping sollten, A \"B und A\" B - _mit_ den \ Zeichen sind.

PowerShell sollte als Shell die Argumente nur nach ihren Regeln analysieren und dann transparent sicherstellen, dass der Zielprozess dieselben wörtlichen Werte sieht, die das Ergebnis der eigenen Analyse von PowerShell waren.

Diese _ anderen_ Shells - und diese schlechten Programme, die unter Windows ausgeführt werden und sich sozusagen wie ihre eigene Shell verhalten müssen, indem sie eine Befehlszeile analysieren müssen, nur um die einzelnen übergebenen Argumente zu extrahieren - verwenden \ als Escape Zeichen sollten hier nicht ins Bild kommen - wenn dies berücksichtigt wird (nur unter Windows erforderlich, übergeben Sie unter Unix nur die wörtlichen Argumente direkt als Array), ist PowerShells Aufgabe als Shell, _behind the scene_.

Abgesehen davon erfordert PowerShell selbst nicht das Entkommen von " _inside '...' _ (Zeichenfolgen in einfachen Anführungszeichen), ebenso wenig wie POSIX-kompatible Shells wie bash : Ausgeführt von bash zum Beispiel, druckt /bin/echo 'A \"B' (vernünftigerweise) A \"B (die \ werden als Literale in einfachen Anführungszeichen behandelt) - das Ausführen der Der gleiche Befehl von PowerShell (unerwartet) ergibt
A "B - das \ fehlt - ist eine Manifestation der hier diskutierten Probleme.

Ich sollte klarstellen:

  • Unter dem Gesichtspunkt, dass Sie letztendlich A "B _verbatim_ übergeben möchten, sollten Sie in der Lage sein, 'A "B' von PowerShell zu verwenden.

  • Die Befehlszeile, die PowerShell derzeit hinter den Kulissen erstellt, enthält "A "B" - was der Zielprozess als A B ansieht - das blinde Gehäuse in "..." , ohne dem _embedded_ " zu entkommen " . Was PowerShell in diesem Fall in der Befehlszeile hinter den Kulissen verwenden sollte, ist "A \"B" - das heißt, das eingebettete " benötigt \ -Escaping.

  • In ähnlicher Weise bewirkt dasselbe blinde Gehäuse, dass 'A \"B' in der Befehlszeile hinter den Kulissen als "A \"B" wird, was zufällig dazu führt, dass _embedded_ \" in ein _escaped_ umgewandelt wird " Zeichen, das der Zielprozess daher als A "B ; Das heißt, das Fehlen einer automatischen Flucht führte zum effektiven Verlust der eingebetteten \ . Was PowerShell in diesem Fall in der Befehlszeile hinter den Kulissen verwenden sollte, ist "A \\\"B" - das heißt, sowohl die \ als auch die " müssen entkommen.

Es ist nicht behoben, da die wörtlichen Hostnamen, die ping sollten, A \"B und A\" B - _mit_ den \ Zeichen sind.

"Es" bezieht sich hier auf die zitierte Beschwerde, die ich glücklicherweise nicht reproduzieren kann.

@ yecril71pl , ich sehe: Meine (falsche) Annahme war, dass sich das "inkonsistente Umschließen von Argumenten, die Leerzeichen in doppelte Anführungszeichen enthalten" auf das _lack des automatischen Escapings von eingebetteten " und \ Zeichen bezieht in meinem vorherigen Kommentar erklärt - und das ist der Kern dieser Ausgabe.

In PowerShell Core wurden kleinere Korrekturen vorgenommen, die Windows PowerShell nicht hat. Ich kann mir momentan nur einen vorstellen:

  • Windows PowerShell verwendet blindes Anführungszeichen im Falle eines nachfolgenden \ : 'A B\' wird zu (kaputtem) "A B\" - PS Core behandelt dies korrekt ( "A B\\" ) .

Da dieses Repo nur für PS Core ist, reicht es aus, sich auf das zu konzentrieren, was in PS Core noch kaputt ist (es kann hilfreich sein, Unterschiede nebenbei zu erwähnen, aber es ist am besten, diesen Aspekt explizit zu machen).


Und selbst das Szenario, an das Sie gedacht haben, ist in PS Core immer noch kaputt - aber nur, wenn Sie die \ aus dem Argument_ weglassen:

Das Übergeben von 'A" B' immer noch zu einem nicht doppelt zitierten A" B hinter den Kulissen (während 'A\" B' zu "A\" B" - was, wie bereits erwähnt, ebenfalls gebrochen ist - nur anders).

Da dieses Repo nur für PS Core ist, reicht es aus, sich auf das zu konzentrieren, was in PS Core noch kaputt ist (es kann hilfreich sein, Unterschiede nebenbei zu erwähnen, aber es ist am besten, diesen Aspekt explizit zu machen).

Meta beiseite: Ich finde es nützlich zu wissen, dass das im Kommentar eines anderen Benutzers erwähnte falsche Verhalten nicht zutrifft. Natürlich hätten wir den Kommentar auch deshalb ablehnen können, weil der Benutzer sich nicht die Mühe gemacht hat, die aktuelle Version zu überprüfen. Gut. Da widerspenstige Reporter widerspenstig sind, ist es immer noch besser, sicher zu sein. MEINER BESCHEIDENEN MEINUNG NACH.

Es gibt dort kein Argument, aber es ist wichtig, solchen Seiten einen ordnungsgemäßen Rahmen und Kontext zu geben, insbesondere in einem langen Thread, in dem der ursprüngliche Kommentar - der für den Kontext benötigt wird - vor langer Zeit veröffentlicht wurde und tatsächlich standardmäßig versteckt ist (es tut nie weh) um tatsächlich auf den zitierten Originalkommentar zu verlinken).

Ich frage mich, ob diese Diskussionen einen Unterschied machen. Die Entscheidungen des Komitees sind eindeutig unabhängig von den Wünschen der Community. Schauen Sie sich die Tags an: Resolution- won't fix. Da PowerShell jedoch Open Source (MIT) ist, kann die Community dies in einem separaten Zweig beheben und diese PowerShellCommunity ( pwshc ) kurz nennen.

Dadurch entfällt die Notwendigkeit der Abwärtskompatibilität. Später in PowerShell 8 kann das Komitee die Gabel integrieren.

Informationen zur Abwärtskompatibilität: PowerShell ist auf keinem Betriebssystem vorinstalliert, und für mich ist die Schwierigkeit, PowerShellCommunity installieren, dieselbe wie PowerShell . Ich bevorzuge es, die Community-Version zu installieren und sofort zu verwenden, anstatt auf eine zukünftige 8-Version zu warten (oder den Code mit einem neuen Operator komplexer zu gestalten).

Da der Ausschuss beschlossen hat, die Dinge kaputt zu halten, ist es besser zu wissen, wie stark sie kaputt sind. Ich denke, die Community kann mit Invoke-Native , das das Richtige tut. Es ist nicht die Aufgabe der Community, das Gesicht von Microsoft gegen ihren Willen zu retten.

Es ist besser zu wissen, wie stark sie kaputt sind

Ich stimme voll und ganz zu - auch wenn das Problem im Moment nicht behoben werden kann, ist es wichtig zu wissen, wie man Dinge im Prinzip richtig macht, wenn und wann die Zeit kommt - auch wenn diese Zeit nie im Kontext einer bestimmten Sprache steht.

Die Community kann mit Invoke-Native , das das Richtige tut

Deutlich sein:

  • So etwas wie Invoke-NativeShell befasst sich mit einem anderen Anwendungsfall - der Gegenstand von # 13068 ist - und für diesen Anwendungsfall ist ein solches Cmdlet keine Notlösung: Es ist die richtige Lösung - siehe https://github.com/ PowerShell / PowerShell / Issues / 13068 # issuecomment -656781439

  • Bei diesem Problem geht es darum, PowerShell selbst zu reparieren (es geht nicht um plattformbezogene Funktionen), und die Lösung für dieses Problem besteht darin, eine einfache Zeremonie durchzuführen - daher der Vorschlag, die Funktion iep als Build zu versenden -in Funktion, bis ein _proper_ Fix in einer zukünftigen Version ansteht, die die Abwärtskompatibilität erheblich beeinträchtigen darf.

Es ist nicht die Aufgabe der Community, das Gesicht von Microsoft zu retten

Ich glaube nicht, dass es @aminya darum geht, korrigieren , der das

Ich bin mir jedoch nicht sicher, ob die Fragmentierung des PowerShell-Ökosystems mit einer Gabel der richtige Weg ist.

Ich habe gerade keine Zeit, auf all das zu antworten, aber ich denke, das ist vernünftig, also öffne ich wieder:

Können Sie dieses Problem zumindest offen lassen, bis dieser Operator tatsächlich existiert und jemand zeigen kann, wie dieser Operator etwas verbessern würde, was in diesem Thread erwähnt wurde?

Wie ich in einem verwandten Problem sagte, erhöht der native Anrufbetreiber die Komplexität in UX und es ist eine falsche Richtung.
Gleichzeitig geht es hier in der gesamten Diskussion um die Vereinfachung der Interaktion mit nativen Anwendungen.

Das einzige, was uns aufhält, ist, dass es eine bahnbrechende Veränderung ist. Wir haben oft gesagt, dass dies zu viel Zerstörung ist, aber lasst es uns abwägen.

Schauen wir uns eine interaktive Sitzung an. Ein Benutzer installiert eine neue PowerShell-Version und entdeckt beim Aufrufen nativer Anwendungen ein neues Verhalten. Was wird er sagen? Ich werde spekulieren, dass er sagen wird - "Danke - endlich kann ich einfach tippen und es funktioniert!".

Schauen wir uns das Skriptausführungs- / Hosting-Szenario an. Jede neue Version einer Anwendung (auch mit einer geringfügigen Änderung!) Kann einen Geschäftsprozess unterbrechen. Wir erwarten dies immer und überprüfen unsere Prozesse nach Aktualisierungen. Wenn wir ein Problem finden, haben wir mehrere Möglichkeiten:

  • Rollback des Updates
  • Bereiten Sie eine schnelle Lösung für das Skript vor
  • Deaktivieren Sie eine Funktion, die unser Skript unterbricht, bis diese Probleme behoben sind oder sie selbst im Laufe der Zeit absterben.

_Deit Versionsaktualisierungen immer etwas kaputt machen, ist die letzte Option die beste, die wir haben und akzeptieren konnten._

(Ich möchte darauf hinweisen, dass PowerShell Core derzeit keine Komponente von Windows ist und daher Hosting-Anwendungen nicht direkt verderben kann, sondern nur Skripte direkt betroffen sind.)

Ich möchte Sie daran erinnern, was Jason oben gesagt hat - dies ist eine Fehlerbehebung.
Lassen Sie es uns beheben und alles für alle vereinfachen. Lassen Sie Benutzer arbeiten powershell-y .

Wie ich in einem verwandten Problem sagte, fügt der native Anrufbetreiber die Komplizenschaft in UX hinzu und es ist eine falsche Richtung.

Komplexität ❓

Schauen wir uns eine interaktive Sitzung an. Ein Benutzer installiert eine neue PowerShell-Version und entdeckt beim Aufrufen nativer Anwendungen ein neues Verhalten. Was wird er sagen? Ich werde spekulieren, dass er sagen wird - "Danke - endlich kann ich einfach tippen und es funktioniert!".

Ich habe ein anderes Szenario: Neue Benutzer probieren PowerShell aus, stellen fest, dass es auf mysteriöse Weise fehlschlägt, verschieben es in den Papierkorb und kehren niemals zurück. Das würde ich tun.

Bereiten Sie eine schnelle Lösung für das Skript vor

Dies sollten wir tun, um die offensichtlichen Fälle in Benutzerskripten abzudecken.

erkennen, dass es auf mysteriöse Weise fehlschlägt

Die ganze Diskussion hier dreht sich nur darum, dieses "mysteriöse Scheitern" loszuwerden. Und dies geschieht am besten, indem Sie neue mysteriöse Dinge vereinfachen, aber nicht hinzufügen.

Bei der ganzen Diskussion hier geht es nur darum, dies loszuwerden. Und dies geschieht am besten, indem Sie neue mysteriöse Dinge vereinfachen, aber nicht hinzufügen.

Wir möchten nicht die Möglichkeit aufheben, ausführbare Programme direkt aufzurufen.

Der alte Aufruf ist auch nicht direkt an cmdline gerichtet, da erraten wird, ob bereits etwas zitiert ist. Außerdem würde diese Fähigkeit weiterhin mit -% beibehalten.

Außerdem würde diese Fähigkeit weiterhin mit -% beibehalten.

--% erfordert eine vordefinierte Befehlszeile, daher ist die Nützlichkeit eingeschränkt.

@ yecril71pl

Wir möchten nicht die Möglichkeit aufheben, ausführbare Programme direkt aufzurufen.

Ich denke, @iSazonov bedeutet, das fehlerhafte Verhalten loszuwerden, dh den Fehler ordnungsgemäß zu beheben (ohne sich über eine zusätzliche Syntax anzumelden), obwohl dies bedeutet, vorhandene

@ Artoria2e5 :

Der alte Aufruf ist auch nicht direkt an cmdline gerichtet, da erraten wird, ob bereits etwas zitiert ist

[_Update_: Ich habe die zitierte Zeile falsch gelesen, aber hoffentlich sind die Informationen immer noch von Interesse.]

  • Sie müssen nicht erraten, und die Regel ist einfach:

    • Nur wenn Ihr Befehlsname oder -pfad _quoted_ - '/foo bar/someutil 'ist und / oder _variable Referenzen (oder Ausdrücke) enthält - $HOME/someutil - & ist _required_.
  • Obwohl Sie dieses Bedürfnis für unglücklich halten, ist es das Herzstück der PowerShell-Sprache und erforderlich, um syntaktisch zwischen den beiden grundlegenden Analysemodi Argumentationsmodus und Ausdrucksmodus unterscheiden zu können.

    • Beachten Sie, dass das Problem nicht spezifisch für den Aufruf von _externen Programmen_ ist. Auch native PowerShell-Befehle erfordern & für den Aufruf, wenn sie über eine Zeichenfolge / Variablenreferenz in Anführungszeichen angegeben werden.
  • Wenn Sie sich diese einfache Regel nicht merken möchten, lautet die einfachere Regel: Verwenden Sie immer & , und es wird Ihnen gut gehen - es ist etwas weniger bequem, als _nicht_ etwas zu benötigen, aber nicht gerade eine Härte (und kürzer) als --% , was absolut die falsche Lösung ist - siehe unten)

Die besagte Fähigkeit würde weiterhin mit -% beibehalten.

[_Update_: Ich habe die zitierte Zeile falsch gelesen, aber hoffentlich sind die Informationen immer noch von Interesse.]

Nein, trotz der unglücklichen Verschmelzung von # 13068 mit diesem Problem ist --% - die Fähigkeit, die _native Shell_ unter Verwendung von _its_, ausnahmslos _plattformspezifischer_ Syntax - aufzurufen, in keiner Weise eine Problemumgehung für das vorliegende Problem und die Diskussion es als solches trägt nur zur Verwirrung bei .

--% ist ein ganz anderer Anwendungsfall und weist, wie derzeit vorgeschlagen, schwerwiegende Einschränkungen auf. Wenn es etwas gibt, für das es sich nicht lohnt, einen Operator einzuführen (oder das Verhalten eines vorhandenen zu ändern), ist es die Möglichkeit, eine wörtliche Befehlszeile an die native Shell zu übergeben (was Sie natürlich bereits mit sh -c '...' selbst tun können cmd /c '...' , _nur nur robust, wenn dieses Problem behoben wird_; eine binäre Invoke-NativeShell / ins Cmdlet-Implementierung, während hauptsächlich die Details der Ziel-Shell abstrahiert werden Die CLI-Syntax würde das vorliegende Problem vermeiden (durch direkte Verwendung von System.Diagnostics.ProcessStartInfo.ArgumentList ) und kann daher unabhängig implementiert werden.

@ yecril71pl

Ich habe ein anderes Szenario: Neue Benutzer probieren PowerShell aus, stellen fest, dass es auf mysteriöse Weise fehlschlägt, verschieben es in den Papierkorb und kehren niemals zurück. Das würde ich tun.

Könnten Sie klarstellen: Befürchten Sie, dass das aktuelle Verhalten von Powershell zu dieser unangenehmen Benutzererfahrung führen könnte, oder befürchten Sie, dass die vorgeschlagene Änderung zu dieser Benutzererfahrung führt?

Denn meiner Meinung nach ist es viel wahrscheinlicher, dass das derzeitige Verhalten von Powershell eine solche Erfahrung erzeugt. Wie oben gesagt: Das aktuelle Verhalten von Powershell sollte als Fehler betrachtet werden.

Warum sollte ein neuer Benutzer, ohne zu wissen, dass PowerShell beschädigt ist, Befehle mit verzerrten Argumenten ausgeben?

@ yecril71pl Nur um ganz sicher zu sein: Sie betrachten das derzeit erforderliche Formular für Argumente als "verzerrt", nicht das vorgeschlagene Update.

Ich betrachte Ihre Frage als aus Ihrer Vermutung stammend, dass ich ein unheilbarer Nerd bin. Das ist richtig - aber ich habe die Fähigkeit behalten, mir vorzustellen, was ein normaler zufälliger Kerl als verzerrt betrachten würde.

@ mklement0
Ich denke, dass @ Artoria2e5 über das Konvertieren des Arrays von Argumenten in die einzelne lpCommandLine Zeichenfolge sprach, wenn man sagte

Der alte Aufruf ist auch nicht direkt an cmdline gerichtet, da erraten wird, ob bereits etwas zitiert ist

Denn beim Anruf

echoargs.exe 'some"complicated_argument'

Sie müssen in der Tat mehr oder weniger raten, ob Powershell Anführungszeichen um some"complicated_argument hinzufügt.

Beispiel 1: In den meisten Powershell-Versionen
echoarg.exe 'a\" b' und echoarg.exe '"a\" b"' werden in übersetzt
"C:\path\to\echoarg.exe" "a\" b" (getestete Versionen 2.0; 4.0; 6.0.0-alpha.15; 7.0.1)
aber meine Standard-Powershell unter Win10 (Version 5.1.18362.752) wird übersetzt
echoarg.exe 'a\" b' bis "C:\path\to\echoarg.exe" a\" b und
echoarg.exe '"a\" b"' bis "C:\path\to\echoarg.exe" ""a\" b"" .

Beispiel 2: Ältere Powershell-Versionen werden übersetzt
echoarg.exe 'a"b c"' bis "C:\path\to\echoarg.exe" "a"b c"" (getestete Versionen 2.0; 4.0;)
während neuere Versionen übersetzen
echoarg.exe 'a"b c"' bis "C:\path\to\echoarg.exe" a"b c" (getestete Versionen 5.1.18362.752; 6.0.0-alpha.15; 7.0.1).


Da das Verhalten eindeutig bereits mehrfach geändert wurde, verstehe ich nicht, warum es nicht noch einmal geändert werden kann, um das erwartete Verhalten zu erhalten.

Ich sehe, @TSlivede , danke für die Klarstellung und entschuldige die Fehlinterpretation, @ Artoria2e5.

Was das eigentliche Problem betrifft: Wir sind uns zu 100% einig - in der Tat sollten Sie niemals darüber nachdenken müssen, was lpCommandLine hinter den Kulissen unter Windows verwendet wird. Wenn PowerShell das Richtige getan hätte, müsste dies niemand tun (mit Ausnahme von Randfällen, aber sie sind nicht die Schuld von PowerShell, und dann kann --% (wie derzeit implementiert) helfen Randfälle auf Unix-ähnlichen Plattformen sein).

Um das Problem einfach richtig zu beheben: Sie haben sicherlich meine Stimme (aber bestehende Problemumgehungen werden nicht funktionieren).

TL; DR: Die Annahme, dass wir einen beliebigen Wert als Argument zuverlässig an ein Programm im Microsoft Windows NT-Subsystem übergeben können, ist falsch , daher sollten wir aufhören, so zu tun, als sei dies unser Ziel. Es gibt jedoch noch viel zu retten, wenn wir das Ausmaß des Arguments berücksichtigen.

Beim Aufrufen nativer ausführbarer Windows-Dateien sollten wir das ursprüngliche Anführungszeichen beibehalten. Beispiel:

CMD /CSTART="WINDOW TITLE"

Das System kann die Datei WINDOW nicht finden.

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

StringConstantType
BareWord
Wert
/ CSTART = FENSTERTITEL
StaticType
System.String
Umfang
/ CSTART = "FENSTERTITEL"
Elternteil
CMD / CSTART = "FENSTERTITEL"

Wenn wir den Umfang als Vorlage nehmen würden, würden wir nichts verlieren und könnten die native ausführbare Datei wie erwartet aufrufen. Die Problemumgehung bei der Verwendung eines String-Arguments funktioniert hier, aber ich denke nicht, dass dies technisch unbedingt erforderlich ist, vorausgesetzt, die ordnungsgemäße Unterstützung wird in PowerShell implementiert. Dieser Ansatz würde in allen Fällen funktionieren.

Anführungszeichen in Anführungszeichen stellen ein unüberwindbares Problem dar, da es Tools gibt, die Backslash-Escape interpretieren ( TASKLIST "\\\"PROGRAM FILES" ) und Tools, die dies nicht tun ( DIR "\""PROGRAM FILES" /B ), und Tools, die nicht stören ( TITLE A " B ). Wenn wir jedoch entkommen, vergiftet das Standard-Escape mit Backslashes alle gängigen Tools zur Dateiverwaltung, da sie einfach überhaupt keine Anführungszeichen unterstützen und doppelte Backslashes \\ etwas ganz anderes bedeuten (versuchen Sie DIR "\\\"PROGRAM FILES" /B ), daher sollte das Senden eines Arguments mit einem Anführungszeichen ein Laufzeitfehler sein. Aber wir können keinen Fehler werfen, weil wir nicht wissen, welcher welcher ist. Während die Verwendung des normalen Escape-Mechanismus Argumente, die keine Anführungszeichen enthalten, keinen Schaden zufügen sollte, können wir nicht sicher sein, dass dies bei Anwendung auf Argumente, die diese enthalten und einem Tool zugeführt werden, das keine Anführungszeichen als Werte unterstützt, der Fall ist verursachen notwendigerweise eher einen Abbruchfehler als unerwartetes Verhalten, und unerwartetes Verhalten wäre in der Tat sehr schlecht. Dies ist eine ernsthafte Belastung für den Benutzer. Darüber hinaus werden wir niemals in der Lage sein, Tools bereitzustellen, die sich nicht darum kümmern ( CMD /CECHO='A " B' ).

Beachten Sie, dass Umgebungsvariablen keine Werte in CMD , sondern Codefragmente, die beim Erweitern von Umgebungsvariablen repariert werden, und es ist nicht vorgesehen, sie zuverlässig als Argumente für andere Befehle zu behandeln. CMD funktioniert einfach nicht mit Objekten jeglicher Art, nicht einmal mit Strings, was die Hauptursache für das gegenwärtige Rätsel zu sein scheint.

TL; DR: Die Annahme, dass wir einen beliebigen Wert als Argument zuverlässig an ein Programm im Microsoft Windows NT-Subsystem übergeben können, ist falsch, daher sollten wir aufhören, so zu tun, als sei dies unser Ziel.

Das sollte das Ziel sein, nicht wahr? Es ist kein Problem von PowerShell, wenn ein Programm empfangene Argumente nicht interpretieren kann.

Beim Aufrufen nativer ausführbarer Windows-Dateien sollten wir das ursprüngliche Anführungszeichen beibehalten. Beispiel:

CMD /CSTART="WINDOW TITLE"

Schlagen Sie vor, dass das Aufrufen eines Programms die Sprache von PowerShell dynamisch auf die vom aufgerufenen Programm verwendete Sprache ändern sollte? Sie haben dieses Beispiel in PowerShell geschrieben. Dies bedeutet, dass es einem der folgenden Beispiele entsprechen sollte

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

TL; DR: Die Annahme, dass wir einen beliebigen Wert als Argument zuverlässig an ein Programm im Microsoft Windows NT-Subsystem übergeben können, ist falsch, daher sollten wir aufhören, so zu tun, als sei dies unser Ziel.

Das sollte das Ziel sein, nicht wahr? Es ist kein Problem von PowerShell, wenn ein Programm empfangene Argumente nicht interpretieren kann.

Das Programm CMD kann das Argument /CECHO=A " B interpretieren, aber PowerShell kann es nicht übergeben, ohne es zu verzerren.

Beim Aufrufen nativer ausführbarer Windows-Dateien sollten wir das ursprüngliche Anführungszeichen beibehalten. Beispiel:

CMD /CSTART="WINDOW TITLE"

Schlagen Sie vor, dass das Aufrufen eines Programms die Sprache von PowerShell dynamisch auf die vom aufgerufenen Programm verwendete Sprache ändern sollte? Sie haben dieses Beispiel in PowerShell geschrieben. Dies bedeutet, dass es einem der folgenden Beispiele entsprechen sollte

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

Ich habe versucht vorzuschlagen, dass PowerShell bei der Verbindung mit externen Programmen unter dem Microsoft Windows NT-Subsystem unzählige Möglichkeiten hat, die Argumente zu codieren, die alle PowerShell, aber nicht dem empfangenden Programm entsprechen. Es ist nicht hilfreich, stumpf zu sein und den One True Way ™ zum Codieren von Argumenten zu erzwingen, ohne darauf zu achten, welche Zitieranordnung der Benutzer tatsächlich verwendet hat, um es milde auszudrücken.

@ yecril71pl Ich bin wirklich verwirrt von Ihren Kommentaren. Was genau schlagen Sie hier vor? Ihre Anwendungsfälle werden alle von --% abgedeckt. Sie haben es früher mit den Worten abgewiesen

--% erfordert eine vordefinierte Befehlszeile, daher ist deren Nutzen begrenzt.

Tatsächlich können Sie jedoch Umgebungsvariablen mit --% . Versuche dies:

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

Also, was vermisse ich?

Wir vermissen die Syntax CMD /CSTART="$mytitle" , ohne die Dinge auf ENV: .

Als schreckliche Idee haben wir die Möglichkeit, Environment.ExpandEnvironmentVariables durch etwas anderes zu ersetzen. Es gibt sowieso keine native Implementierung unter Unix, und ich glaube nicht, dass das, was es verarbeitet, beim Umschreiben in C # leistungskritisch werden würde.

Da Gleichheitszeichen in env var-Namen sowieso nicht zulässig sind, können wir %=$a% bedeuten $a . Dies würde nichts Bestehendes zerstören, während einige sehr flexible (und möglicherweise schlechte) Erweiterungen wie die Funktionsweise von JS-Vorlagenzeichenfolgen berücksichtigt würden. Zur Hölle, wir können %VARNAME=$var% als eine Art Fallback-Syntax definieren.

Was die Dokumentationshölle betrifft, würde dies dazu führen ... Ich entschuldige mich.

  • Wir haben kein Analyseproblem.

  • Was wir haben, ist ein Problem damit, wie PowerShell die wörtlichen, stringifizierten Argumente, die sich aus dem Parsen ergeben, an externe (native) ausführbare Dateien übergibt:

    • Unter Windows besteht das Problem darin, dass die Befehlszeile zum Aufrufen der externen ausführbaren Datei, die hinter den Kulissen erstellt wurde, nicht der am häufigsten verwendeten Konvention zum Zitieren von Argumenten entspricht , wie im Parsing C ++ - Befehl der Microsoft C / C ++ - Compilerdokumentation Zeilenargumenten .

    • Was derzeit passiert, ist nicht einmal, dass eine andere Konvention verwendet wird: Vermutlich aufgrund eines Versehens sind die Befehlszeilen, die konstruiert werden, abhängig von den Besonderheiten der Argumente, die sich auf eine Kombination aus eingebetteten doppelten Anführungszeichen und Leerzeichen beziehen, situativ _syntaktisch grundlegend gebrochen_ sowie Argumente für leere Zeichenfolgen.

    • Letztendlich ist das Problem die grundlegende Architektur der Prozesserstellung unter Windows: Sie müssen die Argumente, die an einen Prozess übergeben werden sollen, als Befehlszeile - eine einzelne Zeichenfolge, die alle Argumente darstellt - codieren, anstatt sie als Array von Argumenten zu übergeben (welche So machen es Unix-ähnliche Plattformen. Die Notwendigkeit, eine Befehlszeile zu übergeben, erfordert die Implementierung von Anführungszeichen und Escape-Regeln, und es liegt letztendlich an jedem Programm, wie die angegebene Befehlszeile zu interpretieren ist. Tatsächlich bedeutet dies, dass Programme unnötig gezwungen werden, eine Art Mini-Shell zu sein: Sie sind gezwungen, die Aufgabe, die die Shell bereits ausgeführt hat, erneut auszuführen. Dies ist eine Aufgabe, die nur in den Zuständigkeitsbereich einer Shell gehen sollte (wie sie ist) der Fall unter Unix), nämlich das Parsen einer Befehlszeile in einzelne Argumente. Kurz gesagt, dies ist die Anarchie, die als Argument für Windows gilt .

    • In der Praxis wird die Anarchie durch die meisten Programme gemildert, die der oben genannten Konvention entsprechen, und neue Programme, die entwickelt werden, halten sich höchstwahrscheinlich an diese Konvention, vor allem, weil weit verbreitete Laufzeiten, die Konsolenanwendungen zugrunde liegen, diese Konventionen implementieren (wie z. B. Microsoft C / C ++ / .NET-Laufzeiten). Die sinnvolle Lösung lautet daher:

      • Stellen Sie sicher, dass PowerShell diese Konvention einhält, wenn Sie die Befehlszeile hinter den Kulissen erstellen.
      • Für "Rogue" -Programme, die sich nicht an diese Konvention halten - die insbesondere cmd.exe , Batch-Dateien und Microsoft-Dienstprogramme wie msiexec.exe und msdeploy.exe - bieten Sie einen Mechanismus, um dies explizit zu tun Steuern Sie die Befehlszeile, die an die ausführbare Zieldatei übergeben wird. Dies ist, was --% , das Stop-Parsing-Symbol, bietet - wenn auch ziemlich umständlich .
    • Unter _Unix_ besteht das Problem darin, dass _ überhaupt eine Befehlszeile erstellt wird_ - stattdessen sollte das Array der wörtlichen Argumente _as-is_ übergeben werden , das .NET Core jetzt unterstützt (seit v2.1 über ProcessStartInfo.ArgumentList ; es sollte dies immer unterstützt haben, da es - vernünftigerweise - keine Befehlszeilen gibt, sondern nur Argumentarrays, wenn ein Prozess auf Unix-ähnlichen Plattformen erstellt wird.

    • Sobald wir ProcessStartInfo.ArgumentList , verschwinden alle Probleme unter Unix.

Das Beheben dieser Probleme ist das Ziel von @TSlivedes https://github.com/PowerShell/PowerShell-RFC/pull/90 .

In https://github.com/PowerShell/PowerShell-RFC/pull/90#issuecomment -650242411 habe ich vorgeschlagen, die "Schurkerei" von Batch-Dateien zusätzlich automatisch zu kompensieren, da sie immer noch als CLI-Einstiegspunkte für hohe Werte weit verbreitet sind -Profil-Software wie Azure (CLI az ist als Batch-Datei implementiert, az.cmd ).
In ähnlicher Weise sollten wir in Betracht ziehen, dasselbe für msiexec.exe und msdeploy.exe und möglicherweise andere hochkarätige "betrügerische" Microsoft-CLIs zu tun.


Ich habe gerade ein Modul veröffentlicht, Native ( Install-Module Native -Scope CurrentUser ), das alle oben genannten Punkte über seine ie -Funktion (kurz für i nvoke (external) e xecutable; it) adressiert ist eine vollständigere Implementierung der oben eingeführten Funktion iep ).

Es enthält auch ins ( Invoke-NativeShell ) , das sich mit # 13068 befasst, und dbea ( Debug-ExecutableArguments ) für die Diagnose der Übergabe von Argumenten - siehe https: // github. com / PowerShell / PowerShell / Issues / 13068 # issuecomment -671572939 für Details.

Mit anderen Worten: ie kann als unauffällige Lücke dienen, während wir darauf warten, dass dieses Problem behoben wird, indem Sie einfach den Aufrufen ie als Befehl voranstellen:

Anstatt von:

# 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" }'

Sie würden Folgendes verwenden:

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

Was das Beispiel CMD /CSTART="WINDOW TITLE" betrifft (dessen idiomatischere Form cmd /c start "WINDOW TITLE" , was bereits funktioniert):

Es ist im Wesentlichen das gleiche Problem wie bei prop="<value with spaces>" Argumenten für msiexec / msdeploy : PowerShell wandelt - zu Recht - /CSTART="WINDOW TITLE" in "/CSTART=WINDOW TITLE" . Dies unterbricht jedoch den Aufruf von cmd.exe .

Es gibt zwei Möglichkeiten, dies zu beheben:

  • Delegieren an ins / Invoke-NativeShell (beachten Sie, dass die Verwendung von cmd.exe /c effektiv impliziert ist):

    • ins 'START="WINDOW TITLE"'
    • Wenn Sie eine _expandable_-Zeichenfolge verwenden, können Sie PowerShell-Werte in die Befehlszeichenfolge einbetten.

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

  • Verwenden Sie alternativ die aktuelle Implementierung von --% , beachten Sie jedoch die Einschränkungen :

    • cmd --% /CSTART="WINDOW TITLE"
    • Wie bereits erwähnt, besteht eine problematische Einschränkung von --% darin, dass die einzige Möglichkeit zum Einbetten von _PowerShell_-Werten die Verwendung eines _aux ist. Umgebungsvariable_ und referenzieren Sie sie mit der Syntax %...% :

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

      • Um diese Einschränkung zu vermeiden, sollte --% immer mit einem _single_ string-Argument implementiert worden sein - z.

        cmd --% '/CSTART="WINDOW TITLE"' oder cmd --% "/CSTART=`"$title`"" - aber dies kann nicht geändert werden, ohne die Abwärtskompatibilität zu beeinträchtigen, sodass ein neues Symbol eingeführt werden müsste - ich persönlich sehe keine Notwendigkeit für eines.

  • Sie sind gezwungen, die Aufgabe, die die Shell bereits ausgeführt hat, erneut auszuführen

Ich glaube nicht, dass CMD.EXE Befehlszeilen in Argumente aufteilt. Das einzige, was benötigt wird, ist herauszufinden, welche ausführbare Datei aufgerufen werden soll, und der Rest ist nur die vom Benutzer geschriebene Befehlszeile (nach Ersetzungen von Umgebungsvariablen, welche werden ohne Rücksicht auf Argumentgrenzen durchgeführt). Interne Shell-Befehle sind hier natürlich eine Ausnahme.

Es ist im Wesentlichen das gleiche Problem wie bei prop="<value with spaces>" Argumenten für msiexec / msdeploy

Ich bin auch kein selbstbewusster Benutzer, daher habe ich es vorgezogen, etwas anzusprechen, mit dem ich besser vertraut bin.

Um es klar auszudrücken: Das Folgende hat keinen Einfluss auf die Punkte, die in meinem vorherigen Kommentar gemacht wurden.

Ich glaube nicht, dass CMD.EXE Befehlszeilen in Argumente aufteilt

  • Es kann ohne _explizite_ Aufteilung durchkommen, wenn _externe ausführbare Dateien_ aufgerufen werden (Befehle, die von einer anderen ausführbaren Datei in einem untergeordneten Prozess ausgeführt werden), muss dies jedoch für _batch-Dateien_ tun.

  • Selbst wenn externe ausführbare Dateien aufgerufen werden, müssen die Argumentgrenzen bekannt sein, um festzustellen, ob ein bestimmtes Metazeichen (z. B. & ) eine _syntaktische_ Funktion hat oder ob es Teil eines Arguments in doppelten Anführungszeichen ist und daher behandelt werden muss als wörtliches:

:: 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"

Außerdem erkennt cmd.exe _embedded_ " Zeichen. in "..." Zeichenfolgen erkannt, wenn sie als "" :

:: 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."

Leider unterstützt cmd.exe _nur_ (nur Windows) "" und nicht auch die am weitesten verbreiteten \" (was POSIX-ähnliche _shells_ unter Unix _exklusiv_ verwenden - Hinweis: _shells_, nicht _programs_, da Programme nur das Array wörtlicher Argumente sehen, die sich aus dem Parsen der Shell ergeben.

Während die meisten CLIs unter Windows _both_ "" und \" , verstehen einige _nur_ nur \" (insbesondere Perl und Ruby), und dann sind Sie in Schwierigkeiten:

:: !! 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.

Deshalb:

  • Vermeiden Sie es nach Möglichkeit, cmd.exe direkt anzurufen.

    • Rufen Sie externe ausführbare Dateien direkt (sobald dieses Problem behoben ist) oder über ie (vorerst) mit der Syntax _PowerShell_ auf.
  • Wenn Sie cmd.exe aufrufen müssen, verwenden Sie ins / Invoke-NativeShell um die allgemeine Einfachheit und insbesondere die Einbettung von PowerShell-Variablen- und Ausdruckswerten in die Befehlszeile zu vereinfachen .

    • Ein legitimer Grund, cmd.exe direkt aufzurufen, besteht darin, die mangelnde Unterstützung von PowerShell für Rohbyte-Daten in der Pipeline zu kompensieren. Ein Beispiel finden Sie in dieser SO-Antwort .

Ich weiß, dass ich hier viel Flak fangen werde, und ich schätze die Tiefe der Diskussion sehr, aber ... Enten ... hat jemand ein Beispiel dafür, dass dies in einem realen Szenario tatsächlich von Bedeutung ist? ?

Ich gehe davon aus, dass wir in PowerShell nicht befugt sind, die "Anarchie" zu lösen, die derzeit beim Parsen von Windows-Argumenten besteht. Und aus vielen der gleichen Gründe, aus denen wir das Problem nicht lösen können, gibt es einen guten Grund, warum Windows und die VC ++ - Compiler beschlossen haben, dieses Verhalten nicht zu unterbrechen. Es ist weit verbreitet, und wir werden nur dann einen wirklich langen Schwanz neuer (und weitgehend nicht entzifferbarer) Probleme schaffen, wenn wir Dinge ändern.

Bei Dienstprogrammen, die bereits plattformübergreifend sind und zwischen Windows und Linux häufig verwendet werden (z. B. Docker, k8s, Git usw.), tritt dieses Problem in der realen Welt nicht auf.

Und für diejenigen "betrügerischen" Anwendungen, die schlechte Arbeit leisten: Es handelt sich größtenteils um ältere Windows-Dienstprogramme.

Ich bin damit einverstanden, dass das, was Sie @ mklement0 beschrieben

Ziemlich einfache Verwendungszwecke brechen:

❯ 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 ist eine gut erzogene Anwendung, daher ist die Korrektur in PowerShell unkompliziert, obwohl alle Skripter da draußen ihre intelligenten Workarounds zurücksetzen müssen. cmd.exe ist schwieriger anzupassen und erfordert einen viel rücksichtsvolleren Ansatz, der einige Probleme lösen kann, aber wahrscheinlich nicht alle. Was eigentlich entsetzlich ist, wenn man bedenkt, dass PowerShell als Windows NT-Tool gestartet wurde. Ich verstehe diese Frage als _wenn es ein reales Szenario gibt, in dem ein schlecht benommenes Legacy-Dienstprogramm wie cmd.exe von PowerShell auf eine Weise aufgerufen wird, die Probleme in der Schnittstelle verursacht_. PowerShell hat versucht, dieses Problem zu lösen, indem die meisten Funktionen in cmd.exe dupliziert wurden, um cmd.exe überflüssig zu machen. Dies ist auch für andere Tools möglich, zum Beispiel kann MSI über ActiveX betrieben werden, obwohl dies beträchtliche Kenntnisse erfordert. Gibt es also etwas Wesentliches, das nicht abgedeckt ist?

@ PowerShell / Powershell-Komitee hat dies diskutiert. Wir schätzen das Git-Beispiel, das ein überzeugendes Beispiel aus der realen Welt zeigt. Wir waren uns einig, dass wir zu Beginn von 7.2 eine experimentelle Funktion haben sollten, um die Auswirkungen einer solchen Änderung zu validieren. Ein zusätzliches Testbeispiel zeigt, dass sogar --% ein Problem hat, obwohl es nicht analysiert werden sollte:

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'>

Dies scheint ein Problem im nativen Befehlsparameter-Ordner zu sein.

Ja, danke @cspotcode. Dieses Beispiel war definitiv ein Aha-Moment für mich (besonders wenn man bedenkt, dass ich dieses tatsächlich in der realen Welt getroffen habe).

Ich bin immer noch besorgt über den Aspekt des bahnbrechenden Wandels, und ich gehe davon aus, dass dies ein Kandidat für eine experimentelle Funktion ist, die über mehrere Versionen von PowerShell hinweg experimentell bleiben kann, und wir sind uns sicher, dass dies letztendlich nicht gelingen wird.

Ich muss mich auch eingehender mit dem Aspekt der @ mklement0 , solche Liste zu führen.

@joeyaiello und @ SteveL-MSFT, lassen Sie mich zuerst eine Meta-Beobachtung machen:

Während es gut , dass @cspotcode zu sehen ‚s Beispiel haben Ihnen einen _glimpse_ des Problems, Ihre Antworten noch einen grundlegenden Mangel an Verständnis und Wertschätzung für die (Größe des) zugrunde liegenden Problems verraten (werde ich diesen Punkt in einem späteren Kommentar argumentieren) .

Dies ist kein persönliches Urteil: Ich erkenne voll und ganz, wie schwierig es sein muss, sehr dünn zu sein und in kurzer Zeit Entscheidungen über ein sehr breites Spektrum von Themen treffen zu müssen.

Dies deutet jedoch auf ein strukturelles Problem hin: Mir scheint, dass Entscheidungen routinemäßig vom @ PowerShell / Powershell-Komitee auf der Grundlage eines oberflächlichen Verständnisses der diskutierten Probleme zum Nachteil der gesamten Gemeinschaft getroffen werden.

Für mich ist die Antwort des Ausschusses auf das hier diskutierte Thema das bislang folgenreichste Beispiel für dieses strukturelle Problem.

Deshalb bitte ich Sie, Folgendes zu berücksichtigen:

Wie wäre es mit der Ernennung von fachspezifischen Unterausschüssen, die der Ausschuss konsultiert, um das erforderliche Verständnis für die damit verbundenen Probleme zu haben?

Kannst du den Inhalt von testexe SteveL-MSFT teilen, willst du nur sicher gehen!

@TSlivede fasste das Problem treffend in https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -665125375 zusammen:

PowerShell hingegen behauptet, eine Shell zu sein (bis # 1995 gelöst ist, werde ich nicht sagen, dass es eine Shell ist)

Wie bereits mehrfach erwähnt, besteht ein Kernauftrag einer Shell darin, externe ausführbare Dateien mit Argumenten aufzurufen.

PowerShell erfüllt dieses Mandat derzeit nicht, da Argumente mit eingebetteten doppelten Anführungszeichen und Argumenten mit leeren Zeichenfolgen nicht korrekt übergeben werden.

Wie bereits erwähnt, war dies in den Windows-Tagen, in denen das Fehlen leistungsfähiger externer CLIs selten auftrat, möglicherweise weniger problematisch, aber diese Zeiten sind vorbei, und wenn PowerShell sich als glaubwürdige plattformübergreifende Plattform etablieren möchte Shell muss es dieses Problem beheben.

Das git Beispiel von @cspotcode ist gut; Jede ausführbare Datei, an die Sie eine JSON-Zeichenfolge übergeben möchten, z. B. curl ist eine andere:

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

Abgesehen von der Abwärtskompatibilität:

  • Unter Unix wird das Problem trivial und vollständig gelöst, indem ProcessStartInfo.ArgumentList hinter den Kulissen verwendet wird.

  • Unter Windows wird das Problem trivial und _mostly_ gelöst, indem ProcessStartInfo.ArgumentList hinter den Kulissen verwendet wird.

    • Für Randfälle ("Rogue" -ClIs) gibt es die ( schlecht implementierten ) --%
    • Aus Höflichkeit können wir bestimmte bekannte Randfälle kompensieren, um den Bedarf an --% zu verringern - siehe unten.

Daher muss so bald wie möglich eine der folgenden Entscheidungen getroffen werden:

  • Erkennen Sie, wie wichtig es ist, dass die Argumentübergabe ordnungsgemäß funktioniert, und beheben Sie sie auf Kosten der Abwärtskompatibilität.

  • Wenn die Abwärtskompatibilität wirklich von größter Bedeutung ist, stellen Sie einen neuen Operator oder eine Funktion wie die ie -Funktion aus dem Native -Modul bereit, mit der das Problem behoben wird, und machen Sie sie als einzige zuverlässige Methode zum Aufrufen von extern bekannt ausführbare Dateien.

Es ist völlig unzureichend, ein experimentelles Merkmal vorzuschlagen, um ein stark gebrochenes grundlegendes Merkmal anzugehen.


@ SteveL-MSFT

Selbst wenn man die Verwendung von --% als Lösung für dieses Problem in Betracht zieht, ist dies grundsätzlich falsch:

Es ist eine _Windows-only_ -Funktion, die nur "..." Anführungszeichen und %...% -Stil-Umgebungsvariablenreferenzen kennt.

Unter Unix gilt das Konzept "Parsing stoppen" grundsätzlich nicht: Es gibt keine Befehlszeile, die an untergeordnete Prozesse übergeben werden kann, sondern nur Arrays von Argumenten.

Daher muss jemand die Befehlszeile in Argumente vor dem Aufruf analysieren, die implizit an die Klasse ProcessStartInfo delegiert werden, und zwar über die Eigenschaft .Arguments , die unter Unix die _Windows_-Konventionen zum Parsen einer Befehlszeile verwendet - und erkennt daher nur "..." Zitate (mit dem Entkommen von eingebetteten " als "" oder \" ).

--% ist eine reine Windows-Funktion, deren einziger legitimer Zweck darin besteht, "Rogue" -ClIs aufzurufen.


@joeyaiello

dass Windows und die VC ++ - Compiler beschlossen haben, dieses Verhalten nicht zu unterbrechen.

Der VC ++ - Compiler legt eine vernünftige, weithin beobachtete Konvention fest , um Ordnung in die Anarchie zu bringen.

Es ist genau die Einhaltung dieser Konvention, die hier befürwortet wird und die uns automatisch die Verwendung von ProcessStartInfo.ArgumentList geben würde.

_Dies allein wird die überwiegende Mehrheit der Anrufe abdecken. Das Abdecken ALLER Anrufe ist unmöglich und in der Tat nicht in der Verantwortung von PowerShell.

Wie bereits erwähnt, muss für "Rogue" -ClIs, für die nicht konventionelle Anführungszeichen erforderlich sind, --% verwendet werden (oder ins / Invoke-NativeShell aus dem Modul Native ).

Aus Höflichkeit können wir bekannte "Rogue" -Szenarien, nämlich das Aufrufen von Batch-Dateien und bestimmten hochkarätigen Microsoft-CLIs, automatisch kompensieren:

  • Der Batch-File-Fall ist ein allgemeiner Fall, der leicht zu erklären und zu konzipieren ist (z. B. a&b als "a&b" , obwohl kein Anführungszeichen erforderlich ist). Dadurch wird die Verwendung vermieden von --% mit allen CLIs, die Batch-Dateien als Einstiegspunkt verwenden (was durchaus üblich ist), wie z. B. Azures az.cmd

  • Die Alternative zu hartcodierten Ausnahmen für bestimmte CLIs - was zugegebenermaßen verwirrend sein kann - besteht darin, das folgende Muster in den Argumenten zu erkennen, die sich aus der Analyse von PowerShell ergeben - <word>=<value with spaces> - und anstatt "<word>=<value with spaces>" , wie es derzeit passiert, <word>="<value with spaces>" ; Letzteres erfüllt die "Rogue" -ClIs und wird auch von konventionskonformen CLIs akzeptiert. Beispiel: echoArgs "foo=bar baz" sieht letztendlich das gleiche erste Argument wie echoArgs --% foo="bar baz"

@musm Den Quellcode von TestExe unter https://github.com/PowerShell/PowerShell/blob/master/test/tools/TestExe/TestExe.cs.

GitHub
PowerShell für jedes System! Tragen Sie zur PowerShell / PowerShell-Entwicklung bei, indem Sie ein Konto auf GitHub erstellen.

Ich denke, dass die standardmäßige Berücksichtigung von Ausnahmen nur zu einer ähnlichen Situation führen wird wie die aktuelle, in der die Leute die "Hilfsbereitschaft" von PowerShell zurücksetzen müssen. Wenn es Ausnahmen gibt, sollte es offensichtlich sein, dass sie angewendet werden.

Vielleicht so etwas wie:

# 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'

Ich kämpfe wirklich darum, eine Syntax dafür zu finden, die nicht kaputt ist. Zumindest macht diese Art von Sinn, wenn Sie es als Casting des Programms betrachten, aber das Casting der tatsächlichen Variablen, die das Programm enthält, würde das Einschließen von Klammern erfordern.

Dies sollte nicht nur dazu führen, dass Personen Ausnahmen für das von ihnen gewünschte Verhalten hinzufügen, sondern hoffentlich auch die Notwendigkeit von --% beseitigen. Die von ihnen registrierte Ausnahmeklasse verfügt dann über eine Methode zum Bestimmen, ob sie auf das Programm anwendbar ist (für den intelligenten Aufruf), und über eine Escape-Methode, bei der Sie einfach den abstrakten Syntaxbaum des Befehls darauf werfen und das Argument array / string zurückgeben.

  • anstatt "<word>=<value with spaces>" , wie es derzeit geschieht, um <word>="<value with spaces>"

Die zum Erstellen der Befehlszeile verwendeten Anführungszeichen sollten der Anführungszeichen des Aufrufs in PowerShell entsprechen. Deshalb:

  1. Ein Aufruf, dessen Text keine doppelten Anführungszeichen enthält, sollte den gesamten Wert in doppelte Anführungszeichen setzen.
  2. Ein Aufruf, der doppelte Anführungszeichen enthält, sollte diese beibehalten, wie im Skript _wenn möglich_ geschrieben.

Speziell:
| Skriptargument | Befehlszeile |
| ----------------- | ---------------- |
| p=l of v | "p = l von v" |
| p=l` of` v | "p = l von v" |
| p="l of v" | p = "l von v" |
| p="l of v"'a s m' | p = "l von v" a "sm" |
| p="l of v"' s m' | p = "l von vsm" |

Die letzte Zeile zeigt ein Beispiel, in dem die ursprünglichen doppelten Anführungszeichen nicht beibehalten werden können.

Ich weiß, dass ich hier viel Flak fangen werde, und ich schätze die Tiefe der Diskussion sehr, aber ..._ ducks _... hat jemand ein Beispiel dafür, dass dies in einem realen Szenario tatsächlich von Bedeutung ist ?

Ich habe es bereits vor einem Jahr in diesem Thread erwähnt (es ist jetzt versteckt ...), dass ich bei der Verwendung von ripgrep in Powershell viele WFT-Momente hatte. Ich konnte nicht verstehen, warum ich nicht nach Anführungszeichen suchen konnte. Es ignorierte meine Zitate:

rg '"quoted"'

und in git bash tat es nicht.

Jetzt bekomme ich weniger WTF-Momente, weil ich leider dieses lange Github-Problem gefunden habe und festgestellt habe, dass die Übergabe von " an Powershel völlig kaputt ist. Das aktuelle Beispiel "git.exe" ist ebenfalls großartig.

Um ehrlich zu sein, wage ich es jetzt nicht einmal, Powershell zum Aufrufen des nativen Befehls zu verwenden, wenn ich weiß, dass ich möglicherweise " in einem String als Parameter übergebe. Ich weiß, dass ich möglicherweise ein falsches Ergebnis oder einen falschen Fehler bekomme.

Wirklich, @ mklement0 hat es großartig zusammengefasst (dies sollte irgendwo in Stein gemeißelt sein)

Wie bereits mehrfach erwähnt, besteht ein Kernauftrag einer Shell darin, externe ausführbare Dateien mit Argumenten aufzurufen .
PowerShell erfüllt dieses Mandat derzeit nicht, da Argumente mit eingebetteten doppelten Anführungszeichen und Argumenten mit leeren Zeichenfolgen nicht korrekt übergeben werden.
Wie bereits erwähnt, war dies in den Tagen nur unter Windows möglicherweise weniger problematisch, als das Fehlen leistungsfähiger externer CLIs dieses Problem nur selten aufzeigte, aber heutzutage vorbei sind und PowerShell sich als glaubwürdige plattformübergreifende Plattform etablieren möchte Shell muss es dieses Problem beheben .

Und um Veränderungen zu brechen.
Kürzlich hat mir ein Mitarbeiter geschrieben, dass mein Skript auf seinem Computer nicht funktioniert hat. Ich habe es nur auf Powershel Core ausgeführt und er hat es auf Windows Powershell ausgeführt. Es stellt sich heraus, dass die Datei Out-File -Encoding utf8 mit "Stückliste" unter Windows Powershell und ohne Stückliste unter Powershel Core codiert ist. Dies ist ein irgendwie unerreichtes Beispiel, aber zeigen Sie, dass es in Powershel bereits subtile bahnbrechende Änderungen gibt, und das ist gut so, weil wir Macken und intuitives Verhalten aus einer Sprache entfernen, die dafür berühmt ist. Es wäre großartig, wenn das Powershel-Team etwas nachsichtiger wäre, wenn es darum geht, Änderungen zu brechen, da wir plattformübergreifendes Powershell haben, das außerhalb von Windows ausgeliefert wird, und wissen, dass Windows Powershell sich im Wartungsmodus befindet und für immer verwendbar sein wird, wenn Sie möchten wirklich, dass ein altes Skript ausgeführt wird, das eine neuere Version von Powershell enthält.

RE: dieser letzte Punkt, an dem Änderungen gebrochen wurden - da stimme ich voll und ganz zu. Es gibt viele bahnbrechende Änderungen, die wir aus verschiedenen Gründen toleriert haben. Es scheint jedoch immer häufiger der Fall zu sein, dass einige bahnbrechende Änderungen aus Gründen der Präferenz einfach verpönt werden und nicht die richtige Schwere der Berücksichtigung ihres tatsächlichen Werts berücksichtigt werden.

Es gibt einige Änderungen wie diese, die die allgemeine Shell-Erfahrung für jeden, der außerhalb von PowerShell greifen muss, um Dinge zu erledigen, massiv verbessern würden, was die ganze Zeit passiert. Es wurde immer wieder vereinbart, dass das derzeitige Verhalten unhaltbar ist und bereits für alles andere als die einfachsten Verwendungen weitgehend gebrochen ist. Und dennoch sehen wir uns dieser Zurückhaltung gegenüber, Änderungen zu brechen, auch wenn es Dutzende bereits akzeptierter Änderungen gibt, von denen einige ähnlich große Auswirkungen haben.

Wenn Sie nach Beispielen fragen, nehmen Sie sich eine Minute Zeit, um Stack Overflow einmal zu besuchen. Ich bin mir sicher, dass @ mklement0 eine keine Entschuldigung , keine hilfreichen Änderungen vorzunehmen.

Immer wenn das MSFT-Team dasselbe wiederholt, können wir sicher sein, dass es mehr weiß, als es öffentlich sagen kann. Wir sollten ihre innere Disziplin respektieren und sie nicht unter Druck setzen. Vielleicht können wir einen Kompromiss finden. Ich hoffe, ich habe heute Zeit, einen alternativen Weg mit fauler Migration zu beschreiben.

Ich erkenne das, und deshalb stelle ich es selten in Frage.

Dies ist jedoch ein Open Source-Projekt. Wenn es keinen Einblick in diese Entscheidungen gibt, werden die Leute unweigerlich frustriert sein. Keine Schuld auf beiden Seiten dieser Medaille, das ist nur die Realität der Situation hier, IMO. Ja, ein Migrationspfad kann diesen Schmerz etwas lindern, aber wir brauchen klare Richtlinien, die definieren, wie das funktionieren muss, damit die Dinge für so viele Leute wie möglich funktionieren. Kompromisse sind jedoch schwer zu erreichen, wenn Informationen fehlen.

Ich freue mich darauf zu sehen, was Sie im Ärmel haben. 😉

@ mklement0 du hast absolut recht und so sehr, dass ich jetzt nur auf deinen meta-point antworten kann. Leider ist es in Fällen, in denen wir nicht in der Lage sind, die für die Beantwortung einer solchen Frage erforderliche Tiefe zu erreichen, oft sicherer, die Änderung zu verschieben oder abzulehnen, bis wir mehr Zeit dafür haben

Ich möchte jedoch noch einen weiteren Meta-Punkt zum Brechen von Änderungen ansprechen: Unsere Telemetrie impliziert, dass die meisten PowerShell 7-Benutzer ihre eigenen Versionen nicht verwalten. Sie führen automatisierte Skripte in einer verwalteten Umgebung aus, die komfortabel ist, z. B. ein Upgrade ihrer Benutzer von 6.2 auf 7.0 (siehe den 2-Tage-Sprung von 6.2-Benutzern zu 7.0-Benutzern ab 8/3; dies ist jedoch nicht unser einziger Datenpunkt hier, aber es ist gerade eine bequeme, die den Punkt macht). Für diese Benutzer ist eine grundlegende Änderung, die ein perfekt funktionierendes Skript in ein nicht funktionierendes Skript verwandelt, nicht akzeptabel.

Ich schulde der Community auch einen Blog darüber, wie ich über die Auswirkungen von Änderungen nachdenke: nämlich die Prävalenz der vorhandenen Nutzung und den Schweregrad der Unterbrechung gegen die einfache Identifizierung und Korrektur der Unterbrechung abzuwägen. Dieser ist in vorhandenen Skripten äußerst verbreitet, verwirrend zu identifizieren und zu beheben, und das Bruchverhalten reicht von totalem Erfolg bis zu totalem Misserfolg, daher meine extreme Zurückhaltung, hier etwas zu tun.

Ich denke, es ist fair zu sagen, dass wir hier in 7.1 nichts unternehmen werden, aber ich bin definitiv offen dafür, dies zu einer Untersuchungspriorität für 7.2 zu machen (dh wir verbringen mehr als nur unsere Ausschusszeit damit, dies zu diskutieren.)

Wie wäre es mit der Ernennung von fachspezifischen Unterausschüssen, die der Ausschuss konsultiert und die das erforderliche Verständnis für die damit verbundenen Fragen haben?

Wir arbeiten daran. Ich weiß, dass ich das schon einmal gesagt habe, aber wir stehen uns sehr nahe (wie in, ich bastle den Blog und du wirst wahrscheinlich bald einige neue Labels sehen, mit denen wir spielen werden).

Ich schätze die Geduld aller und ich erkenne, dass es ärgerlich ist, alle paar Wochen eine markige Antwort vom Ausschuss zu erhalten, wenn die Leute eine immense Menge an Gedanken und Überlegungen in die Diskussion einfließen lassen. Ich weiß, es sieht so aus, als würden wir nicht tief über die Dinge nachdenken, aber ich denke, wir drücken die Tiefe unserer Diskussionen nicht so detailliert aus wie die Leute hier. In meinem eigenen Rückstand habe ich eine ganze Reihe von Blog-Themen wie die bahnbrechende Änderung darüber, wie ich Entscheidungen innerhalb des Ausschusses treffen möchte, aber ich habe nie die Gelegenheit bekommen, mich hinzusetzen und sie herauszupumpen. Aber ich kann hier sehen, dass die Leute vielleicht viel Wert darin finden würden.

Ich hoffe, ich habe die Schienen in dieser Diskussion nicht zu weit entfernt. Ich möchte nicht, dass dieses Problem zu einem Meta-Problem über das Projektmanagement wird, aber ich wollte einige der verständlichen Frustrationen ansprechen, die ich hier sehe. Ich bitte jeden, der mit uns ausführlicher darüber sprechen möchte, nächste Woche am Community Call teilzunehmen (fügen Sie hier Ihre Fragen und Gedanken hinzu

Nur eine kurze Anmerkung zum Meta-Punkt: Ich schätze die nachdenkliche Antwort, @joeyaiello.

Was die Schwere der Änderung betrifft: Die folgenden Aussagen scheinen uneins zu sein:

Hat jemand ein Beispiel dafür, dass dies in einem realen Szenario tatsächlich von Bedeutung ist?

vs.

Dieser ist in vorhandenen Skripten äußerst verbreitet und verwirrend zu identifizieren und zu beheben

Wenn dies bereits weit verbreitet ist, sind die Unbeholfenheit und Unklarheit der erforderlichen Problemumgehungen umso mehr ein Grund, dies endgültig zu beheben, insbesondere angesichts der Tatsache, dass mit einer Zunahme der Fallzahlen zu rechnen ist.

Mir ist klar, dass alle vorhandenen Problemumgehungen nicht mehr funktionieren.

Wenn dies von größter Bedeutung ist, ist dieser zuvor vorgeschlagene Ansatz der richtige Weg:

Stellen Sie einen neuen Operator oder eine Funktion wie die Funktion ie aus dem Modul Native bereit, mit der das Problem behoben wird, und veröffentlichen Sie sie als einzige zuverlässige Methode zum Aufrufen externer ausführbarer Dateien.

Eine _Funktion_ wie ie würde es den Leuten ermöglichen, sich mit minimalem Aufwand als Notlösung für das richtige Verhalten zu entscheiden, ohne die Sprache mit einem neuen syntaktischen Element (einem Operator) zu belasten, dessen alleinige Existenzberechtigung dies wäre um einen Legacy-Fehler zu umgehen, der als zu fehlerhaft angesehen wird, um ihn zu beheben:

  • Die Notlösung würde einen offiziell genehmigten Zugriff auf das richtige Verhalten ermöglichen (keine Abhängigkeit von experimentellen Merkmalen).
  • Solange die Notlösung notwendig ist, müsste sie umfassend bekannt gemacht und ordnungsgemäß dokumentiert werden.

Wenn / wann das Standardverhalten behoben wird:

  • Die Funktion kann so geändert werden, dass sie nur verschoben wird, um den Code, der sie verwendet, nicht zu beschädigen.
  • Neuer Code kann geschrieben werden, ohne dass die Funktion mehr benötigt wird.

Eine Funktion wie z. B. würde es den Menschen ermöglichen, sich mit minimalem Aufwand als Notlösung für das richtige Verhalten zu entscheiden

Wir können die Adoption mit # 13428 vereinfachen. Wir können dies mit @ mklement0s Untersuchungen in Engine transparent

@Dabombber

Ich denke, dass die standardmäßige Berücksichtigung von Ausnahmen nur zu einer ähnlichen Situation führen wird wie die aktuelle, in der die Leute die "Hilfsbereitschaft" von PowerShell zurücksetzen müssen. Wenn es Ausnahmen gibt, sollte es offensichtlich sein, dass sie angewendet werden.

Die von mir vorgeschlagenen Unterkünfte machen die überwiegende Mehrheit der Anrufe "einfach funktionieren" - sie sind nützliche Abstraktionen von der Windows-Befehlszeilenanarchie, vor denen wir unser Bestes tun sollten, um Benutzer zu schützen.

Die berechtigte Annahme ist, dass diese Abschirmung eine einmalige Anstrengung für ausführbare _legacy_-Dateien ist und dass neu erstellte Dateien den Microsoft C / C ++ - Konventionen entsprechen.

Es ist jedoch unmöglich, dies in allen Fällen zu tun; Für diejenigen, die nicht automatisch untergebracht werden können, gibt es --% .

Persönlich möchte ich nicht darüber nachdenken müssen, ob ein bestimmtes Dienstprogramm foo zufällig als foo.bat oder foo.cmd implementiert wird oder ob es foo="bar none" erfordert "foo=bar none" akzeptieren, die für konventionskonforme ausführbare Dateien gleichwertig sind.

Und ich möchte auf keinen Fall ein separates Syntaxformular für verschiedene Ausnahmen, wie z. B. &[bat]
Stattdessen ist --% das Catch-All-Tool (nur für Windows), mit dem Sie die Befehlszeile genau so formulieren können, wie Sie sie übergeben möchten - unabhängig von den spezifischen, unkonventionellen Anforderungen des Zielprogramms.

Insbesondere sind die vorgeschlagenen Unterkünfte die folgenden:

Hinweis:

  • Wie bereits erwähnt, sind sie nur unter Windows erforderlich. Unter Unix reicht das Delegieren an ProcessStartInfo.ArgumentList aus, um alle Probleme zu lösen.

  • Zumindest auf hohem Niveau sind diese Unterkünfte leicht zu konzipieren und zu dokumentieren.

  • Beachten Sie, dass sie nach der üblichen Analyse von PowerShell im (nur Windows) Übersetzungsschritt "In den Prozess übersetzen" angewendet werden. Das heißt, PowerShells eigene Parameteranalyse wird nicht beteiligt sein - und sollte es auch nicht sein , @ yecril71pl.

  • Alle dann wirklich exotischen Fälle, die nicht durch diese Unterkünfte abgedeckt sind, müssten von den Benutzern selbst bearbeitet werden
    --% - oder, Native Modul ins / Invoke-NativeShell , wodurch es einfacher wird, PowerShell-Variablen- und Ausdruckswerte in den Aufruf einzubetten.

    • Der Befehl dbea ( Debug-ExecutableArguments ) aus dem Modul Native kann dabei helfen, zu diagnostizieren und zu verstehen, welche Prozessbefehlszeile letztendlich verwendet wird - siehe den folgenden Beispielabschnitt.

Liste der Unterkünfte:

  • Für Batch-Dateien (wie bereits erwähnt, ist es wichtig, diesen Fall automatisch zu berücksichtigen, wenn hochkarätige CLIs verwendet werden, die Batch-Dateien als Einstiegspunkt verwenden, z. B. az.cmd für Azure).

    • Gegebenenfalls eingebettete doppelte Anführungszeichen werden in allen Argumenten als "" (anstelle von \" ) maskiert.
    • Jedes Argument, das keine Leerzeichen enthält, aber entweder doppelte Anführungszeichen oder cmd.exe Metazeichen wie & doppelte Anführungszeichen (während PowerShell standardmäßig nur Argumente mit Leerzeichen in doppelten Anführungszeichen enthält). Beispielsweise wird ein wörtliches Argument, das von PowerShell als a&b wird, als "a&b" in der Befehlszeile platziert, die an eine Batchdatei übergeben wird.
  • Für hochkarätige CLIs wie msiexec.exe / msdeploy.exe und cmdkey.exe (ohne Ausnahmen für die Hardcodierung):

    • Jeder Aufruf, der mindestens ein Argument der folgenden Formen enthält, löst das unten beschriebene Verhalten aus. <word> kann aus Buchstaben, Ziffern und Unterstrichen bestehen:

      • <word>=<value with spaces>
      • /<word>:<value with spaces>
      • -<word>:<value with spaces>
    • Wenn ein solches Argument vorliegt:

      • Gegebenenfalls eingebettete doppelte Anführungszeichen werden in allen Argumenten als "" (statt \" ) maskiert. - Siehe https://github.com/PowerShell/PowerShell/pull/13482#issuecomment -677813167, warum wir dies nicht tun sollten. Dies bedeutet, dass in dem seltenen Fall, dass <value with spaces> eingebettete_ " Zeichen hat, --% verwendet werden muss; z.B,
        msiexec ... --% PROP="Nat ""King"" Cole"
      • Nur der Teil <value with spaces> wird in doppelte Anführungszeichen gesetzt, nicht das gesamte Argument (letzteres ist das, was PowerShell - zu Recht - standardmäßig tut). Beispielsweise wird ein wörtliches Argument, das von PowerShell als foo=bar none wird, als foo="bar none" in der Befehlszeile des Prozesses platziert (und nicht als "foo=bar none" ).
    • Hinweis:

      • Wenn es sich bei der ausführbaren Zieldatei nicht um eine CLI im Stil von msiexec , wird kein Schaden angerichtet, da CLIs, die Konventionen einhalten, <word>="<value with spaces>" und "<word>=<value with spaces>" _äquivalent_ sinnvoll berücksichtigen, die beide wörtlich <word>=<value with spaces> .

      • In ähnlicher Weise akzeptiert die überwiegende Mehrheit der ausführbaren Dateien "" austauschbar mit \" um eingebetteten " Zeichen zu entkommen, mit Ausnahme von PowerShells eigener CLI, Ruby und Perl (_not_ Performing) Die Unterbringung lohnt sich zumindest, wenn die PowerShell-CLI aufgerufen wird, aber ich denke, dass es auch Sinn macht, Ruby und Perl hart zu codieren. https://github.com/PowerShell/PowerShell/pull/13482#issuecomment -677813167 zeigt, dass alle Anwendungen, die die WinAPI-Funktion CommandLineToArgvW verwenden, _not_ "" -escaping unterstützen.

Alle anderen Fälle unter Windows können auch mit ProcessStartInfo.ArgumentList , was implizit die Microsoft C / C ++ - Konvention anwendet (was insbesondere \" für " -Escaping bedeutet).


Die ie -Funktion aus der aktuellen Version ( 1.0.7 ) des Moduls Native implementiert diese Anpassungen (zusätzlich zum Beheben der Analyse fehlerhafter Argumente) für PowerShell-Versionen ab 3 ( Install-Module Native ).

Ich lade Sie und alle hier ein, es auf Herz und Nieren zu prüfen, um die Behauptung zu testen, dass es für die überwiegende Mehrheit der extern ausführbaren Anrufe "nur funktioniert"

Derzeit unvermeidbare Einschränkungen:

  • Hinweis: Diese technischen Einschränkungen ergeben sich aus der Implementierung von ie als _Funktion_ (eine ordnungsgemäße Korrektur in der Engine selbst würde _nicht_ diese Probleme haben):

    • Während $LASTEXITCODE richtig auf den Exit-Code des Prozesses gesetzt ist, endet $? immer $true - Benutzercode kann derzeit $? explizit setzen, obwohl diese Fähigkeit hinzugefügt wird wurde grün beleuchtet - siehe https://github.com/PowerShell/PowerShell/issues/10917#issuecomment -550550490. Leider bedeutet dies, dass Sie derzeit ie sinnvoll mit && und || , den && , den Pipeline-Kettenbetreibern, verwenden können .
      Wenn jedoch ein Skript zum Erkennen eines Exit-Codes ungleich Null abgebrochen werden soll, kann die Wrapper-Funktion iee verwendet werden.

    • -- als Argument wird ausnahmslos vom PowerShell-Parameterordner "gefressen". Übergeben Sie es einfach zweimal, um -- an die ausführbare Zieldatei weiterzuleiten ( foo -- -- anstelle von foo -- ).

    • Ein nicht zitiertes Token mit , muss in Anführungszeichen gesetzt werden, damit es nicht als Array interpretiert und als mehrere Argumente übergeben wird. Beispiel: Übergeben Sie 'a,b' anstelle von a,b . Übergeben Sie in ähnlicher Weise -foo:bar (etwas, das wie ein benanntes PowerShell-Argument aussieht) als '-foo:bar' (dies sollte nicht erforderlich sein, ist aber auf einen Fehler zurückzuführen: # 6360); ähnlich '-foo.bar must be passed as ' -foo.bar'` (ein weiterer Fehler, der auch direkte Aufrufe externer ausführbarer Dateien betrifft: # 6291)

  • Ich erwarte, dass die Funktion in PowerShell _Core_ robust funktioniert. Aufgrund von Änderungen in _Windows PowerShell_ im Laufe der Zeit kann es zu Randfällen kommen, die nicht richtig behandelt werden, obwohl mir nur zwei bekannt sind:

    • Die teilweise zitierte Unterkunft für CLIs im Stil von msiexec kann in Version 3 und 4 nicht angewendet werden, da diese Versionen das gesamte Argument in einen zusätzlichen Satz doppelter Anführungszeichen einschließen. es funktioniert jedoch in v5.1.

    • "" -escaping wird standardmäßig verwendet, um Probleme zu umgehen. In Fällen, in denen \" erforderlich ist (PowerShell CLI, Perl, Ruby), ist ein Token wie 3" of snow erforderlich fälschlicherweise als _3_ Argumente übergeben, da alle Windows PowerShell-Versionen es versäumen, ein solches Argument in doppelte Anführungszeichen zu setzen; Dies scheint bei Argumenten mit nicht initialen " -Zeichen zu passieren, denen kein Leerzeichen vorangestellt ist.


Beispiele mit Ausgabe von PowerShell Core 7.1.0-Vorschau.5 unter Windows 10:

Hinweis: Die Funktion dbea ( Debug-ExecutableArguments ) wird verwendet, um zu veranschaulichen, wie die Argumente von externen ausführbaren Dateien / Batchdateien empfangen würden.

Aktuelle, gebrochene Argumentation:

  • Aufrufen einer konventionskonformen Anwendung (eine .NET-Konsolenanwendung, die standardmäßig von dbea hinter den Kulissen verwendet wird):
# 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" }"
  • Aufrufen einer Batchdatei:

Beachten Sie die Verwendung von -UseBatchFile , damit dbea die Argumente stattdessen an eine Hilfsprogrammdatei übergeben.

# 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.
  • Aufrufen einer CLI im msiexec -Stil, 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.

Behebung des Problems mit ie :

Beachten Sie die Verwendung von -ie in den dbea -Aufrufen, wodurch ie für die Aufrufe verwendet wird.

  • Aufrufen einer konventionskonformen Anwendung (eine .NET-Konsolenanwendung, die standardmäßig von dbea hinter den Kulissen verwendet wird):
# 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\" }"
  • Aufrufen einer Batchdatei:
# 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"" }">
  • Aufrufen einer CLI im msiexec -Stil, 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.

Um zu zeigen, dass das doppelte Anführungszeichen nur in der tatsächlichen Befehlszeile über dbea angewendet wurde:

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"

Der folgende Code verursacht einen Datenverlust:

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

1

@JamesWTruher hat einen Korrekturvorschlag vorgeschlagen und behoben wurden

Gibt es eine Pull-Anfrage für diesen vorgeschlagenen Fix? Es wäre schön, wenn wir die PR kommentieren könnten. Weil es IMO nie kompliziert war, dies zu beheben. Der komplizierte Teil war der Umgang mit Abwärtskompatibilität. Und es wäre schön, wenn wir sehen könnten, wie das vorgeschlagene Update damit umgeht ...

Das ist gut zu hören, @ SteveL-MSFT - eine Lösung für alle hier diskutierten Probleme? Immerhin für v7.1? Und ich stimme der Anfrage von

@ yecril71pl , das ist ein guter Fund, obwohl sich dieser wirklich auf PowerShells eigenes Parsing bezieht (das ein spezielles Gehäuse für externe ausführbare Dateien enthält), nicht darauf, wie die native Befehlszeile nach dem Parsen aufgebaut ist (wo die zuvor diskutierten Probleme auftreten von).

Eine prägnantere Darstellung des Problems unter Unix:

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

Das heißt, nur das _first_ Array-Element wurde direkt an -a: angehängt, die anderen wurden als separate Argumente übergeben.

Es gibt verwandte Probleme mit Argumenten, die wie PowerShell-Parameter aussehen, aber nicht:

Es gibt ein verwandtes Problem, das nur Aufrufe von _PowerShell_-Befehlen betrifft, die $args / @args : # 6360 verwenden

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

Es gibt auch # 6291, das sowohl PowerShell-Befehle als auch externe ausführbare Dateien betrifft (beachten Sie die . ).

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

Eine Sache, die zu beachten ist, ist, dass (...) als Teil eines Barworts normalerweise dazu führt, dass die Ausgabe von (...) als Ganzes zu einem separaten Argument wird, so dass die Tatsache, dass das erste Element in printf angehängt ist. printf ist ein Beweis für ein spezielles Gehäuse für extern ausführbare Anrufe. z.B,
& { $args.Count; $args.ForEach({ "$_" }) } foo('bar', 'baz') ergibt 2, 'foo', 'bar baz' , das zweite Argument ist die Stringifizierung des Arrays 'bar', 'baz' .

Wenn PowerShell -A:(1,2) für eine externe ausführbare Datei übergeben muss, stellt sich heraus, dass -A: eine Zeichenfolge und (1,2) ein Array ist, das als '1 2' gemarshallt werden muss. PowerShell versucht, die ursprüngliche Syntax des Aufrufs beizubehalten. Wenn wir also alles zusammenfügen, erhalten wir '-A: 1 2', während das korrekte Ergebnis '-A: "1 2"' wäre. Es sieht für mich nach einer trivialen Auslassung im Rangiercode aus.

Ich würde nicht sagen, dass das spezifische Problem von @ yecril71pl mit dem Parsen zusammenhängt (obwohl ich damit einverstanden bin, dass es nichts mit dem Problem "Konvertieren von Arrays in Befehlszeilen" zu tun hat, das in dieser Ausgabe behandelt wird).

Wenn PowerShell -A: (1,2) für eine externe ausführbare Datei übergeben muss, stellt es fest, dass -A: eine Zeichenfolge und (1,2) ein Array ist

Fast: -A: ist ein benannter Parameter und das Array ist der Wert dieses Parameters (Um dies zu testen: Entfernen Sie das - vorne und Sie sehen, dass es anders in Anführungszeichen steht). Das Problem ist jedoch nicht, dass das Array falsch in eine Zeichenfolge konvertiert wurde - das Problem ist, dass für native ausführbare Dateien Argumente (fast) immer gespritzt werden, selbst wenn $ und nicht @ und selbst wenn das Array von einem Ausdruck wie (1,2) .

Test zum Beispiel printf '<%s>\n' -a:('a b',2) : Da die Zeichenfolge a b ein Leerzeichen enthält, wird sie korrekt in Anführungszeichen gesetzt, aber als 2 befindet sich das nächste Array-Element und das Array wird gespritzt, 2 ist nicht Teil des ersten Arguments.


Die Magie geschieht in NativeCommandParameterBinder.cs

In Zeile 170 versucht Powershell, einen Enumerator für den aktuellen Argumentwert zu erhalten.

IEnumerator list = LanguagePrimitives.GetEnumerator(obj);

Wenn list nicht null , fügt Powershell jedes Element der Liste (möglicherweise in Anführungszeichen, wenn Leerzeichen enthalten) zur lpCommandLine hinzu.

Die Elemente werden standardmäßig durch Leerzeichen ( Zeile 449 ) getrennt. Die einzige Ausnahme ist, wenn das Array ein Literal war
(wie in printf '<%s>\n' -a:1,2 ).
Dann versucht Powershell, dasselbe Trennzeichen in der lpCommandLine zu verwenden, das in der Skriptzeile verwendet wurde.

Ich erwarte eine PR, wenn ich fertig bin. Wenn es in 7.1 gelingt, nehmen wir es, sonst ist es 7.2. Abwärtskompatibilität ist etwas, das er anspricht. Vielleicht hilft etwas beim Schreiben von Pester-Tests (mit testexe -echoargs , das mit publish-pstesttools aus build.psm1 erstellt werden kann).

Ich erwarte eine PR, wenn ich fertig bin

Genau das wollte ich vermeiden - bitte zeigen Sie den Code, der noch nicht fertig ist (markieren Sie PR als in Arbeit).

Oder zumindest kommentieren, was er tun will.

Es wäre schön, wenn wir diskutieren könnten, wie er mit Abwärtskompatibilität umgehen möchte.

@TSlivede , beachten Sie, dass, da die PowerShell _CLI_ aufgerufen wird - eine externe ausführbare Datei - -A:(1,2) analysiert wird, bevor Sie wissen, dass dieses Token schließlich an den Parameter _named_ -A gebunden wird - dass ein solcher Parameter _eventually_ ins Spiel kommt ist neben dem Problem.

@ yecril71pl :

Es stellt sich heraus, dass -A: eine Zeichenfolge ist

Nein, es wird während des Parsens speziell behandelt, da es wie ein PowerShell-Parameter aussieht.

Dieses spezielle Gehäuse tritt bei Aufrufen von PowerShell-Befehlen auf, die ebenfalls $args (im Gegensatz zu tatsächlich deklarierten deklarierten Parametern), aber es geschieht _different_ für externe ausführbare Dateien (was normalerweise ein separates Argument ist, bleibt angehängt). aber im Falle einer Sammlung nur ihr erstes Element).

Sie können dieses Sondergehäuse tatsächlich deaktivieren, wenn Sie zuvor -- , aber natürlich auch -- , das nur für Aufrufe von _PowerShell_-Befehlen entfernt wird:

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>

Wenn das Argument nicht wie ein PowerShell-Parameter aussieht, wird das übliche Verhalten (die Ausgabe von (...) wird zu einem separaten Argument) auch für externe ausführbare Dateien aktiviert (wobei das übliche Verhalten eines Arrays bespritzt wird, dh umgewandelt wird) einzelne Argumente im extern ausführbaren Fall).

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

Die konsequente Anwendung dieses Verhaltens wäre sinnvoll - ein (...) -Ausdruck als Teil eines Barworts sollte immer zu einem separaten Argument werden - siehe # 13488.

Um ein einzelnes Argument '-A:1 2 3' mit dem Array _stringified_ zu übergeben, verwenden Sie eine (n implizite) _expandable Zeichenfolge_. In diesem Fall benötigen Sie überraschenderweise $(...) anstelle von (...) _und_ - derzeit auch "..." :

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.

Sie sollten in diesem Fall auch nicht "..." benötigen - dies ist wiederum aufgrund der Anomalien in Bezug auf Token erforderlich, die wie Parameter aussehen (die im Allgemeinen für PowerShell- und extern ausführbare Aufrufe gelten - siehe # 13489). Wenn nicht, brauchen Sie nicht zu zitieren:

# 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.

Die Welt der zusammengesetzten Token im Argumentationsmodus ist komplex und weist mehrere Inkonsistenzen auf - siehe # 6467.

@ SteveL-MSFT

In der aktuellen Form gibt testexe -echoArgs nur die einzelnen Argumente aus, die die ausführbare .NET Core-Datei über die Raw-Befehlszeile (unter Windows) analysiert hat, nicht die Raw-Befehlszeile selbst.

Es kann daher keine Unterkünfte mit selektivem Anführungszeichen für Batch-Dateien und CLIs im msiexec -Stil testen - vorausgesetzt, solche Unterkünfte werden implementiert, was ich dringend empfehle. Sie können beispielsweise nicht überprüfen, ob PROP='foo bar' als PROP="foo bar" , wobei nur um den Wertteil doppelte Anführungszeichen gesetzt werden.

Um die unformatierte Befehlszeile zu drucken, darf testexe keine ausführbare .NET _Core_-Datei sein, da .NET Core _ eine hypothetische Befehlszeile_ erstellt, die immer \" -escaping für eingebettete " chars., Auch wenn "" verwendet wurde, und spiegelt im Allgemeinen nicht genau wider, welche Argumente doppelt in Anführungszeichen gesetzt wurden und welche nicht - Hintergrundinformationen finden Sie unter https://github.com/ dotnet / runtime / issue / 11305 # issuecomment -674554010.

Nur eine mit .NET _Framework_ kompilierte ausführbare Datei zeigt die wahre Befehlszeile in Environment.CommandLine , sodass testexe auf diese Weise kompiliert werden müsste (und geändert werden müsste, um (optional) die unformatierte Befehlszeile zu drucken).

Um die Anpassungen für Batch-Dateien zu testen, wird eine separate Test-Batch-Datei benötigt, um zu überprüfen, ob 'a&b' als "a&b" und 'a"b' als "a""b" wird Beispiel.

@ mklement0 Kompilieren für .NET Framework ist nicht möglich und muss wahrscheinlich unter .NET Framework ausgeführt werden, um das richtige Verhalten zu erzielen. Wir haben absichtlich die gesamte native Code-Kompilierung aus dem PS-Repo entfernt, und ich glaube nicht, dass wir sie wieder hinzufügen möchten ... Eine Option ist die vorgefertigte native Testexe (die für Windows, MacOS, kompiliert werden müsste). und verschiedene Linux-Distributionen (wie Alpine separat). Das Schreiben von testexe ist einfach. Es wird einige Zeit dauern, bis die Veröffentlichung abgeschlossen ist.

Können wir uns alternativ einfach auf ein einfaches Bash-Skript für Linux / macOS verlassen, um die Argumente auszugeben?

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

Und unter Windows etwas Ähnliches mit einer Batch-Datei.

Wie wäre es mit einem Knoten mit einem .js-Skript?

console.log(process.execArgv.join('\n') oder was auch immer für eine Zeichenfolge Sie behandeln
Willst du tun, damit die Ausgabe schön aussieht?

@cspotcode , um die

@ SteveL-MSFT:

Unter Windows können Sie die Kompilierung über die CLI an _Windows PowerShell_ delegieren. Dies geschieht in dbea . Hier ist ein einfaches Beispiel, das eine ausführbare .NET _Framework_-Datei erstellt, die (nur) die unformatierte Befehlszeile wiedergibt: ./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);
  }
}
'@

}

Beispielanruf:

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

Für eine _batch-Datei_, die ihre Argumente wiedergibt, erstellt dbea bei Bedarf auch eine .

Unter Unix ist ein einfaches Shell-Skript, wie in Ihrem Kommentar gezeigt, in der Tat ausreichend, und Sie können sogar ein Ad-hoc-Skript verwenden, das Sie als _argument_ an /bin/sh übergeben .

@ PowerShell / Powershell-Komitee hat dies heute besprochen. Wir bitten @JamesWTruher , seine PR

Für diejenigen, die es vielleicht nicht bemerkt haben: Die PR wurde veröffentlicht (als WIP) und wird bereits diskutiert: https://github.com/PowerShell/PowerShell/pull/13482

PS, @ SteveL-MSFT, zum Abrufen der unformatierten Befehlszeile unter Windows: Eine Alternative zum Delegieren der Kompilierung an Windows PowerShell / .NET _Framework_ besteht natürlich darin, die vorhandene .NET _Core_-Konsolenanwendung zu erweitern, um eine (plattformbedingte) P / Aufruf der WinAPI-Funktion GetCommandLine() aufrufen, wie unten gezeigt.

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-MSFT

Abwärtskompatibilität ist etwas, das er anspricht.

Ich denke, es wird durch Ihre spätere Klarstellung impliziert, dass ProcessStartInfo.ArgumentList (eine Sammlung von _verbatim_ Argumenten, die unter Unix unverändert verwendet und von .NET Core selbst in eine MS C / C ++ - konventionskonforme Befehlszeile unter Windows übersetzt werden ) sollte verwendet werden, aber lassen Sie es mich ausdrücklich sagen:

  • Wenn Sie dieses Problem ein für alle Mal richtig beheben, werden Zugeständnisse an die Abwärtskompatibilität ausgeschlossen.

  • @JamesWTruhers PR ist zum leere Argumente immer noch nicht durchlaufen werden .

    • Sobald dies behoben ist, ist der Fix unter _Unix_ abgeschlossen - es fehlen jedoch die wichtigen Anpassungen für CLIs unter _Windows_ (siehe unten).

Möglicherweise benötigen wir eine Zulassungsliste für bekannte Sonderfallbefehle, die bei der vorgeschlagenen Änderung immer noch fehlschlagen und die später hinzugefügt werden können.

Ich fordere Sie auf, dies erst später aufzuschieben .

Anstelle einer _allowlist_ (Sondergehäuse für bestimmte ausführbare Dateien) können einfache allgemeine Regeln die Anpassungen einigen weiteren Diskussionen mit @TSlivede gegenüber den oben genannten verfeinert wurden .

Diese Unterkünfte, die nur unter Windows benötigt werden:

Im Einzelnen sind dies:

  • Genau wie unter Unix wird standardmäßig ProcessStartInfo.ArgumentList verwendet - außer wenn eine oder beide der folgenden Bedingungen erfüllt sind. In diesem Fall muss die Prozessbefehlszeile manuell erstellt (und ProcessStartInfo.Arguments as zugewiesen werden zur Zeit):

    • Eine Batch-Datei ( .cmd , .bat ) oder cmd.exe direkt:
    • In diesem Fall werden _embedded_ " als "" (anstatt als \" ) und platzlose Argumente maskiert, die einen der folgenden cmd.exe Metazeichen enthalten werden auch in doppelte Anführungszeichen gesetzt (normalerweise werden nur Argumente _with Leerzeichen_ in doppelte Anführungszeichen gesetzt): " & | < > ^ , ; - Dadurch funktionieren Aufrufe von Batchdateien robust, was wichtig ist, da viele hochkarätige CLIs Batchdateien als _entry verwenden points_.
    • Unabhängig (und möglicherweise zusätzlich), wenn mindestens ein Argument mit dem regulären Ausdruck übereinstimmt
      '^([/-]\w+[=:]|\w+=)(.*? .*)$' ist vorhanden, alle diese Argumente müssen _partial_ nur in doppelten Anführungszeichen um den _value-Teil_ anwenden (was folgt auf : oder = )

      • ZB msiexec.exe / msdeploy.exe und cmdkey.exe Argumente im Power-Stil, die von PowerShell wörtlich als gesehen werden
        FOO=bar baz und /foo:bar baz / -foo:bar baz werden in der Prozessbefehlszeile als platziert
        foo="bar baz" oder /foo:"bar baz" / -foo:"bar baz" machen _jede_ CLIs, die diese Art des Zitierens erfordern, glücklich.
    • Verbatim \ Zeichen in den Argumenten müssen gemäß den MS C / C ++ - Konventionen behandelt werden.

Was wird von diesen Unterkünften nicht abgedeckt:

  • msiexec.exe (und vermutlich auch msdeploye.exe ) unterstützt _nur_ "" -Entweichen von _embedded_ " Zeichen. , die die oben genannten Regeln _nicht_ abdecken würden - außer wenn Sie zufällig über eine Batch-Datei oder cmd /c aufrufen.

    • Dies sollte zunächst selten genug sein (z.
      msiexec.exe /i example.msi PROPERTY="Nat ""King"" Cole" ), aber es wird wahrscheinlich noch seltener gemacht, da misexec Aufrufe normalerweise synchron gemacht werden, um auf das Ende einer Installation zu warten. In diesem Fall können Sie das Problem in einem von beiden vermeiden zwei Wege:
    • cmd /c start /wait msiexec /i example.msi PROPERTY='Nat "King' Cole' - Verlassen Sie sich auf Anrufe bei cmd.exe (dann), die "" -es Flucht auslösen
    • Start-Process -Wait msiexec '/i example.msi PROPERTY="Nat ""King"" Cole"' - Verlassen Sie sich auf den Parameter -ArgumentList ( -Args ), der ein einzelnes Zeichenfolgenargument wörtlich als Prozessbefehlszeile übergibt (obwohl dieser Parameter nicht so funktionieren soll - siehe # 5576).
  • Alle anderen nicht konventionellen CLIs, für die die oben genannten Unterkünfte nicht ausreichen - mir persönlich sind keine bekannt.

Am Ende des Tages gibt es immer eine Problemumgehung: Rufen Sie über cmd /c oder für Nicht-Konsolenanwendungen über Start-Process an oder verwenden Sie --% ; Wenn wir ein Cmdlet ins ( Invoke-NativeShell ) bereitstellen, ist dies eine weitere Option. Ein dbea ( Debug-ExecutableArguments Cmdlet mit echoArgs.exe -ähnlichen Fähigkeiten, aber auf Anfrage auch für Batch-Dateien, würde ebenfalls helfen, Probleme zu diagnostizieren.


Was den Weg zu einer bahnbrechenden Veränderung im Vergleich zum Opt-In betrifft:

  • Bedeutet die Implementierung als experimentelles Merkmal, dass, wenn genügend Interesse gezeigt wird, dies zum Standardverhalten wird und daher eine (nicht triviale) Änderung darstellt?

  • Können Sie bitte sicherstellen, dass dieses experimentelle Merkmal aufgrund seiner Bedeutung weit verbreitet ist?

    • Ein allgemeines Problem, das ich in Bezug auf experimentelle Funktionen habe, ist, dass ihre Verwendung in Vorschauversionen häufig _unwitting_ sein kann, da _ alle_ experimentellen Funktionen standardmäßig aktiviert sind. Wir möchten auf jeden Fall, dass die Leute diese Funktion bewusst kennen und bewusst ausüben.
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen