Powershell: Rufen Sie den nativen Operator an

Erstellt am 30. Juni 2020  ·  189Kommentare  ·  Quelle: PowerShell/PowerShell

Problemstellung

Derzeit gibt es Fälle, in denen das Ausschneiden und Einfügen einer nativen Befehlszeile in PowerShell nicht wie erwartet ausgeführt wird. Dies kann auf ein falsches Parsen von Anführungszeichen zurückzuführen sein, die an den nativen Befehl übergeben werden sollen, oder auf die Verwendung der PowerShell-Syntax, die nicht als PowerShell interpretiert werden soll. PowerShell hat ein spezielles Argument --% , wenn es mit nativen Befehlen verwendet wird. Es behandelt den Rest der Argumente als Literale, die an den nativen Befehl übergeben werden, hat jedoch mehrere Probleme:

  1. Es ist nicht erkennbar, Benutzer müssen diesen speziellen Parameter im Voraus kennen
  2. | , && und || Vorrang, also: wsl --% ls | less würde wsl ls ausführen und die Ergebnisse an less läuft in PowerShell anstatt less läuft in wsl
  3. Wenn Sie eine Befehlszeile ausschneiden und einfügen, müssen Sie die Zeile bearbeiten, um --% am Anfang einzufügen
  4. Auf Unix-Systemen werden die Argumente nach -% wörtlich ohne Globbing übergeben, wobei native Befehle unter Unix erwarten, dass die Shell Globbing ausführt

Vorgeschlagene technische Implementierungsdetails

Es wird vorgeschlagen, einen neuen Operator --% (Call Native) einzuführen.

Jeder Textinhalt nach diesem Operator ruft die "Standard-Shell" des Betriebssystems auf, um ausgeführt zu werden. Unter Windows wäre dies cmd.exe und auf Unix-basierten Systemen wäre dies / bin / sh. Dies behebt das Globbing-Problem auf Unix-basierten Systemen und ermöglicht auch eine Erweiterung von %variable% unter Windows. Im Gegensatz zum Schalter --% bedeutet dies auch, dass | , && und || als Teil der nativen Befehlszeile behandelt werden.

Dies bedeutet, dass diese beiden funktional gleich sind:

wsl --% ls $foo `&`& echo $PWD
--% wsl ls $foo && echo $PWD

Dabei werden $foo und $PWD von der Shell innerhalb der WSL ausgewertet. Beachten Sie, dass Sie im ersten Beispiel wissen müssen, ob Sie && entkommen müssen, damit es in WSL statt in PowerShell ausgeführt wird.

Um die Ausgabe einer solchen Ausführung zurück in PowerShell zu leiten, muss der Benutzer die Ergebnisse zuerst in einer Variablen speichern:

$out = --% ls *.txt
$out | select-string hello

Beachten Sie, dass Sie im Gegensatz zum aktuellen & keine PowerShell-Syntax verwenden können.

--% $commandline

würde $commandline zuerst von PowerShell als Variable auflösen, sondern $commandline an die Standard-Shell übergeben, um ungelöste zu verarbeiten.

Das Problem des Ausschneidens und Einfügens wird durch einfaches Einfügen gelöst, nachdem --% eingegeben wurde.

Das obige Beispiel für wsl würde folgendermaßen aussehen:

--% wsl ls | less

Dabei soll die gesamte Zeile innerhalb der WSL-Linux-Instanz ausgeführt werden.

Auffindbarkeit

Benutzer, die bereits mit --% als Switch vertraut sind, können problemlos auf die Verwendung dieses neuen Operators umsteigen, wenn dies sinnvoll ist. Für neue Benutzer ist --% einzigartig, sodass Suchmaschinen es leicht mit PowerShell in Verbindung bringen können.

Alternative Überlegungen

&! und &n wurden als Siegel vorgeschlagen, aber es wurde zurückgedrängt, da &! in einigen Sprachen ein gültiger Operator ist, der eine Websuche nach Dokumentation erschwert. Es gab auch Bedenken, ob eine visuelle Darstellung, die dem Anrufbetreiber & ähnelt, die Benutzer verwirren würde.

Bei Verwendung dieses neuen Operators besteht die Frage nach der Unterstützung der Zeilenfortsetzung. Ich würde vorschlagen, dass wir es zunächst nicht unterstützen.

Eine Cmdlet-Lösung anstelle einer Operatorlösung wurde vorgeschlagen. Wir glauben, dass dies das Problem des Ausschneidens und Einfügens nicht löst, da Sie jetzt wissen müssen, wie Sie die Pipeline in einfache Anführungszeichen setzen und / oder Sonderzeichen umgehen können. Wir glauben, dass ein Cmdlet wie in Invoke-NativeCommand (zu bestimmendes Substantiv) als zusätzliche Option nützlich wäre, anstatt die Notwendigkeit eines Operators zu ersetzen.

Verwandte Themen

Dies sollte auch diese Probleme lösen:

https://github.com/PowerShell/PowerShell/issues/12491
https://github.com/PowerShell/PowerSHell/issues/1761

Committee-Reviewed Issue-Discussion Issue-Enhancement

Hilfreichster Kommentar

Ich habe gerade ein Modul veröffentlicht, Native ( Install-Module Native -Scope CurrentUser ), das ich Sie alle zum Ausprobieren einlade und dessen Befehle ich unten verwenden werde . die numerischen Anwendungsfälle verwiesen wird, sind von @rkeithhill ‚s Kommentar oben .

Wenn wir jemals wollen, dass sich der konzeptionelle Nebel auflöst, müssen wir die Anwendungsfälle meiner Meinung nach anders gestalten.
Keiner von ihnen erfordert Änderungen beim Parsen.

Anwendungsfall [Native-Shell-Aufruf]:

Sie möchten einen einzelnen Befehl (Anwendungsfall 1) oder eine gesamte Befehlszeile mit mehreren Befehlen (Anwendungsfall 2) ausführen, die für die plattformeigene Shell geschrieben wurde (dieses Problem):

  • Da Sie die Syntax einer anderen Shell verwenden, übergeben Sie den Befehl (Zeile) als einzelne Zeichenfolge an die Ziel-Shell, damit _it_ die Analyse durchführen kann. Die String-Interpolation von PowerShell bietet die Möglichkeit, PowerShell-Werte in den übergebenen String einzubetten (Anwendungsfall 2a).
  • ins ( Invoke-NativeShell ) deckt diese Anwendungsfälle ab.
# Use case 1: Single executable call with line continuation, on Unix.
@'
tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found 
'@ | ins

# Use case 2: Entire Bash command line (also with line continuation)
@'
ps -o pid,args | awk \
  '/pwsh/ { print $1 }'
'@ | ins

# Use case 2a: Entire Bash command line (also with line continuation) with string interpolation.
# Note the double-quoted here-string and the need to escape the $ that is for Bash as `$
$fields = 'pid,args'
@"
ps -o $fields | awk \
  '/pwsh/ { print `$1 }'
"@ | ins

# Alternative to use case 2a: pass the PowerShell value *as a pass-through argument*,
# which allows passing the script verbatim.
# Bash sees the pass-through arguments as $1, ... (note that the `awk` $1 is unrelated).
@'
ps -o $1 | awk \
  '/pwsh/ { print $1 }'
'@ | ins - 'pid,args'

Die Here-String-Syntax ist nicht die bequemste (daher der Vorschlag, eine Inline-Variante zu implementieren - siehe # 13204), aber Sie müssen sie nicht verwenden. Für wörtliche Befehle können Sie '...' , für die nur _doubling_ embedded ' erforderlich ist, falls vorhanden.

Hier ist auch ein vernünftiger Ersatz für den "StackOverflow-Operator" :

Wenn Sie den folgenden PSReadLine Key-Handler in Ihre $PROFILE -Datei einfügen, können Sie Alt-v verwenden, um einen Aufruf von ins mit einer wörtlichen Hier-Zeichenfolge zu erstellen in die der aktuelle Text der Zwischenablage eingefügt wird.Enter sendet den Anruf.

# Scaffolds an ins (Invoke-NativeShell) call with a verbatim here-string
# and pastes the text on the clipboard into the here-string.
Set-PSReadLineKeyHandler 'alt+v' -ScriptBlock {
  [Microsoft.PowerShell.PSConsoleReadLine]::Insert("@'`n`n'@ | ins ")
  foreach ($i in 1..10) { [Microsoft.PowerShell.PSConsoleReadLine]::BackwardChar() }
  # Comment the following statement out if you don't want to paste from the clipboard.
  [Microsoft.PowerShell.PSConsoleReadLine]::Insert((Get-Clipboard))
}

Anwendungsfall [direkter ausführbarer Aufruf]:

Sie möchten eine einzelne externe ausführbare Datei mit der Syntax von PowerShell mit einzelnen Argumenten aufrufen (Anwendungsfälle 1a und 3).

  • Dies ist ein Kernmandat jeder Shell, und es ist von entscheidender Bedeutung, dass sie robust funktioniert.

    • Sie müssen sich darauf verlassen können, dass PowerShell alle Argumente, die sich aus der Analyse von _as-is_ ergeben, an die ausführbare Zieldatei weiterleiten kann. Dies ist derzeit nicht der Fall - siehe # 1995 .
  • Sie müssen die Syntax von PowerShell und die Metazeichen im Argumentmodus verstehen.

    • Sie müssen sich bewusst sein, dass ` als Escape-Zeichen und für die Fortsetzung der Zeile verwendet wird.
    • 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 ).

Während wir darauf warten, dass # 1995 behoben wird, füllt die Funktion ie (für i nvoke (external) e xecutable) die Lücke, indem einfach ein direkter Anruf vorangestellt wird. zB anstelle des folgenden Aufrufs:

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

Das Modul Native etwas, das echoArgs.exe ähnelt, den Befehl dbea ( Debug-ExecutableArguments ). Beispielausgabe unter Windows:

# Note the missing first argument, the missing " chars., and the erroneous argument boundaries.
PS> dbea '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'
7 argument(s) received (enclosed in <...> for delineation):

  <a&b>
  <3 of snow Nat>
  <King>
  <Cole c:\temp>
  <1\ a>
  <">
  <b>

Command line (helper executable omitted):

  a&b 3" of snow "Nat "King" Cole" "c:\temp 1\\" "a \" b"

Mit dem Schalter -UseIe ( -ie ) können Sie dbea anweisen, die Argumente über ie . Dies zeigt, dass die folgenden Probleme behoben werden:

# OK, thanks to -UseIe
PS> dbea -UseIe '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'
6 argument(s) received (enclosed in <...> for delineation):

  <>
  <a&b>
  <3" of snow>
  <Nat "King" Cole>
  <c:\temp 1\>
  <a \" b>

Command line (helper executable omitted):

  "" a&b "3\" of snow" "Nat \"King\" Cole" "c:\temp 1\\" "a \\\" b"

Hinweis: Sobald # 1995 behoben ist, ist ie nicht mehr erforderlich. Im Interesse der Vorwärtskompatibilität wurde ie entwickelt, um einen Fix zu erkennen und automatisch zu verschieben. Das heißt, sobald das Update installiert ist, werden ie Problemumgehungen nicht mehr anwenden und sich effektiv wie ein direkter / & -Anruf verhalten.

Anwendungsfall [direkter, nicht ausführbarer Windows-Aufruf]:

Es gibt zwei Hauptprobleme, die nur bei Windows auftreten:

  • Sie rufen eine ausführbare "Rogue" -Datei auf, deren Angebotsanforderungen von der am häufigsten verwendeten Konvention abweichen . Zum Beispiel erfordern msiexec.exe und msdeploy.exe dass prop="<value with spaces>" genau so zitiert wird - das Zitat direkt um den _value_-Teil - obwohl "prop=<value with spaces>" - das gesamte zitiert Argument - sollte gleich sein (letzteres ist das, was PowerShell - zu Recht - hinter den Kulissen tut).

  • Sie rufen eine Batchdatei mit einem Argument auf, das keine Leerzeichen enthält, aber cmd.exe Metazeichen_ enthält, z. B. & , ^ oder | ; z.B:

    • .\someBatchFile.cmd 'http://example.org/a&b'
    • PowerShell übergibt - zu Recht - http://example.org/a&b _unquoted_ (da kein eingebettetes Leerzeichen vorhanden ist), dies unterbricht jedoch den Aufruf der Batchdatei, da cmd.exe - unangemessen - die an eine Batchdatei übergebenen Argumente dem unterwirft Dieselbe Parsing-Regelung, die innerhalb von cmd.exe gelten würde, anstatt sie als Literale zu akzeptieren.

    • Hinweis: Während die Verwendung von Batchdateien zur direkten Implementierung von Funktionen wahrscheinlich nachlässt, ist die Verwendung als _CLI-Einstiegspunkte_ immer noch weit verbreitet, z. B. die az CLI von Azure, die als Batchdatei az.cmd implementiert ist.

Hinweis: ie behandelt diese Szenarien automatisch für Batch-Dateien sowie für msiexec.exe und msdeploy.exe , sodass für _most_ Aufrufe kein zusätzlicher Aufwand erforderlich sein sollte . Es ist jedoch unmöglich, alle ausführbaren "Rogue" -Dateien zu antizipieren.

Es gibt zwei Möglichkeiten, dies zu beheben:

  • Angesichts der Tatsache, dass cmd.exe nach dem Anwenden einer eigenen Interpretation auf Argumente in einer Befehlszeile _does_ das angegebene Anführungszeichen beibehält, können Sie unter Windows an einen Aufruf von ins delegieren:

    • ins '.\someBatchFile.cmd "http://example.org/a&b"'
    • Wenn Sie eine _expandable_-Zeichenfolge verwenden, können Sie auf diese Weise erneut PowerShell-Werte in die Befehlszeichenfolge einbetten.

      • $url = 'http://example.org/a&b'; ins ".\someBatchFile.cmd `"$url`""

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

    • .\someBatchFile.cmd --% "http://example.org/a&b"'
    • Angesichts der Implementierung von --% besteht die einzige Möglichkeit, PowerShell-Werte einzubetten, darin, - umständlich - ein _aux zu definieren. Umgebungsvariable_ und referenzieren Sie sie mit der Syntax %...% :

      • $env:url = 'http://example.org/a&b'; .\someBatchFile.cmd --% "%url%"


Fehlerbehandlung

Die ausstehende https://github.com/PowerShell/PowerShell-RFC/pull/88 ermöglicht eine bessere Integration der Fehlerbehandlung in externe (native) ausführbare Dateien.

In der Zwischenzeit wird das Modul Native mit zwei Funktionen für die Ad-hoc-Anmeldung geliefert, mit der ein Exit-Code ungleich Null als Fehler beim Beenden eines Skripts behandelt wird :

  • ins unterstützt den Schalter -e / -ErrorOnFailure , der einen Fehler auslöst, wenn $LASTEXITCODE nach dem Aufruf der nativen Shell ungleich Null ist.

  • iee ist eine Wrapper-Funktion für ie , die analog einen Fehler auslöst, wenn $LASTEXITCODE nach dem Aufruf der externen ausführbaren Datei ungleich Null ist.

Die mit dem Modul gelieferten Befehle enthalten viele Feinheiten.
Die ziemlich umfangreiche kommentarbasierte Hilfe deckt sie hoffentlich ausreichend ab.

Alle 189 Kommentare

Ich mag die Idee und finde sie nützlich, denke aber, dass sie weitaus nützlicher wäre (ich denke speziell im Fall von DSL), wenn ich eine einzelne Zeichenfolgenerweiterung erhalten könnte. Wenn ich also eine cmd.exe-kompatible Zeichenfolge in meinem PS erstellt habe, kann ich eine wörtliche Zeichenfolge zu cmd.exe erstellen. Vielleicht

&n -command $string

Dadurch könnte ich möglicherweise auch meinen Dolmetscher angeben:

&n -shell /bin/sh -command $string
oder
&n -shell cmd.exe -command $string

in Bezug auf den Namen / Betreiber ist "&!" in Benutzung? Das hat Ähnlichkeit mit shbang ("#!").

$! ist ein guter Vorschlag!

Die Shell kann durch einfaches Angeben als Befehl angegeben werden:

&! /bin/sh -c blah

Die $var -Erweiterung verursacht das Problem, dass $ möglicherweise wörtlich sein muss, um an den nativen Befehl / die native Shell übergeben zu werden. Eines der Probleme, die damit vermieden werden sollen, besteht darin, dass die Menschen herausfinden müssen, wie sie alles richtig entkommen können. Wenn Sie eine variable Erweiterung benötigen, können Sie immer Folgendes tun:

$mycommand = "/bin/sh ls"
Invoke-Expression "&! $mycommand"

In Fällen, in denen Sie PowerShell mit dem nativen Befehl mischen möchten, ist es wahrscheinlich am besten, die aktuelle Syntax zu verwenden und sich nur darüber im Klaren zu sein, was ordnungsgemäß maskiert werden muss, da ich diese erweiterte Verwendung in Betracht ziehen würde.

$mycommand = "/bin/sh ls"
Invoke-Expression "&! $mycommand"

Ich kann nicht glauben, dass du gerade iex vorgeschlagen hast. :-)

@essentialexch gibt es ein paar mal wo es angebracht ist :)

Beachten Sie, dass im Beispiel erwartet wird, dass der Benutzer den Inhalt von $mycommand vor der Ausführung ordnungsgemäß überprüft hat!

Eine großartige Beschreibung und eine Lösung für Probleme wie das von Ihnen beschriebene sind auf jeden Fall erforderlich.
Ich selbst wusste nichts über --% und kann mich leider nicht erinnern, es jemals gesehen zu haben. Daher kann der vorgeschlagene &n -Operator unter ähnlichen Erkennbarkeitsproblemen leiden, und es erscheint mir nicht selbstverständlich, ein Symbol mit einem Zeichen zu kombinieren (und es erinnert mich an% f in C printf-Anweisungen). Da es sowieso eine bahnbrechende Veränderung ist Gibt es einen Grund, warum es nicht zum Beispiel && ?
Vielleicht wäre es für mich intuitiver, so etwas wie Klammern (oder doppelte Klammern?) Zu verwenden, um den Bereich zu markieren, den Sie ausführen möchten. Beispiel: & [ wsl ls ] | less

Ich bin damit einverstanden, dass &! der intuitivere Weg für diejenigen ist, die aus anderen Shells stammen. In Super-Duper-Linter führe ich jedoch normalerweise native Befehle aus, indem ich ein Parameter-Array erstelle und es dann auf den Befehl aufteile.

$command = 'linter'
$myargs = @(
    '-config'
    'linterconfig.path'
)
if ($Test) {$myargs += 'test'}
& $command <strong i="7">@myargs</strong>

Das ist also der beste Weg für bedingte und so weiter für mich

@bergmeister Ich dachte ursprünglich an && da es visuell ähnlich ist wie & das viele Leute heute kennen. Wenn && ein Pipeline-Kettenbetreiber ist, kann dies jedoch zu Verwirrung darüber führen, was er tun soll. Ich mag den &! Vorschlag derzeit.

@ JustinGrote , es gibt keinen Grund, das nicht weiter zu verwenden. Die Syntax &! ist wirklich für Fälle gedacht, in denen Sie nur ausschneiden und einfügen möchten oder einige Argumente haben, die mit der PowerShell-Syntax in Konflikt stehen und nicht möchten oder nicht wissen, wie Sie richtig entkommen sollen

Oh Mann, ich erinnere mich, dass ich vor einem Jahrzehnt mit Bruce und Jason darüber diskutiert habe. Mein Bauchgefühl ist, dass es hier unnötig erscheint, einen anderen Operator zu erfinden. Ich weiß, dass einige Leute in diesem Thread noch nichts von --% aber ich wette, dass es mehr gibt, als Sie denken. Was ist falsch mit:

# run ls in wsl, return results to powershell, then pipe to wsl again, to grep.
& wsl ls | wsl grep -i "foo"  

#  the entire pipeline right of wsl will run in wsl. 
& --% wsl ls | grep -i "foo"

Warum hat das niemand vorgeschlagen? Es ist logisch konsistent mit der Verwendung von --% anderer Stelle zu sagen, "alles danach soll ohne spezielle Powershell-Behandlung weitergegeben werden."

@oising gut für einen, dieser Befehl funktioniert nicht, weil Sie ihm den Befehl Schlagen Sie vor, die Funktionalität hinzuzufügen?
image

Das macht irgendwie was erwartet wird:
function Invoke-LiteralCommand ($command) {Invoke-Expression "& $command --% $args"}

Invoke-LiteralCommand ping -W 200 www.google.com | grep icmp

@JustinGrote Ja, ich weiß, dass es jetzt nicht funktioniert :) Ich schlage vor, dass _instead_ von &n oder &! oder was auch immer darüber gesprochen wird. Für mich macht es einfach mehr Sinn: & ist ein Aufruf und --% unterdrückt das Parsing von Powershell; zusammen sind sie kohärent und auffindbarer als etwas Neues hinzuzufügen. Ich mag die Idee nicht, einen "Anruf" - und einen "Anruf nativen" Operator zu haben.

Ich habe mein Beispiel erweitert, um die Unterschiede zu zeigen.

@oising Ich wäre cool mit dem über einen neuen Operator, obwohl das eine Menge stumpfer Eingabe für "neuer Powershell-Benutzer, der Bash kennt" ist, wofür ich annehme, dass dies gedacht wäre.

@oising Ich wäre cool mit dem über einen neuen Operator, obwohl das eine Menge stumpfer Eingabe für "neuer Powershell-Benutzer, der Bash kennt" ist, wofür ich annehme, dass dies gedacht wäre.

Überhaupt nicht. Es ist für den PowerShell-Benutzer gedacht, der die komplexen Angebotsregeln zwischen PowerShell / cmd.exe / bash nicht versteht. Wie der Titel der Ausgabe lautet "call native", um native ausführbare Dateien aufzurufen.

@oising Ich wäre cool mit dem über einen neuen Operator, obwohl das eine Menge stumpfer Eingabe für "neuer Powershell-Benutzer, der Bash kennt" ist, wofür ich annehme, dass dies gedacht wäre.

Überhaupt nicht. Es ist für den PowerShell-Benutzer gedacht, der die komplexen Angebotsregeln zwischen PowerShell / cmd.exe / bash nicht versteht. Wie der Titel der Ausgabe lautet "call native", um native ausführbare Dateien aufzurufen.

Stimmt, oder für diejenigen von uns, die lieber gar nicht an sie denken müssen.

Ja, ich bin bei @oising. Dieses Konzept existiert, es ist einfach absolut unzureichend. Wenn wir etwas völlig Neues implementieren wollen, ist es besser, die alte Syntax zu verwerfen / zu entfernen.

Ich bin der Meinung, dass diese Ideen oft geäußert werden und der tatsächlichen Zielgruppe nicht genügend Gewicht beigemessen wird. Wenn Benutzer bereits Schwierigkeiten haben, die vorhandenen Methoden zu finden, und feststellen, dass die vorhandenen Methoden fehlen, wenn sie gefunden werden, müssen a) die Dokumentation verbessert und b) die tatsächliche Funktionalität der vorhandenen Operatoren verbessert werden.

Stattdessen erhalten wir eine seltsame Option c), die die Probleme der Vergangenheit irgendwie lösen soll, während ein weiterer Operator eingeführt wird, der sich auf Dokumentation stützt, die für Benutzer bereits nicht zugänglich genug ist, um sie auf natürliche Weise zu finden.

Ich bin damit einverstanden, dass Fehlermeldungen und / oder das Vorschlags-System verwendet werden sollten, um diese Konzepte einzuführen. Ich stimme nicht zu, dass wir einen dritten brauchen? vierte? fünfte? (Ich habe buchstäblich die Zählung verloren, jemand hilft mir hier raus) Weg, Befehle aufzurufen. Die vorhandenen Methoden sind unzureichend - das heißt, wir sollten sie verbessern und nicht wie Spinnweben zurücklassen, um die Benutzer weiter zu verwirren, wenn sie anfangen, sich in sie zu vertiefen.

Die erste Frage, die ich bekomme, wenn Leute erkennen, dass es 5 Möglichkeiten gibt, etwas zu tun, ist "Warum gibt es 2-3 Möglichkeiten, die buchstäblich dasselbe tun, aber nicht so gut funktionieren", und meine Antwort lautet wie immer: - Das PS-Team konzentriert sich zu sehr darauf, sicherzustellen, dass alles aus dem letzten Jahrzehnt ++ noch funktioniert, wenn versucht wird, Funktionen hinzuzufügen / zu verbessern. Hier sehen wir es wieder, wir haben vorhandene, aber unzureichende Implementierungen, die überarbeitet und verbessert werden müssen, und die vorgeschlagene Lösung besteht darin, den Pool weiter zu trüben, indem eine weitere potenziell unvollständige Implementierung hinzugefügt wird.

Wir sollten das beenden, was wir beginnen, und nicht noch einmal eine neue Implementierung starten, IMO.

Ich glaube, wenn wir & für mehr als ein paar einzigartige Anwendungsfälle verwenden wollen, sollten wir darüber nachdenken, dies als Verb zu betrachten oder die Standardisierung in der gesamten Sprache zu betrachten.

Das Letzte, was ich möchte, ist Verwirrung darüber, was & tun soll. In seiner Gänze.

Ich würde 3 Szenarien betrachten:

  1. Volle Erweiterung - aktuelles PowerShell-Verhalten.
  2. Vollständiges Literal - das wird im OP vorgeschlagen.
  3. Teilweise Erweiterung - wsl ls $path funktioniert immer noch

Ich frage mich, wo @ mklement0 Kommentare? :-)

Zu Beginn des Lesens dieses Threads dachte ich; großartige Idee! Aber nachdem ich Kommentare gelesen hatte, begann ich nachzudenken. Ich habe immer festgestellt, dass der Befehl & nicht "PS-Standards nicht würdig" ist und mich an "PERL-Tage" erinnert (ugh). Die Einführung eines anderen nicht standardmäßigen PS-Befehls (kein Substantiv-Verb) wird nicht helfen. Sicherlich nicht die weniger versierten PS.

Ich wusste auch nichts über den Parameter -%. Ich verwende das gleiche Prinzip wie @JustinGrote als Lösung.

Das Ausschneiden / Einfügen von Befehlen in die PS-Shell war noch nie meine Hauptsache.
Wären wir nicht besser darin, diese nativen Befehle durch PowerShell-Befehle zu ersetzen?
Machen Sie einen Pfad zum vollständigen Ersetzen von cmd.exe ...

Ich stimme dafür, die Verbesserung bestehender Befehle zu untersuchen. Und - in der Tat - einige Dokumentationen zu -% und & Nutzung verbessern

@iSazonov Der Anwendungsfall @ SteveL-MSFT schlägt das Szenario "Ausschneiden und Einfügen" vor. Eine teilweise Erweiterung würde die Dinge meiner Meinung nach viel schwieriger machen (auf der AST-Seite).

Sofort aber von "#!" und dann "!#". Ich mag das "&!".

Wir können wahrscheinlich versuchen, Token-Beschleuniger zu Funktionsaufrufen im Tokenizer hinzuzufügen. Dies würde die Verwendung der folgenden ermöglichen.

Iex -l
Oder Invoke-Expression -literal, das das Parsen beendet, bevor es weitergegeben wird.

Ich bin der Meinung, dass das Hinzufügen weiterer einzigartiger Token auf Parsing-Ebene die Eintrittsbarriere erhöht und die Erkennbarkeit für diese Funktion verringert.

Get-Help & zum Beispiel wird mit nichts angezeigt. Und wir müssen in about_Operators danach suchen.

Es gibt jedoch eindeutige Fälle für den call_operator, die nicht dokumentiert werden.
& Modulname {Befehl}
Ermöglicht den Zugriff auf ein Modul. Und ich bin mir sicher, dass es noch mehr gibt. Die Auffindbarkeit ist jedoch so gering, dass es schwierig wird, sie in der nativen Dokumentation zu finden. Und die Community weiß davon nur durch JSnover und einen Vortrag, der uns coole Dinge zeigt.

Ich glaube, was auch immer entschieden wird, muss vollständig berücksichtigen, wie auffindbar diese "Funktion" aus einer neuen Benutzerperspektive ist, und bedenken, dass neuere Benutzer versuchen werden, dies auf Powershell 5 zu verwenden, nicht ohne zu wissen, dass es neu für pwsh core ist, wenn Die Auffindbarkeit ist zu gering.

Ich würde es lieben, wenn wir das vorhandene Verhalten ändern würden, aber das Ändern einer so grundlegenden API in PowerShell würde einfach so viele Dinge kaputt machen. Wir könnten möglicherweise eine Migration mit einer Konfiguration in Betracht ziehen.

Ein direkteres Aufbrechen der Dinge war vielleicht einmal möglich, bevor PS 6 auf GA ging, aber ehrlich gesagt wäre es selbst dann eine ernsthafte Bedrohung für die Abwärtskompatibilität gewesen (ähnlich wie bei Pythons print() -Funktion).

In Bezug auf die Übergabe von Argumenten an Unterprozesse denke ich, dass sowohl der synchrone Aufruf als auch der Aufruf von Start-Process bereits Probleme bei der Erörterung einer Überarbeitung haben, und ich bin der Meinung, dass beide aus Gründen der Konsistenz gleichzeitig investieren müssen. Insbesondere Start-Process muss seine Unterstützung für die Übergabe eines Arrays von Argumenten aktualisieren.

Für den synchronen Aufruf mit cmdline arg übergeben sehe ich vier Arten von Argumenten möglich:

  • Aktuelles Verhalten, das zum Legacy werden würde, das unter Windows CommandLineToArgvW unterliegt und unerwartete Ergebnisse hat
  • Standardverhalten, das Argumente anhand ihres Ausdruckswerts übergeben sollte, aber die üblichen Bareword-Token-Regeln anwendet, sodass Dinge wie > und | separate Befehle sind. In diesem Modus sollte der Wert, den ein Unterprozess aus dem Ausdruck nimmt, der Wert sein, der von Write-Host $val angezeigt wird
  • Verbatim-Verhalten, bei dem alle Token als Barwortzeichenfolgen interpretiert werden, bis das Escape-Token angezeigt wird. Dies ist, was --% heute tun soll, aber im Idealfall hätte es nur ein End-Token, das in dieselbe Zeile wie --% ... %-- eingebettet werden kann
  • Bash-Readline-Verhalten, bei dem eine alternative, sh-orientierte Escape-Logik angewendet wird, die eine einfachere Kompatibilität ermöglicht

Ich denke, das erste Verhalten sollte langsam auslaufen, indem das zweite als experimentelles Merkmal eingeführt und dann ausgetauscht wird.

Das zweite und dritte Verhalten könnten ihre eigenen Siegel haben, wie &! und &# oder vielleicht +% ... -% oder so. Aber für den wörtlichen Modus denke ich, dass eine wichtige Facette darin besteht, das Escape-Token zu vereinfachen, damit mehr Token wörtlich genommen werden. Ähnlich wie bei einem Heredoc.

Wenn die Anforderung nur das Copy-Paste-Szenario wie "Jeder Textinhalt nach diesem Operator wird in die" Standard-Shell "des Betriebssystems aufgerufen, um ausgeführt zu werden." Warum sagst du das nicht explizit mit shell ?
`` `Powerhell
PS> Shell wsl ls | weniger
PS> (Shell wsl ls * .txt) | Select-String Hallo

Es ist besser erkennbar und lesbar als kryptische Operatoren im Forth / APL-Stil.

Ich sehe absolut nicht ein, wie sich das lösen würde # 1995: Siehe https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -653174261

Das Problem mit WSL ist auch ein Problem des Verhaltens von wsl.exe (siehe https://github.com/PowerShell/PowerShell/issues/12975#issuecomment-650353021), nicht von Powershell.
(Ok, es gibt ein Problem mit Powershell, aber das ist # 1995)

Oh, und ist das nicht fast ein Duplikat von https://github.com/PowerShell/PowerShell/issues/12975 ? Weil ich im Wesentlichen meistens wiederholen kann, was ich dort erwähnt habe:

@bitcrazed
Das Problem ist, dass das Fehlen einer Fähigkeit, einen Teil einer Befehlszeile abzugrenzen, der varbatim an den empfangenden Befehl / das empfangende Skript übergeben werden soll, die Benutzer ständig auslöst.
Was meinst du mit "Teil einer Befehlszeile, die varbatim übergeben werden soll"? Meinen Sie 1. Übergeben Sie eine Folge von Zeichen wörtlich an die aufgerufene ausführbare Datei, sodass die aufgerufene ausführbare Datei diese Folge von Zeichen in einem Element ihres Argumentarrays enthält (z. B. sind diese Zeichen dann in `argv [1]` in [main verfügbar) ] (https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs-2019#command-line-arguments)) Oder meinen Sie 2. Einfügen eine wörtliche Zeichenfolge in den Parameter [`lpCommandLine` von` CreateProcess`] (https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#parameters) - - Wenn Sie (1.) meinen, dann funktioniert es im Wesentlichen bereits:
PS /home/User> /bin/echo 'cd / && ls . | cowsay'
cd / && ls . | cowsay
PS /home/User>
(Mit Ausnahme des Problems der eingebetteten Anführungszeichen, wie unter https://github.com/PowerShell/PowerShell/issues/1995 beschrieben) Man könnte argumentieren, dass das Hinzufügen einer einzeiligen Zeichenfolge einige Anwendungsfälle verbessern würde, aber ich denke, Darum geht es in dieser Ausgabe nicht wirklich. Da dies bereits mehr oder weniger funktioniert, haben Sie vermutlich (2.) --- Wenn Sie (2.) meinen, lassen Sie mich meine Meinung dazu auf etwas dramatische Weise äußern: Bitte, bitte, bitte nicht hinzufügen spezielle Syntax dafür. Dies ist im Grunde das, was `-%` versucht hat, was auch niemals implementiert werden sollte. Warum bin ich so stark dagegen? 1. Es handelt sich nur um ein Windows-Problem. Wenn Sie also eine Syntax hinzufügen, hat Powershell unter Windows eine andere Syntax als unter Linux. (Oder die Syntax wird unterstützt, ist aber völlig bedeutungslos, wie dies derzeit bei `-%` der Fall ist.) 2. Wenn die von Microsoft veröffentlichte Hauptbefehlszeilen-Shell unter Windows eine erstklassige Funktion hinzufügt (über eine spezielle Syntax im Gegensatz zu via ein Cmdlet) zum Aufrufen von ausführbaren Dateien, die nicht den typischen [Befehlszeilen-Parsing-Regeln] entsprechen (https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs) -2019 # parsing-c-Befehlszeilenargumente) (Wenn das Tool den typischen Regeln folgt, brauchen Sie (2.) nicht, können Sie normalerweise (1.) besser verwenden), dann ermutigt dies die Autoren der Befehlszeile Tool, um diese Regeln nicht zu befolgen, was nur die ["Windows-Befehlszeilenanarchie"] verschlechtert (https://stackoverflow.com/a/4094897/2770331). Je weniger Leute den typischen Regeln folgen, desto schwieriger ist es, externe ausführbare Dateien programmgesteuert aufzurufen oder generell plattformübergreifenden Code zu schreiben. Daher denke ich definitiv, dass Programmautoren ermutigt werden sollten, diese typischen Regeln zu befolgen. 3. Ich bezweifle stark, dass dies ein häufiger Anwendungsfall ist.> Und dies ist nicht nur ein Problem, das die WSL betrifft: Es betrifft auch die wt-Befehlszeilenaufrufe von Windows Terminal und viele andere Tools. Können Sie einige Beispiele hinzufügen, bei denen solche Probleme auftreten? Denn im Fall von WSL würde ich sagen, dass das Parsen der Befehlszeile durch WSL einfach [fehlerhaft] ist (https://github.com/Microsoft/WSL/issues/1746) (das Problem betraf "bash.exe", aber die Situation ist es Standardmäßig nicht besser mit `wsl.exe`) im Standardfall - Ich würde jedes Tool in Betracht ziehen, das nicht den typischen [Befehlszeilen-Parsing-Regeln] (https://docs.microsoft.com/en-us/) entspricht. cpp / cpp / main-function-befehlszeilenargumente? view = vs-2019 # parsing-c-befehlszeilenargumente) gebrochen, aber das Standardverhalten von WSL ist IMHO nicht einmal richtig dokumentiert ... Ich sagte das "default" "Das Verhalten von" wsl.exe "ist fehlerhaft. Beim Schreiben dieser Antwort ist mir aufgefallen, dass sich" wsl.exe "bei Verwendung von" -e "tatsächlich wie erwartet zu verhalten scheint:
PS C:\> wsl -e bash -c 'cd / && ls . | cowsay'
 _______________________________________
/ acct bin boot cache cygdrive data dev \
| etc home init lib lib64 lost+found    |
| media mnt opt proc root run sbin snap |
\ srv sys tmp usr var                   /
 ---------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
PS C:\>
Das einzige, was für diesen expliziten Anwendungsfall fehlt, ist IMO, ein Parameter für `wsl.exe`, um die Standard-Shell mit den Befehlszeilenargumenten aufzurufen, die wie in jeder anderen normalen Exe analysiert werden.

Die einzige Sache in Bezug auf "Teil einer Befehlszeile, die varbatim an den empfangenden Befehl / das empfangende Skript übergeben werden soll", die mit diesem Operator verbessert werden würde, wäre die Bedeutung (2.) oben, und wie bereits erwähnt, denke ich, dass die Entwicklung in diese Richtung ist Schlecht.

Wenn Sie nur eine Verknüpfung zum Kopieren und Einfügen vorhandener Befehlszeilen verwenden möchten, fügen Sie ein Cmdlet hinzu (z. B. Invoke-Shell , das unter Linux bash -c und unter cmd /c aufruft Fenster?
Dann könnten Sie einfach tun

Invoke-Shell @'
whatever existing comandline containg '"quotes"' or whatnot
'@

Wenn es sich um eine Zeile handeln muss, fügen Sie eine einzeilige Hier-Zeichenfolgen-Syntax hinzu. Implementieren Sie jedoch keinen speziellen Operator, der nicht richtig gefunden werden kann und nur alles kompliziert.

Und bitte geben Sie # 1995 dafür nicht auf!

+10 Punkte für die Verwendung des Wortes "Siegel".

Das Schöne an &! gegenüber shell ist, dass "shell" ein allgemeines englisches Wort ist, daher haben die Leute möglicherweise einen vorhandenen Befehl namens shell. (Tatsächlich habe ich sowohl eine Funktion namens "shell" als auch eine Funktion namens "n" (daher mag ich &n ).)

@oising : & --% ist nett, außer dass einer der ausdrücklichen Zwecke der vorgeschlagenen Funktion darin besteht, zu bedeuten, dass | zu etwas wird, das an die native Shell übergeben wird. Nicht etwas, das Sie zurück in die PowerShell-Analyse bringt. Und wenn wir die Priorität der Pipeline für & --% anders machen als für foo $blah --% more args , würde ich das verwirrend finden ... was wäre, wenn Sie & $foo --% more args | blah tun würden? Meiner Meinung nach ist das vorgeschlagene &! so unterschiedlich, dass es einen anderen Operator rechtfertigt. (Anders ausgedrückt: Die vorgeschlagene Funktionalität ist verwandt, unterscheidet sich jedoch von der Kombination von & und --% .)

Diese Funktion ist mehr als nur für "neue PowerShell-Benutzer, die Bash kennen" und "Benutzer, die nicht alle komplexen Anführungszeichen / Escape-Regeln kennen". Es ist auch für die erfahrensten Benutzer gedacht, die nur einen Befehl aus StackOverflow kopieren / einfügen möchten, ohne sich mit allem beschäftigen zu müssen. Das passiert mir ziemlich oft - ein Produkt, an dem ich arbeite, enthält eine Menge Dokumentation darüber, wie man Dinge macht, und die Verkehrssprache lautet cmd.exe-Befehle - also muss ich mich mit der Korrektur von% VARIABLE_REFERENCES% usw. befassen .; Es wäre viel schöner, &! einzugeben und dann einzufügen. Wenn dies implementiert ist, werde ich es wahrscheinlich als "StackOverflow-Operator" bezeichnen. : D.

@ vexx32 : Dies ist nicht der

@TSlivede : Ich stimme zu, dass # 1995 getrennt ist. Ich bin jedoch nicht einverstanden mit "Es ist nur ein Windows-Problem": Das Kopieren und Einfügen plattformspezifischer Befehle aus StackOverflow gilt für alle Betriebssysteme gleichermaßen. (In der Tat wäre dies sehr hilfreich für viele Leute wie mich, die auf einer Plattform stärker sind als auf einer anderen - ich bin mit Windows-Technologien besser vertraut, und unter Linux neige ich dazu, viel von SO zu kopieren und einzufügen.)

Ob über einen neuen Operator &! oder einen Befehl Invoke-Shell , ich habe keine so starken Gefühle wie andere ... Ich sympathisiere mit Leuten, die sich darüber beschweren, dass die Operatoren schwer zu suchen sind usw., aber ich mag die Prägnanz von &! .

@jazzdelightsme Dies ist nicht "ein weiteres Werkzeug in der Toolbox", sondern "ein weiterer unglaublich spezifischer Schraubendreheraufsatz für einen Schraubendreher, der bereits etwa 3-5 hat, die derzeit bereits _okay_ sind und noch verbessert werden könnten".

@jazzdelightsme Der Teil "Nur Windows" bezog sich möglicherweise eher auf das Problem, bei dem ich diese Aussage ursprünglich veröffentlicht habe - ja, OK, dies ist kein genaues Duplikat dieses Problems.

Wenn # 1995 nicht aufgegeben wird und die hier angeforderte Funktion nicht über eine spezielle Syntax, sondern über ein Commandlet (oder eine neue Syntax für eine einzeilige Zeichenfolge, die nicht nur für diesen speziellen Fall überall funktioniert) aufgelöst wird, bin ich tatsächlich dafür Hinzufügen dieser Funktionalität. (Oder vielleicht nicht "dafür", sondern "das ist für mich völlig akzeptabel")

Eine andere Idee: Wenn es sich bei diesem Problem tatsächlich um das Kopieren und Einfügen vollständiger Befehle handelt, die für native Shells vorgesehen waren, könnte PsReadLine möglicherweise eine Funktion hinzufügen, um einen Befehl grob vom Bash- oder Cmd-Stil in Powershell zu übersetzen. Die Übersetzung kann durch eine spezielle Tastenkombination oder automatisch ausgelöst werden, wenn beim Ausführen eines Befehls ein Analysefehler auftritt.

Dies hätte den zusätzlichen Vorteil, dass Benutzer die Syntax von Powershell lernen.

In Bezug auf die Erkennbarkeit: PsReadLine kann immer für kurze Zeit eine Meldung anzeigen, dass die bestimmte Tastenkombination verwendet werden kann, wenn syntaktisch inkorrekter Inhalt (der nach der Übersetzung gültig wäre) eingefügt wird.

Aber ich konnte verstehen, ob dies etwas zu komplex war, um es zu realisieren.

@jazzdelightsme hast du mit @joeyaiello gesprochen? Ich dachte nur, er nannte es StackOverflow operator was mich zusammenzucken ließ, obwohl es sehr wahr ist

@ SteveL-MSFT Nein, ich habe mit niemandem darüber gesprochen. Ich bin unabhängig bei "StackOverflow operator" angekommen. xD

@jazzdelightsme

... mit der Ausnahme, dass einer der ausdrücklichen Zwecke des vorgeschlagenen Merkmals darin besteht, dass | wird etwas, das an die native Shell übergeben wird; Nicht etwas, das Sie zurück in die PowerShell-Analyse bringt.

Ich bin nicht sicher, ob Sie meinen Vorschlag verstanden haben. Was Sie sagen, ist genau das, was ich sage. Lassen Sie mich mit einigen strategisch großgeschriebenen Hinweisen ausführlicher sein: D.

# NON-OPTIMAL SOLUTION: run "ls" in wsl, return results to powershell, then pipe to wsl again, to "grep."
& wsl ls | wsl grep -i "foo"  

# NEW SOLUTION: the entire pipeline/string, INCLUDING THE PIPE SYMBOL, right of wsl will be executed in wsl. 
& --% wsl ls | grep -i "foo"

# NEW SOLUTION: the entire pipeline AS ABOVE is sent to WSL, then the results are returned to powershell
# and piped, in powershell, to foreach-object
(& --% wsl ls | grep -i "foo") | % { "match {0}" -f $_ }

# NEW SOLUTION: allow placing --% beyond first argument
# pass everything right of --% to the command in $wsl 
$wsl = gcm wsl
& $wsl --% ls | grep -i "foo"

# or

& wsl --% ls | grep -i "foo"

# NEW SOLUTION: execute multiline pasted code without powershell parsing.
& --% @'
(pasted multiline code)
@'

Was vermisse ich hier? Wenn die Pipe nach --% es nur ein weiteres dummes Zeichen: ein Argument. Es gibt keine Probleme mit der Priorität des Operators. Wenn es in einer verschachtelten Pipeline oder einem Unterausdruck enthalten ist, beginnen Sie erneut mit dem Parsen nach Powershell.

Was wäre, wenn du & $ foo -% mehr Argumente | tust? bla?

Nun, das würde versuchen, das Argument in $foo auszuführen und "more" , "args" , "|" und "blah" zu übergeben, um als was auch immer behandelt zu werden in $foo will. Die Regeln scheinen mir einfach zu sein, aber zugegebenermaßen benutze ich Powershell / Monad schon lange. Dies ist keine wesentliche Änderung von & da & $foo --% ... --% wie jedes andere Argument behandelt. Wenn wir dieses Siegel nicht schändlich an anderer Stelle verwendet haben, sollten wir in Sicherheit sein.

Gedanken?

Ich habe das oben Gesagte mehrfach bearbeitet, um die teilweise Linienauswertung, die mehrzeilige Auswertung und die variable Positionierung von --% abzudecken. Alles ohne neue Siegel, Operatoren oder seltsame Syntax einzuführen. Ich denke, das deckt es ab. Das einzige ist: Kann der Parser damit umgehen? Was denkst du @lzybkr @JamesWTruher @BrucePay ?

Hören Sie, hören Sie, @TSlivede.

Verzeihen Sie mir, dass ich stumpf bin, aber wir haben lange über diese Themen gesprochen:

Was diese Ausgabe vorschlägt, läuft auf magisches Denken hinaus:
Es hängt mit dem unglücklichen, inhärent problematischen "Stop-Parsing-Symbol" --% , das in seiner aktuellen Form unter Windows nur begrenzt verwendet werden kann (siehe https://github.com/MicrosoftDocs/PowerShell- Docs / Issues / 6149) und auf _Unix-ähnlichen Plattformen_ praktisch nutzlos (siehe https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4963).
--% hätte niemals umgesetzt werden dürfen, und dieser Vorschlag sollte auch nicht wie dargestellt umgesetzt werden.

Das magische Denken ist eine konzeptionell durcheinandergebrachte Verschmelzung zweier Ideen:

  • (a) "Ich möchte durch Leerzeichen getrennte Token an ein externes Programm übergeben, um Barwörter zuzulassen, ohne mich um Shell-Metazeichen wie & oder | kümmern zu müssen." - Siehe https://github.com/PowerShell/PowerShell/issues/12975#issuecomment -646276628

    • Natürlich steht eine solche Funktion im Widerspruch dazu, solche Befehle als Teil einer Pipeline oder mit Pipeline-Verkettungsoperatoren ( && und || ) zu verwenden und zusätzliche Befehle in derselben Zeile zu platzieren. nach ; - und nicht zuletzt in der Lage sein, _PowerShell-Variablen und -Ausdrücke_ in der Befehlszeile zu verwenden.
  • (b) "Ich möchte, dass meine Befehlszeile so behandelt wird, wie ich es von der Shell gewohnt bin, die ich normalerweise benutze (oder von der Shell, für die die Befehlszeile ursprünglich geschrieben wurde)", die natürlich der nicht zitierten Verwendung von Zeichen unterliegt B. & oder | , die als Metazeichen interpretiert werden und daher eine _syntaktische_ Bedeutung haben.

    • Ein typisches Beispiel: Weder cmd noch /bin/sh (oder eine POSIX-ähnliche Shell wie bash ) würden wsl ls | less auf eine Weise akzeptieren, die ls überschreitet | und less _verbatim_ bis zu wsl - alle würden _unquoted_ | als _Ihr_Pipe-Symbol interpretieren.

Diese Ideen stehen im Widerspruch zueinander und erfordern unterschiedliche Lösungen - für die keine spezielle Syntax per se erforderlich ist:

  • Die Lösung für (a) besteht darin, die Idee aufzugeben und stattdessen die eigene, vorhandene Syntax von PowerShell zu verwenden, um sicherzustellen, dass Metazeichen _verbatim_ verwendet werden - was ausnahmslos _quoting_ oder _escaping_ erfordert:

    • Entweder: Barwörter in Anführungszeichen (Token, die nicht in Anführungszeichen eingeschlossen sind), die Metazeichen als Ganzes enthalten: wsl ls '|' less
    • Oder: ` -scape Metazeichen _individuell_ in Barwörtern: wsl ls `| less
    • Oder: Verwenden Sie ein _array [Variable] _, um die Argumente _verbatim_ zu übergeben: $wslArgs = 'ls', '|', 'less'; wsl $wslArgs
    • Natürlich zeigt # 1995 , dass bedauerlicherweise dieser Ansatz ist und immer gebrochen ist , wenn es um Argumente geht , die _embedded_ haben " _when Aufruf externe programs_ - _Diese Bedürfnisse addressed_ sein: siehe unten.
  • Die Lösung für (b) besteht darin, die Shell, die ich normalerweise benutze, tatsächlich aufzurufen und die Befehlszeile als einzelne Zeichenfolge zu übergeben.

    • Wie @TSlivede vorschlägt, ist ein Invoke-Shell Cmdlet, an das Sie die Befehlszeile als einzelne Zeichenfolge übergeben und das die plattform-native Shell aufruft, die Lösung - und wir haben bereits eine Syntax für wörtliche Zeichenfolgen ( '...' oder seine Here-String-Variante @'<newline>...<newline>'@
    • Meine Empfehlung ist, es Invoke-NativeShell zu nennen und /bin/sh - nicht bash - auf Unix-ähnlichen Plattformen zu nennen.

      • Außerdem schlage ich vor, ins als prägnanten Alias ​​zu definieren.

    • Dieser Ansatz löst alle konzeptionellen Probleme mit --% und dem vorliegenden Vorschlag:

      • Es macht sehr deutlich, wo der an die native Shell übergebene Befehl endet (da es sich um eine einzelne Zeichenfolge handelt), und es ermöglicht die Verwendung eines Aufrufs von Invoke-NativeShell / ins in einer regulären PowerShell-Pipeline / mit PowerShell-Umleitungen ohne Mehrdeutigkeit.

      • Sie können optional eine _expandable_-Zeichenfolge ( "...." oder deren Here-String-Variante) verwenden, um _PowerShell_-Variablenwerte in die Befehlszeile einzubetten (etwas, das --% nicht kann).


Da die Befugnisse, die zu dem Schluss gekommen sind, dass # 1995 nicht behoben werden kann, damit die Abwärtskompatibilität nicht beeinträchtigt wird, schließlich alle vorhandenen Problemumgehungen nicht mehr funktionieren, siehe https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606 - the Das Beste, was wir tun können, ist, das Opt-In für das richtige Verhalten mit einer Syntax zu unterstützen, die so kurz wie möglich ist, um den Schmerz zu minimieren, dass Sie sich aus dem Weg gehen müssen, um das Verhalten zu erhalten, das immer das gewesen sein sollte Standard.

Aus diesem Grund: Eine spezielle Syntax wie &! sollte nur als Opt-In bereitgestellt werden, um die ordnungsgemäße Übergabe von Argumenten an externe Programme zu erhalten, um # 1995 zu adressieren , basierend auf dem in @TSlivedes RFC vorgeschlagenen Verhalten - siehe https://github.com/PowerShell/PowerShell-RFC/pull/90. Wenn eine zukünftige PowerShell-Version jemals die Abwärtskompatibilität unterbrechen darf, sollte ein direkter Aufruf / Aufruf mit & dieses Verhalten aufweisen.

Aus Anwendersicht bedeutet dies: Alles, was ich tun muss, ist, die Syntaxanforderungen von PowerShell zu erfüllen - siehe https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -640711192 - und ich kann mich auf PowerShell verlassen Übergeben Sie die resultierenden wörtlichen Token zuverlässig an das Zielprogramm - _dies ist ein Kernmandat jeder Shell, das PowerShell in Bezug auf externe Programme leider nie erfüllt hat_.

Zum Beispiel sollte der folgende Befehl - der derzeit nicht wie beabsichtigt funktioniert (wenn er direkt oder mit & aufgerufen wird - siehe dieses Dokumentproblem ) - dann funktionieren:

PS> &! bash -c 'echo "one two"'
one two   # Currently (invoked with & instead of &! or with neither) prints only 'one', 
          # because PowerShell passes the entire argument (brokenly) as  "echo "one two"",
          # which the target program sees as *2* verbatim arguments `echo one` and `two`

Wer in aktuellen / früheren PowerShell-Versionen nach einer Problemumgehung sucht, kann unter https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -303345059 eine einfache Funktion finden, die die meisten Anwendungsfälle abdeckt (Einschränkungen unter https: //) github.com/PowerShell/PowerShell/issues/1995#issuecomment-649560892) und eine ausführlichere Version unter https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606

PS: Das Entdeckungsproblem von (nicht nur symbolbasierten) Operatoren ist ein separates Problem, das es wert ist, selbst angegangen zu werden: Die Lösung besteht darin, Get-Help zu verbessern, um die Suche nach Operatoren zu ermöglichen - siehe auch # 11339 Links zu einer Zwischenlösung.

Da die Befugnisse, die zu dem Schluss gekommen sind, dass # 1995 nicht behoben werden kann, damit die Abwärtskompatibilität nicht beeinträchtigt wird - schließlich würden alle vorhandenen Problemumgehungen brechen, siehe # 1995 (Kommentar) -, können wir das Opt-In für das Beste am besten unterstützen Richtiges Verhalten mit einer Syntax, die so prägnant wie möglich ist, um den Schmerz zu minimieren, dass Sie sich aus dem Weg gehen müssen, um das Verhalten zu erhalten, das immer die Standardeinstellung sein sollte.

@ mklement0 Ich denke, die https://github.com/PowerShell/PowerShell/issues/1995 und das wurde abgelehnt. Für mich klingt es nach Wunschdenken. Liege ich falsch?

@ AndrewSav , wir haben ursprünglich nur ein Opt-In über die Konfigurations- oder Präferenzvariable besprochen.

@Joeyaiello sagte jedoch Folgendes unter https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -653164987, als er das Problem schloss (Hervorhebung hinzugefügt):

Alles, was wir hier berühren, ist einfach eine zu große Veränderung, und wir sollten das Problem mit einem neuen Bediener angehen

Ein neuer Operator ist eine Art Opt-In, aber hoffentlich ein unproblematischer (obwohl die Notwendigkeit dafür unglücklich ist, aber das ist der Preis für die Abwärtskompatibilität).

Während dieser Vorschlag angeblich auch # 1995 löst, ist dies nicht der Fall, da er (hauptsächlich) darauf abzielt, die Syntax anderer Shells zu berücksichtigen. Angesichts der Tatsache, dass beide Probleme unabhängig voneinander gelöst werden sollten, war mein Vorschlag folgender:

  • Lassen Sie uns einen neuen Operator einführen, aber verwenden Sie ihn ausschließlich, um # 1995 (über https://github.com/PowerShell/PowerShell-RFC/pull/90) zu beheben, dh um PowerShells _own_ (nicht andere Shell-emulierende) Fehler zu beheben Umgang mit Argumenten, die an externe Programme übergeben werden.

    • Neuer Code sollte dann beim Aufrufen externer Programme immer &! (oder eine andere Form, für die wir uns entscheiden) anstelle von & verwenden (Sie können sich vorstellen, dass & für externe Programme veraltet ist). .
    • Beim Aufrufen von _PowerShell_-Befehlen sollte sich &! wie & verhalten (es gab nie ein Problem), wodurch die konsistente Verwendung von &! .
  • Um das Problem der Wiederverwendung von Befehlszeilen aus anderen Shells zu lösen, implementieren wir ein Invoke-NativeShell / ins Cmdlet, das die plattformgerechte native Shell aufruft ( cmd /c unter Windows , /bin/sh -c auf Unix-ähnlichen Plattformen), wobei die Befehlszeile als _single string_ übergeben wird.

    • Im einfachsten Fall funktioniert eine reguläre Zeichenfolge in einfachen Anführungszeichen, wenn in der Befehlszeile kein ' vorhanden ist. z.B:

      ins 'ls | cat-n '

    • Mit ' können sie entweder verdoppelt werden; z.B:

      ins 'echo' 'hi' '| cat-n '

    • oder wenn der Wunsch besteht, die Befehlszeile überhaupt nicht berühren zu müssen, kann eine Here-Zeichenfolge verwendet werden (wie bereits von @TSlivede gezeigt) - obwohl diese Syntax etwas umständlicher ist, sollte sie immer funktionieren:

      ins @ '
      echo 'hi' | Katze -n
      '@

    • Durch die Verwendung einer _expandierbaren_ (interpolierenden) Zeichenfolge ( "..." oder @"<newline>...<newline>"@ ) haben Sie außerdem die Möglichkeit, _PowerShell_-Variablen in die an die native Shell übergebene Zeichenfolge aufzunehmen (etwas, das --% unterstützt nicht):

      $ foo = 'hi'
      ins @ "
      echo '$ foo' | Katze -n
      "@

    • Hinweis:

      • Invoke-NativeShell Cmdlet wäre ein ziemlich dünner Wrapper um cmd /c / /bin/sh -c Anrufe, die Sie natürlich direkt selbst tätigen könnten, aber es hat den Vorteil, dass Sie nicht müssen Kennen Sie den spezifischen Namen der ausführbaren Datei und die Syntax für die Befehlszeilenübergabe.
      • Als potenziell vorteilhafter Nebeneffekt können Sie allgemeine Parameter wie -OutVariable und darüber nachdenken, ob die -Error* -Parameter auf _stderr_ einwirken sollen (allerdings aus Symmetriegründen Invoke-Command sollte dann auch, was eine bahnbrechende Änderung wäre).

        • Da POSIX-ähnliche Shells mehrere Argumente mit -c die ersten den auszuführenden Code und die nachfolgenden die Argumente, die an diesen Code übergeben werden sollen, beginnend mit $0 (!) - zB bash -c 'echo $@' _ 1 2 3 - optionale zusätzliche Argumente müssen ebenfalls unterstützt werden.

        • Da die plattformeigene Shell aufgerufen wird, sind solche Aufrufe aufgrund der grundlegenden Unterschiede zwischen cmd und sh per Definition nicht portierbar.Aus diesem Grund ist es so wichtig, PowerShells _own_ argument-passing (# 1995) zu korrigieren, da es dann eine zuverlässige, wirklich plattformübergreifende Methode zum Aufrufen externer Programme bietet.


Das letzte Stück des Puzzles ist _Dokumentation_.

https://github.com/MicrosoftDocs/PowerShell-Docs/issues/5152 ist ein langjähriger Vorschlag, ein konzeptionelles Hilfethema zum Aufrufen externer Programme (z. B. about_Native_Calls ) zu verfassen, das an einem einzigen Ort ausgeführt werden sollte. _alle_ Aspekte abdecken, die für den Aufruf externer Programme relevant sind; Abgesehen von dem, was dort bereits behandelt wird (Syntaxprobleme aufgrund zusätzlicher Metazeichen, Fehlen einer Rohbyte-Pipeline, Nichtintegration in die PowerShell-Fehlerbehandlung, ...), sollten auch die vorliegenden Probleme erörtert werden:

  • Wie das Übergeben von Argumenten mit eingebetteten Argumenten " (und _empty_) in früheren Versionen (hoffentlich in Kürze) gebrochen wird (derzeit in einem separaten Vorschlag unter https://github.com/MicrosoftDocs/PowerShell-Docs beschrieben) / Issues / 2361) und wie man das umgeht (manuelles \ -Entweichen oder Verwenden einer der Hilfsfunktionen von # 1995, von denen eine kurz genug ist, um direkt in das Thema aufgenommen zu werden).

  • Wie zukünftig nur &! verwendet werden sollte, um native (externe) ausführbare Dateien aufzurufen, um eine ordnungsgemäße Argumentübergabe sicherzustellen.

  • Wie Invoke-NativeShell / ins verwendet werden kann, um Befehlszeilen auszuführen, die für die plattformeigene Shell geschrieben wurden, wie sie ist.

Interessant, heute können Benutzer:

  1. direkter Aufruf
  2. & Operator
  3. Start-Prozess

Es ist überhaupt nicht klar, dass Benutzer verwenden sollten.

Nach dem Beheben eines fehlerhaften Verhaltens haben Benutzer die folgenden Funktionen:

  1. direkter Aufruf
  2. & Operator
  3. Start-Prozess
  4. &! Operator
  5. Start-ProcessV2

Ist es nicht zu viel für die Shell, ping 127.0.0.1 auszuführen?

Ja, das ist mein Hauptanliegen, noch einen weiteren Operator @iSazonov hinzuzufügen. Es ist schon verwirrend und das wird nicht helfen. Die Dokumentation ist nur ein Teil des Puzzles. Selbst wenn wir eine anständige Dokumentation haben, wird die bloße Tatsache, dass es fünf verschiedene Wege gibt, die sich jeweils ein bisschen anders verhalten, immer Verwirrung stiften.

Wenn uns die UX überhaupt wichtig ist, sollten wir uns dafür entscheiden, bestehende Fälle zu verbessern, anstatt noch mehr hinzuzufügen.

Wenn uns die UX überhaupt wichtig ist, sollten wir uns dafür entscheiden, bestehende Fälle zu verbessern, anstatt noch mehr hinzuzufügen.

Was Sie nicht können, weil Sie abwärtskompatibel sein müssen. Ich fühle, wir gehen im Kreis. https://github.com/PowerShell/PowerShell/issues/1995 war das und es wurde aus genau diesem Grund geschlossen.

@iSazonov :

Der bestehende Mangel an Anleitung muss behoben werden, auch wenn wir keinen neuen Operator einführen:

Kurz zusammengefasst:

  • Verwenden Sie Start-Process , um _console_ (Terminal) -Programme aufzurufen (es sei denn, Sie möchten sie - was nur unter Windows verfügbar ist - _in einem neuen Fenster_ ausführen).

  • Direkter Aufruf und Aufruf über & sind _äquivalent_; (abgesehen von Skriptblöcken), & wird immer nur aus _syntaktischen_ Gründen benötigt, wenn der Befehlsname oder Pfad _quotiert_ ist und / oder _variable Referenzen_ enthält.


Nach dem Beheben eines fehlerhaften Verhaltens haben Benutzer die folgenden Funktionen:
direkter Aufruf

  • & Operator
  • Start-Prozess
  • &! Operator
  • Start-ProcessV2
  • Nein, es wird kein Start-ProcessV2 , nur einen neuen Parameter, -IndividualArguments - siehe https://github.com/PowerShell/PowerShell/issues/5576#issuecomment -552124719

  • Ja, es wird &! (oder welches Symbol auch immer wir uns entscheiden), aber es ersetzt effektiv sowohl & als auch den direkten Aufruf_ (zumindest für _externe Programme_, aber Sie können es verwenden mit _all_ Befehlsformularen) - die leicht zu kommunizieren sein sollten.

Ist es nicht zu viel für die Shell, ping 127.0.0.1 auszuführen?

ping 127.0.0.1 wird weiterhin funktionieren, aber wenn Sie möchten, dass bash -c 'echo "one two"' funktioniert, müssen Sie verwenden
&! bash -c 'echo "one two"'

Das ist bedauerlich, aber - da das Verhalten des direkten Aufrufs / & behoben werden kann - scheint es mir die beste Lösung zu sein.

Glücklicherweise ist es eine einfache Regel, sich Folgendes zu merken: _Wenn Sie vorhersehbare Argumente an externe Programme übergeben möchten, verwenden Sie &! _

Alternative Formen der Anmeldung - Konfiguration (über powershell.config.json ) oder eine Präferenzvariable - sind viel problematischer:

  • Das dynamische Scoping von Präferenzvariablen macht es zu einfach, das Opt-In versehentlich auf Legacy-Code anzuwenden, der nicht dafür entwickelt wurde - siehe https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -650262164

  • Die Konfiguration würde für Sitzungen als Ganzes gelten, was ein Deaktivieren insgesamt unmöglich macht.

  • In beiden Fällen ist eine Aktion erforderlich, die von einem bestimmten Anruf getrennt ist, und wenn Sie sich einen bestimmten Anruf ansehen, erfahren Sie nicht, wie er sich verhält. Im Gegensatz dazu lässt &! keinen Zweifel daran, welches Verhalten auftreten wird.

Für den Endbenutzer sieht es genauso schrecklich aus, als hätten wir einen Taschenrechner gestartet und dort 5 Schaltflächen für den Additionsvorgang gefunden. 😄

Wenn wir es so stark verbessern möchten, dass wir für solche Komplikationen bereit sind, reicht dies möglicherweise aus, um eine wichtige Änderung (für direkten Aufruf und & ) zu akzeptieren, um das Verhalten zu vereinfachen und V8.0 freizugeben.

Wenn unser Wunsch nicht so stark ist, ist es möglich, die Diagnose ausreichend zu verbessern ( prog.exe param1 param2 -Whatif und dasselbe für Start-Process -WhatIf), so dass angezeigt wird, was das Programm testargs anzeigt, und Empfehlungen dazu Flucht korrekt durchführen.

Der Skriptentwickler kann es sich leisten, Zeit mit dem Testen und Verwenden des Debuggers zu verbringen. In einer interaktiven Sitzung möchte jeder Benutzer Einfachheit, Kürze und Klarheit, und dies überwiegt alles.

@iSazonov :

Dann reicht dies möglicherweise aus, um eine wichtige Änderung zu akzeptieren (für direkten Aufruf und &), um das Verhalten zu vereinfachen und V8.0 freizugeben

Sie haben sicherlich meine Stimme - aber unterstützen die Mächte, die diesen Plan unterstützen ? [_update_ - siehe # 13129]
Wenn wir eine Änderung dieser Größenordnung vornehmen, können wir endlich das gesamte historische Gepäck in Angriff nehmen - siehe Nr. 6745

Wenn wir es so sehr verbessern wollen

Es gibt zwei gute Gründe, es so sehr zu wollen:

  • Um jahrelange Schmerzen aufgrund gebrochener Argumente zu beenden - wie # 1995, viele verwandte Probleme und unzählige Fragen zum Stapelüberlauf belegen.

  • Weil - wie bereits mehrfach erwähnt - das Aufrufen externer Programme mit Argumenten ein Kernauftrag jeder Shell ist.

In der Vergangenheit machte der Mangel an leistungsfähigen externen CLIs die Unfähigkeit, sie ordnungsgemäß aufzurufen, in den Tagen nur unter Windows weniger problematisch, und Benutzer, die im ummauerten Garten von PowerShell blieben (nur PowerShell-native Befehle aufriefen), spürten den Schmerz nicht. Aber die Zeiten haben sich geändert:

Wir leben im Zeitalter einer Vielzahl plattformübergreifender CLIs, die für Entwicklungs-, CI- und Bereitstellungsaufgaben von entscheidender Bedeutung sind.
Wenn PowerShell als Shell ernst genommen werden soll, muss dieses Problem behoben werden.


Selbst wenn eine erlaubte Abwärtskompatibilität v8.0 auftritt (Daumen drücken), könnte ihre Veröffentlichung noch lange dauern, und wir brauchen auch eine Zwischenlösung.

So sehr die vorgeschlagene Verbesserung der Dokumentation erforderlich ist, glaube ich nicht, dass sie allein die Schmerzen ausreichend lindern, da es keine sofort verfügbare Laufzeitlösung gibt.

Ich verstehe, dass der Operator hässlich ist, aber eine nicht brechende Lösung erfordert zusätzliche Syntax (mehr zu tippen), unabhängig von ihrer Form.

Eine Alternative zu &! , die weniger hässlich ist, einfacher zu tippen ist und die Sprache nicht "verschmutzt", besteht darin, eine integrierte _Funktion_ mit einem präzisen Namen zu versenden, wie z. B. die Funktion iep ( I nvoke- E xternal P rogram), das alle hier gezeigten Probleme maskiert.
(Beachten Sie, dass wir bereits Funktionen ausliefern, die entweder andere Funktionen einschließen (z. B. pause ) oder cmd.exe Benutzer aufnehmen (z. B. cd.. [sic]).)

Damit bash -c 'echo "one two"' richtig funktioniert, würden Sie iep bash -c 'echo "one two"'
Oder um ein realistischeres Szenario zu verwenden:

# Pass JSON to an external utility (Unix example)
# Without `iep`, the embedded double quotes are lost and *broken JSON* is passed.
iep /bin/echo '{ "foo": "bar" }'

Wiederum sprechen wir letztendlich von einem Kompromiss im Dienst zur Wahrung der Abwärtskompatibilität - dies ist für Endbenutzer unweigerlich ein Ärgernis.


Verbessern Sie die Diagnose (prog.exe param1 param2 -Whatif und dasselbe für Start-Process -WhatIf), sodass angezeigt wird, welche Testargs-Programme angezeigt werden, und Empfehlungen zur korrekten Durchführung der Escape-Funktion.

Da der direkte / & -basierte Aufruf kein Cmdlet-Aufruf ist und angenommen wird, dass alle Argumente Pass-Through-Argumente sind, bedeutet die Behandlung von -WhatIf als gemeinsamen Parameter eine grundlegende Änderung (wie unwahrscheinlich dies auch sein mag) ist in der Praxis).

Wenn Sie bereit sind, eine solche Änderung zu akzeptieren, können wir (auch) einen -DoItRight -Schalter (oder einen symbolbasierten, ähnlich --% ) als Opt-In für unterstützen das feste Verhalten - und das scheint mir schlimmer als der iep -Präfix / &! -Ansatz.

(Eine ununterbrochene Diagnoseoption wäre vermutlich die Verbesserung von Invoke-Command , die derzeit -WhatIf nicht unterstützt - wenn wir jedoch ein gut dokumentiertes Opt-In für das Richtige bereitstellen Verhalten, ich denke nicht, dass es sich lohnt, dies zu tun.)

Ich stimme @iSazonov zu, dass die Situation nicht ideal ist, um noch etwas anderes einzuführen, um etwas zu tun, das funktionieren soll, aber wie von @ mklement0 erwähnt, könnte es sehr, sehr lange
https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606 richtig?

@ mklement0 Ich bin mir nicht sicher, wie ein iep / invoke-external-Programm funktionieren könnte, da ein Cmdlet demselben Parameter-Munging unterliegt, das wir zu beheben versuchen. Dies ist ein Parser-Problem, oder? Wenn wir versuchen würden, alles in einem Cmdlet / einer Cmdlet zu verbergen, würde ich mir vorstellen, dass es schrecklich komplex wäre, da es die Absicht des Mungings / Rekonstruierens rückgängig machen müsste. Oder fehlt mir etwas?

@oising :

Um es klar auszudrücken: Die verknüpfte iep -Funktion behebt # 1995, nicht das, worum es in diesem Vorschlag in erster Linie geht (was meiner Meinung nach, wie oben dargelegt, nicht wert ist, verfolgt zu werden). Beachten Sie jedoch, dass die im OP angegebene Absicht darin besteht _also_ lösen # 1995.

1995 geht es darum, sich auf die Syntax von PowerShell (nicht auf die einer anderen Shell) verlassen zu können, um Argumente originalgetreu an externe Programme zu übergeben, wie PowerShell nach dem eigenen Parsen wörtlich gesehen hat.

Die eigene Analyse von PowerShell ist in Ordnung, und es gibt keine Probleme, solange Sie Argumente an _PowerShell_-Befehle übergeben.

Im Gegensatz dazu ist das Übergeben von Argumenten an externe Programme stark fehlerhaft, und genau das behebt iep .

Es ist defekt, weil die Argumente hinter den Szenen fehlerhaft neu zitiert und maskiert wurden, wenn die letztendlich verwendete Befehlszeile erstellt wird.
https://github.com/PowerShell/PowerShell-RFC/pull/90 schlägt eine ordnungsgemäße Lösung vor (ohne Einigung über den Umgang mit Abwärtskompatibilität und mit ein paar kleinen Fragen, die noch zu beantworten sind), die im Kern auf der Vor kurzem wurde System.Diagnostics.ProcessStartInfo.ArgumentList , um das richtige Zitieren und Entkommen für uns durchzuführen (wir übergeben nur die Sammlung von Argumenten, die sich aus PowerShells eigener Analyse ergeben haben) - was übrigens immer nur unter Windows erforderlich ist. Unter Unix gibt es vernünftigerweise keine Befehlszeile (außerhalb einer Shell), wenn Sie beim Start des Prozesses Argumente übergeben: Sie übergeben die Argumente nur wörtlich als Array.

Zusamenfassend:

  • Das Bereitstellen von iep als integrierte Funktion wäre ein Stop-Gap-Opt-In mit geringer Zeremonie, um die ordnungsgemäße Weitergabe von Argumenten an externe Programme zu ermöglichen, bis eine ordnungsgemäße Korrektur - im Kontext einer Änderung - implementiert werden kann

  • Ein Cmdlet Invoke-NativeShell ( ins ) ist die richtige Lösung - keine Notlösung -, um die Befehlszeilen einer anderen Shell (der nativen Shell) mithilfe der Syntax _its_ aufzurufen - siehe oben .

Derzeit gibt es Fälle, in denen das Ausschneiden und Einfügen eines PERL-Befehls in Python nicht wie erwartet ausgeführt wird. Stelle dir das vor. Dies muss ein großer Nachteil für all die Cutter-and-Paster da draußen sein. Ich denke, Python sollte dafür einen speziellen Operator haben.

Ich bin nicht sicher, ob Sie meinen Vorschlag verstanden haben. Was Sie sagen, ist genau das, was ich sage. Lassen Sie mich mit einigen strategisch großgeschriebenen Hinweisen ausführlicher sein: D.

# NEW SOLUTION: the entire pipeline AS ABOVE is sent to WSL, then the results are returned to powershell
# and piped, in powershell, to foreach-object
(& --% wsl ls | grep -i "foo") | % { "match {0}" -f $_ }

Was vermisse ich hier? Wenn die Pipe nach --% es nur ein weiteres dummes Zeichen: ein Argument. Es gibt keine Probleme mit der Priorität des Operators. Wenn es in einer verschachtelten Pipeline oder einem Unterausdruck enthalten ist, beginnen Sie erneut mit dem Parsen nach Powershell.

Gedanken?

Was Sie vermissen, ist die Notwendigkeit, eine schließende Klammer als Argument an ein externes Programm zu übergeben. Dies ist ein typischer DWIM-Ansatz, PowerShell ist jedoch nicht HAL.

Vielen Dank für das ausführliche Feedback an alle. Ich spreche hier wie ich. Ein paar Überpunkte, bevor ich mich näher mit dem Thema befasse:

  • Ich bin immer noch nicht mit einer vNext / 8.0 an Bord, die beschließt, eine ganz neue Litanei von Dingen zu brechen. Abwärtskompatibilität ist wichtig. Ich weiß, dass es schmerzhaft ist, und ich würde lieber in einer Welt leben, in der wir zurückgehen und jeden Fehler korrigieren könnten, den PowerShell jemals gemacht hat, viele unbeabsichtigt. Aber die überwiegende Mehrheit der PS-Benutzer ist hier nicht in Problemen, knietief in SO-Edge-Fällen. Sie haben die gleiche Problemumgehung 4-10 Jahre lang in etwas kopiert / eingefügt, sie wissen nicht unbedingt, warum es funktioniert, und es ist ihnen egal, dass wir "Fehler behoben" haben, wenn ihr Skript beim Upgrade kaputt geht. Sie wissen nur, dass "das Aktualisieren von PS mich kaputt gemacht hat und ich Updates nicht vertrauen kann", und dann werden noch mehr Benutzer ihre Updates und die Übernahme verlangsamen.
  • Ich denke, einige Leute in dieser Ausgabe projizieren möglicherweise viele verschiedene Hoffnungen und Wünsche in diese Ausgabe oder sehen sie als entweder / oder mit anderen Korrekturen. Mir ist klar, dass ich das durch das Schließen von # 1995 verschärft habe, und das tut mir leid (obwohl ich sagen werde, ich bin mir immer noch nicht sicher, ob eines tatsächlich repariert werden kann, aber wir werden es uns genauer ansehen), aber das hier ist wirklich eine "Falltür" -Lösung, die sowohl neue als auch erfahrene Benutzer schnell aus dem Gleichgewicht bringt, ohne dass die Frustration zunimmt. Wir können immer noch längerfristige Gespräche über Korrekturen an der PS-Parsing-Seite des Hauses führen, damit wir für alles eine wirklich plattformübergreifende Lösung anbieten können.

Nun Kommentare beim Lesen:

  • Ich bin besorgt über den vorgeschlagenen Wunsch, sich an #! auszurichten (entweder mit &! oder $! , da Benutzer im Standardfall den Interpreter wahrscheinlich nicht angeben werden Wenn ich so etwas wie $! net <stuff> möchte, ist net.exe nicht mein Interpreter. Und unter Nicht-Windows ist es immer noch "nativ", an sh / bash / was auch immer vorher zu übergeben Ich möchte nicht, dass Python *.py in der Zeichenfolge $! /usr/bin/python *.py oder sogar $! python *.py analysiert. Ich möchte, dass Bash das Globbing macht und dann zur Hand geht die Liste der Übereinstimmungen mit python .
  • Wie von @oising vorgeschlagen, bin ich für --% als Operator, der überlastet ist, um vorne verwendet zu werden. Die Auffindbarkeit ist ein Problem, und da wir bereits seit 10 Jahren an der Auffindbarkeit dieses Modells arbeiten ( es ist jetzt Google-fähig! Ich sage, wir machen das einfach so) (ich war derjenige, der @ SteveL-MSFT gemacht hat Setzen Sie es in seinen Vorschlag als alternative Überlegung ein. Frage: Brauchen wir hier überhaupt den Anrufer & ? Warum nicht einfach die Zeile mit --% native.exe do /some $stuff ?
  • @rjmholt : Ich habe Ihren Kommentar hier nicht ganz verstanden, aber ich gehe davon aus, dass wir wahrscheinlich nichts an dem vorhandenen Verhalten von --% ändern sollten, wenn es nach der ausführbaren Datei kommt, aber ich denke, wir können Ihr "neues" machen Verhalten "mit wo es vor der ausführbaren Datei kommt.
  • Lesen Sie dies von @jazzdelightsme und ich stimme nachdrücklich zu:

    Es ist auch für die erfahrensten Benutzer gedacht, die nur einen Befehl aus StackOverflow kopieren / einfügen möchten, ohne sich mit allem beschäftigen zu müssen.

    In meinen Augen ist dies ein großer Aspekt des Szenarios, möglicherweise der größte. Es ist nicht nur SO, es sind Dokumente aus dem Internet, die davon ausgehen, dass Sie sich in einer Bash-ähnlichen Shell befinden.

  • Unabhängig davon, ob Dinge wie # 1995 repariert werden, ist der Punkt, dass a) es einen langen Schwanz von solchen seltsamen Randfällen gibt und b) sie wirklich schwer zu reparieren sind, ohne Menschen zu brechen. Es ist nicht unbedingt für alle von ihnen zutreffend, aber wir haben es in den letzten Jahren so oft getroffen, dass mir klar ist, dass wir eine solche "Falltür" brauchen, um Menschen aus haarigen Situationen herauszuholen, in denen sie nicht abtauchen wollen das Kaninchenloch unserer verschachtelten Flucht (nur um herauszufinden, dass es einen Fehler wie # 1995 gibt, der sie daran hindert zu arbeiten, selbst wenn sie wissen , was sie wissen).

Ich denke, bei der Überprüfung der Kommentare gibt es zwei offene Fragen, die wir noch beantworten müssen:

  1. Analysieren wir weiterhin Befehlstrennzeichen und alles danach in PowerShell oder wird das Befehlstrennzeichen auch wörtlich an die native Shell übergeben?
    Ich sage, wir sollten alles wörtlich an die native Shell übergeben, da dies derzeit nicht möglich ist. Die Lösung von kann darin bestehen, dass wir weiterhin Pipes, Pipelineketten usw. Unterstützen können ., wenn Sie native und PS-Analyse mischen / abgleichen möchten (obwohl ich einige Meinungen von denen erhalten möchte, die näher am Parser sind).
  2. Was ist der Betreiber?
    Wie oben erwähnt, mag ich --% weil es bereits den Ruf hat, "einfach das zu tun, was es früher getan hat", und wir müssen die Erkennbarkeit mit etwas Neuem zurücksetzen. Ich verstehe Argumente wie @ mklement0 , dass wir, wenn wir dies tun, die technische Bedeutung von --% ändern (und @ SteveL-MSFT hat mir dieses Argument auch vorgebracht), aber ich denke nicht Die meisten Leute in freier Wildbahn denken an --% in Bezug auf "es macht das, was es früher in cmd gemacht hat". Ich denke nicht, dass es schwierig ist, diese Annahme in der Definition des Operators als "tun, was die native Shell hier tut" zu kodifizieren.

Orthogonal nur an ein anderes Szenario gedacht, das wir möglicherweise unterstützen möchten oder nicht.

Unter Windows nimmt cmd (effektiv) das aktuelle Verzeichnis in seine PATH -Suche auf. PowerShell erfordert jedoch, dass der Benutzer ein ./ vorab ausstellt, um EXE-Dateien (oder etwas in PATHEXT ) auszuführen, wenn sich das aktuelle Verzeichnis nicht in PATH .

Meine offene Frage lautet also: Wollen wir tatsächlich die gesamte Befehlszeichenfolge an cmd übergeben? Ich würde vorschlagen, dass, wenn wir das Kopier- / Einfügeszenario mit diesem Operator wirklich beleuchten möchten, die Antwort Ja lautet, da einige Dokumente / Tutorials von Drittanbietern nicht unbedingt genau beschreiben, wie die von Ihnen erwarteten Tools platziert werden sollen in die PATH . Es ist wahrscheinlich auch nicht so wichtig, aber ich wollte es hier aufzählen.

  • Ich bin immer noch nicht mit einer vNext / 8.0 an Bord, die beschließt, eine ganz neue Litanei von Dingen zu brechen.

Ja, ich weiß nicht, wie Sie diese Probleme beheben können, ohne unglaublich schwer zu behebende Wege einzuschlagen. Außerdem weiß ich nichts über euch, aber selbst ich möchte nicht all diese komplizierten Unterschiede zwischen Ordner und Parser beim Schreiben für mehrere Versionen unter einen Hut bringen müssen.

Wenn ich dagegen bin, Änderungen zu brechen, ist das normalerweise für andere Leute. Dies scheint jedoch wahrscheinlich eine PITA zu sein, mit der man sich auch für Leute befassen muss, die in ITT anwesend sind.

  • Wie von @oising vorgeschlagen, bin ich für --% als Operator, der überlastet ist, um vorne verwendet zu werden. (...) Warum nicht einfach die Zeile mit --% native.exe do /some $stuff ?

Das gefällt mir wirklich gut. Idealerweise so nah wie möglich am Aufrufen von CreateProcess / exec (plus nix env var-Behandlung und In / Out-Umleitung oder Konsolenanhang). Oder vielleicht ganz auf cmd.exe / bash zurückgreifen.

Ich bin auch nicht daran gehindert, & vor --% . Ich kann die Idee rechtfertigen, rohe --% zumindest wie eine Direktive / einen Operator in meinem Kopf zu behandeln. Was ist mit mehrzeilig? Erlauben Sie --% mit @" und "@ für Var-Ersetzungen und Unterausdrücke, und einfache Anführungszeichen @' und '@ , um das Literal zu übergeben? Ohne die hier angegebenen Zeichenfolgen ist es nur eine einzelne Zeile?

Wie @oising vorgeschlagen hat, bin ich für -% als Operator

Funktioniert bei mir.

Frage: Brauchen wir hier überhaupt den & call Operator?

Solange ich noch --% & $computedPathToNativeExe foo;@workspace mir gut. Wir müssen in der Lage sein, den Anrufbetreiber damit zu verwenden.

Ich sage, wir sollten alles wörtlich an die native Shell übergeben, da dies derzeit nicht möglich ist

Einverstanden.

Ich denke nicht, dass es schwierig ist, diese Annahme in der Definition des Operators (-%) als "tun, was die native Shell hier tut" zu kodifizieren.

Auch vereinbart!

Vielleicht ist dies auch eine Möglichkeit, in die Funktion zu gelangen, um env vars vor der ausführbaren Datei zu definieren, die nur für die Sitzung der ausführbaren Datei definiert sind (# 3316):

--% JEKYLL_ENV=production jekyll build

Nur ein Gedanke.

@rkeithhill , die Idee mit call native operator ist, dass die Zeile nicht von PowerShell verarbeitet und wörtlich an die "Standard-Shell" des Betriebssystems gesendet wird. In diesem Fall würde Ihr erstes Beispiel --% & $computerPathToNativeExe foo;@workspace nicht funktionieren. Sie müssten die aktuelle Methode verwenden und entsprechend entkommen. Der zweite Fall --% JEKYLL_ENV=productoin jekyll build sollte einfach funktionieren.

Die Zeile wird nicht von PowerShell verarbeitet und wörtlich an die "Standard-Shell" des Betriebssystems gesendet

OK, das scheint vernünftig. Dieses --% JEKYLL_ENV=productoin jekyll build würde jedoch nur unter Linux / MacOS funktionieren, aber nicht unter Windows - zumindest nicht ohne die Hilfe von PowerShell, oder?

@rkeithhill wie derzeit vorgeschlagen, funktioniert unter Windows nie, da es auf der "Standard" basiert . Für PowerShell, das env var für einen Prozess deklariert, haben wir ein anderes Problem geöffnet, um dies zu diskutieren.

@ PowerShell / Powershell-Komitee hat dies heute diskutiert. Ich habe den ursprünglichen Vorschlag aktualisiert, um das Ergebnis dieser Diskussion widerzuspiegeln. Wir werden mit einer experimentellen Feature-Implementierung fortfahren und mehr Feedback von Benutzern mit Arbeitscode erhalten. Wir sind noch offen für das Siegel für den Betreiber, verwenden aber vorerst --% .

Dabei ist $foo ein wörtlicher Dateiname und $PWD das aktuelle Verzeichnis innerhalb der WSL. Beachten Sie, dass Sie im ersten Beispiel wissen müssen, ob Sie && entkommen müssen, damit es in WSL statt in PowerShell ausgeführt wird.

Sowohl $foo als auch $PWD sind variable Referenzen, die von sh bewertet werden.

Um die Ausgabe einer solchen Ausführung zurück in PowerShell zu leiten, muss der Benutzer die Ergebnisse zuerst in einer Variablen speichern:

$out = --% ls *.txt
$out | select-string hello

Beachten Sie, dass Sie im Gegensatz zum aktuellen & keine PowerShell-Syntax verwenden können.

--% $commandline

wird versuchen, $commandline buchstäblich (unter cmd ) oder nach der Shell-Erweiterung (unter sh ) auszuführen. PowerShell versucht nicht, diese Variable aufzulösen.

Das Problem des Ausschneidens und Einfügens wird durch einfaches Einfügen gelöst, nachdem &n eingegeben wurde.

durch einfaches Einfügen nach --%

Das obige Beispiel für wsl würde folgendermaßen aussehen:

Welcher?

Da alle Argumente nach --% an die "Standard" -Shell übergeben werden, müssen Sie eine Variable verwenden, um das Pipelining in PowerShell zu unterstützen:

Dies ist eine Wiederholung.

Wenn wir etwas Nützliches hinzufügen wollen, schlage ich einen Pivot vor.

Warum können wir nicht so etwas wie ein Herestring-Literal haben?

@! ""! @

Wir können also einen echten Weg finden, dies zu tun, anstatt ein -%, das auch seine eigenen Probleme hat.

Zumindest auf diese Weise können wir das Problem mit "besseren" Designtaktiken begraben.
Und Auffindbarkeit für diese Randfälle in About_Strings, wo sie gelöst werden sollten.

Nur ein Gedanke.

Ich sehe nicht, wie @!"…"!@ besser ist als Invoke-NativeShell @"…"@ . Versuchen wir, PowerShell Brainfuck-ähnlicher zu machen?

@ yecril71pl

Ich sehe nicht, wie @!"…"!@ besser ist als Invoke-NativeShell @"…"@ . Versuchen wir, PowerShell Brainfuck-ähnlicher zu machen?

Ich glaube, dass @ romero126 ein Online-Herestring-Literal vorschlagen sollte, so dass man schreiben konnte

Invoke-NativeShell @!"complicated native command with "quotes" and | pipe"!@

Anstatt von

Invoke-NativeShell @"
complicated native command with "quotes" and | pipe
"@

Wenn ein solcher Online-Herestring in Betracht gezogen würde, würde ich vorschlagen, Syntax wie @"""…"""@ und @'''…'''@ . Dies wäre IMO einfacher zu tippen als @!"…"!@ .


@ romero126 Könntest du klarstellen, was du

@ yecril71pl danke für das Abfangen dieser Fehler, aktualisierte die Top-Nachricht mit Korrekturen.

Es ist nicht angegeben, ob --% als Befehlszeile oder als Batchdatei ausgeführt wird.

Ich glaube, dass @ romero126 ein Online-Herestring-Literal vorschlagen sollte, so dass man schreiben konnte

Invoke-NativeShell @!"complicated native command with "quotes" and | pipe"!@

Anstatt von

Invoke-NativeShell @"
complicated native command with "quotes" and | pipe
"@

Wenn ein solcher Online-Herestring in Betracht gezogen würde, würde ich vorschlagen, Syntax wie @"""…"""@ und @'''…'''@ . Dies wäre IMO einfacher zu tippen als @!"…"!@ .

@ romero126 Könntest du klarstellen, was du

Ich glaube, wenn wir Strings flexibler machen, um "fast rohe" nicht analysierte Daten zu verarbeiten, hätten wir keine Probleme mit diesem Popup.
@ TSlivede Ich stimme fast vollständig mit dem überein, was du gesagt hast. Nur mit geringfügigen Abweichungen. Es sollte in der Lage sein, sowohl einzeilig als auch mehrzeilig zu verarbeiten, ohne irgendetwas in den Anführungszeichen zu interpolieren. Für welche Syntax wir auch immer landen, ich bin offen dafür. Ich konzentriere mich mehr auf das Konzept.

Mit diesen Beispielen wird eine Menge Flexibilität hinzugefügt, bei der ich "Invoke-NativeShell" nicht verwenden muss, da nur eine Zeichenfolge zurückgegeben wird. Das heißt, wir können es so manipulieren, wie es eine Zeichenfolge wäre. Damit dies möglicherweise zu einem Grundnahrungsmittel dafür wird, wie großartig es aussieht.

Ein Pivot wie dieser würde auch bedeuten, dass wir uns nicht mit den oben genannten Problemen befassen müssten, bei denen wir unsere Räder auf eine beinahe Lösung drehen, wenn wir einen Kantenfall behandeln können, anstatt das Problem als Ganzes anzugehen.

Schnelle Beispiele:

# For this scenario let @!"<text>"!@ be our HereString Verbatim

Invoke-NativeShell @!"complicated native command with "quotes" and | pipe"!@
# or
Invoke-NativeShell @!"
Complicated native command with "quotes" and pipe and <tabs>
"!@

#or 
$r = @!"
complicated native command with custom arguments: {0}
"!@ -format "foo"
Invoke-NativeShell $r

Damit wird das Problem @ mklement0 im zweiten Teil dieses Kommentars nicht behoben . Tatsache ist, wir können nicht einfach jedem Befehl eine einzige Zeichenfolge übergeben. In vielen Fällen müssen sie aufgeteilt werden.

Ich glaube, wenn wir Strings flexibler machen, um "fast rohe" nicht analysierte Daten zu verarbeiten, hätten wir keine Probleme mit diesem Popup.

Dafür sind ' und @' gedacht.

Mit diesen Beispielen wird eine Menge Flexibilität hinzugefügt, bei der ich "Invoke-NativeShell" nicht verwenden muss, da nur eine Zeichenfolge zurückgegeben wird. Das heißt, wir können es so manipulieren, wie es eine Zeichenfolge wäre. Damit dies möglicherweise zu einem Grundnahrungsmittel dafür wird, wie großartig es aussieht.

Native Befehle geben eine Folge von Zeichenfolgen zurück. @!" hat das Potenzial, ein Grundnahrungsmittel dafür zu werden, wie unverständlich es aussieht.

Ich glaube, wenn wir Strings flexibler machen, um "fast rohe" nicht analysierte Daten zu verarbeiten, hätten wir keine Probleme mit diesem Popup.

Dafür sind ' und @' gedacht.

Mit diesen Beispielen wird eine Menge Flexibilität hinzugefügt, bei der ich "Invoke-NativeShell" nicht verwenden muss, da nur eine Zeichenfolge zurückgegeben wird. Das heißt, wir können es so manipulieren, wie es eine Zeichenfolge wäre. Damit dies möglicherweise zu einem Grundnahrungsmittel dafür wird, wie großartig es aussieht.

Native Befehle geben eine Folge von Zeichenfolgen zurück. @!" hat das Potenzial, ein Grundnahrungsmittel dafür zu werden, wie unverständlich es aussieht.

Ich denke du bist verwirrt. Mit dem Punkt, den ich machte.
Einfache Anführungszeichen und doppelte Anführungszeichen erweitern nur $ variable oder nicht. Es werden Zeichen nicht automatisch ausgeblendet. Es schränkt mich auch ein, so dass ich seinen bestimmenden Charakter nicht mehr verwenden kann. Also stattdessen, damit es richtig funktioniert, wie es jetzt ist. Ich müsste immer noch Zeichen entkommen, was der ganze Grund ist, warum wir ein &! Befehl.

Wenn Sie eine bessere Lösung haben, bin ich ganz Ohr. Ich glaube, dies trifft mehr Randfälle als das Hinzufügen eines &!

Einfache Anführungszeichen und doppelte Anführungszeichen erweitern nur $ variable oder nicht. Es werden Zeichen nicht automatisch ausgeblendet. Es schränkt mich auch ein, so dass ich seinen bestimmenden Charakter nicht mehr verwenden kann. Also stattdessen, damit es richtig funktioniert, wie es jetzt ist. Ich müsste immer noch Zeichen entkommen, was der ganze Grund ist, warum wir ein &! Befehl.

Welchen Charakteren müsstest du entkommen und warum?

@ romero126 Jetzt bin ich verwirrt, was du hier eine Zeichenfolge hat ?

Ich dachte vorher, dass Sie die Syntax von Powershells hier als störend empfinden könnten (zumindest ärgere ich mich manchmal, dass es am Anfang und am Ende eine neue Zeile geben muss ...), aber jetzt bin ich mir nicht so sicher, was Sie sagen, könnten Sie klarstellen?

@TSlivede , stimmte zu, dass hier-Strings syntaktisch etwas umständlich sind.

Es gibt einen bestehenden Vorschlag, # 2337, der Verbesserungen vorschlägt, und während er sich darauf konzentriert, das Einrücken des schließenden Trennzeichens zu ermöglichen, schlägt -Variante in

# `ins` is `Invoke-NativeShell`
ins @' python -c 'print("hi")' | cat -n '@

Die Notwendigkeit des Entkommens würde vermieden, indem _multiple_ ' nach Bedarf mit @ kombiniert wird (z. B. @'' can use @' here ''@ .

Mit anderen Worten: Dies wäre eine allgemeine Verbesserung der überall verwendbaren PowerShell-Zeichenfolgenliterale, von denen auch Invoke-NativeShell profitieren würde.

Da sich # 2337 hauptsächlich auf etwas anderes konzentriert, habe ich in # 13204 einen

Die Notwendigkeit der Flucht würde vermieden

Ich habe das ein paar Mal in diesem Thread gesehen.

Jeder Indikator für den Tokeniser, um einen neuen Modus zu starten, benötigt einen Escape-Mechanismus, da Sie den Indikator vom Tokeniser, dass Sie diesen Modus verlassen, von der wörtlichen Form dieses Zeichens unterscheiden müssen.

Die natürlichste Form davon sind heute einfach zitierte Zeichenfolgen:

  • ' : Starten Sie die Tokenisierung in einfachen Anführungszeichen
  • ' : Beenden Sie die Tokenisierung in einfachen Anführungszeichen
  • '' : Schreiben Sie ein wörtliches einfaches Anführungszeichen in eine Zeichenfolge in einfachen Anführungszeichen

Ich denke, der Grund, warum wir davon sprechen, trotzdem zu fliehen, ist:

  • Der Operator --% definiert zu viele Möglichkeiten, um seinen Modus zu verlassen ( | , && , || , ; , Zeilenumbruch) und lässt Zeichenfolgen zu darin (ihre ' Zeichen entfernen)
  • Herestrings und Co lösen sich eher auf ein String-Argument als auf ein Array von ihnen auf

Wenn Sie Argumente an einen ausführbaren Aufruf übergeben möchten, handelt es sich tatsächlich um eine Array-Syntax:

  • So starten Sie das Array (wörtlicher Modus starten)
  • So trennen Sie das Array (übergeben Sie ein neues Argument an die neue ausführbare Datei)
  • Wie das Ende des Arrays (Ende wörtlicher Modus)

(Dies ist ein natives Konzept unter * nix, da exec ein Array erwartet. Unter Windows sind wir meiner Meinung nach gezwungen, ein Array zu nehmen, es zu einer bestimmten Zeichenfolgensyntax zu serialisieren und es durch CommandLineToArgvW . .NET bietet jedoch eine Array-basierte Abstraktion für beide - Arrays sind also immer noch am sinnvollsten.)

Das heißt, wir brauchen zwei Fluchten:

  • Das Array-Trennzeichen-Literal
  • Das Array-Endliteral

Nehmen wir zum Beispiel an:

  • --% bevor der Befehl zur neuen wörtlichen Syntax wird
  • Wie beenden wir die Syntax? Newline und ; vielleicht?
  • Wie trennen wir Argumente? vielleicht?

Die Fragen sind dann:

  • Wie übergeben wir ein wörtliches ; oder eine neue Zeile?
  • Wie übergeben wir ein wörtliches ?

Eine native Befehlszeile ist kein Array. Es ist entweder ein String oder ein AST. Da PowerShell nichts über ASTs in nativen Shells weiß, bleibt nur eine Zeichenfolge übrig.

Eine native Befehlszeile ist kein Array. Es ist entweder ein String oder ein AST

Sie haben keine Gründe für diese Behauptung angegeben.

Hier erstellt PowerShell den AST für einen Befehl (jeglicher Art, da er erst zur Laufzeit weiß, wann der Befehl die Art des Befehls auflöst):

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/parser/Parser.cs#L6391 -L6634

Beachten Sie, dass Befehlselemente (einschließlich Parameter und Argumente) zu einem Array hinzugefügt werden.

Sie können dies in PowerShell folgendermaßen in Aktion sehen:

> { ping 192.168.0.1 }.Ast.EndBlock.Statements[0].PipelineElements[0].CommandElements

StringConstantType : BareWord
Value              : ping
StaticType         : System.String
Extent             : ping
Parent             : ping 192.168.0.1

StringConstantType : BareWord
Value              : 192.168.0.1
StaticType         : System.String
Extent             : 192.168.0.1
Parent             : ping 192.168.0.1

Dieses Array wird hier zu Ausdrücken zusammengestellt:

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/parser/Compiler.cs#L4127 -L4160

Und dann wird das hier an einen Pipeline-Aufruf übergeben (beachten Sie, dass in Expression.NewArrayInit(typeof(CommandParameterInternal[]), pipelineExprs) die Argumente übergeben werden):

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/parser/Compiler.cs#L3798 -L3805

Zur Laufzeit, wenn die kompilierten Ausdrücke ausgeführt werden, wird dies zu

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs#L29 -L312

Wenn die Pipeline aufgerufen wird, wird dieses Argumentarray schließlich mit dem Befehl NativeCommandParameterBinder an den nativen Befehl gebunden:

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/NativeCommandParameterBinder.cs#L69 -L137

Dazu wird das Argumentarray verwendet, das AST-Metadaten enthält, z. B. welche Art von Zeichenfolge für jedes Argument verwendet wurde, und hier wird eine neue Zeichenfolge für den Aufruf von Argumenten in NativeCommandProcessor :

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/NativeCommandProcessor.cs#L1094 -L1166

Dies geschieht, weil die .NET-API, die wir derzeit für den Prozessaufruf verwenden, die alte Version "Alle Argumente in einer einzelnen Zeichenfolge" ist und nicht die neue in .NET Core, mit der Sie ein Array übergeben und .NET den Aufruf neu erstellen können nach Bedarf plattformspezifisch.

Die Verwendung dieser API ist jedoch ein Implementierungsdetail. Zum Beispiel könnten wir fork / exec direkt auf * nix aufrufen, wie wir es hier tun:

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/powershell/Program.cs#L268 -L330

Beachten Sie, dass exec hier ein Array benötigt - wir haben dies getan, weil dies die einfachste und direkteste Möglichkeit ist, Argumente an den Systemaufruf zu übergeben, was letztendlich das Ziel einer wörtlichen Argumentsyntax ist.

@rjmholt :

_Funktional_ werden alle Anforderungen bereits durch die vorhandenen Zeichenfolgenliteralformulare abgedeckt, _wenn Sie die native Befehlszeile als einzelne Zeichenfolge_ übergeben, was ich dringend empfehle, über ein neues Invoke-NativeShell / ins Cmdlet.

Der Ausgangspunkt hier ist als einzelne Zeichenfolge, nicht als Array: Es handelt sich um eine gesamte Befehlszeile, die von der Ziel-Shell analysiert werden soll, und es ist am sinnvollsten, dies in PowerShell als solche --%

Alles, was Invoke-NativeShell tun muss, ist, die einzelne Zeichenfolge, die die gesamte Native-Shell-Befehlszeile enthält, über ihre CLI an die native Shell zu übergeben:

  • Unter Unix bedeutet dies: Übergeben Sie die Zeichenfolge wie sie ist, als Ganzes, als zweites Element des Arguments, das an exec (wobei -c das erste Argument-Array-Element ist und /bin/sh die ausführbare Datei); ausgedrückt in .NET-Begriffen mit einem einfachen Beispiel, wobei printf "'%s'" foo | cat -n der wörtliche Inhalt der einzelnen Zeichenfolge ist, die die zu übergebende Befehlszeile enthält (beachten Sie die Verwendung von .ArgumentList , nicht .Arguments ; die Verdoppelung von ' ein Artefakt dieses Beispiels):

    • $psi = [Diagnostics.ProcessStartInfo]::new('/bin/sh'); $psi.ArgumentList.Add('-c'); $psi.ArgumentList.Add('printf "''%s''" foo | cat -n'); [Diagnostics.Process]::start($psi).WaitForExit()
  • Unter Windows bedeutet dies: Um die Macken von cmd zu berücksichtigen, füllen Sie das Argument lpCommandLine von CreateProcess wie folgt aus (wobei lpApplicationName als Wert von angenommen wird Umgebungsvariable ComSpec , die auf cmd.exe ): "/c " + cmdLine , wobei cmdLine die gesamte Befehlszeilenzeichenfolge ist, wie sie ist; ausgedrückt in .NET-Begriffen mit einem einfachen Beispiel, wobei echo a^&b & ver der wörtliche Inhalt der einzelnen Zeichenfolge ist, die die zu durchlaufende Befehlszeile enthält:

    • $psi = [Diagnostics.ProcessStartInfo]::new($env:ComSpec); $psi.Arguments = '/c ' + 'echo a^&b & ver'; [Diagnostics.Process]::start($psi).WaitForExit()

      • @ yecril71pl , dies impliziert die Antwort auf Ihre Frage, ob die Semantik der Eingabeaufforderung oder der Batchdatei verwendet werden soll: Dies ist die erstere, wie sie auch von anderen Skriptsprachen wie Python mit os.system() . Die (möglicherweise unglückliche) Implikation ist, dass Sie % Zeichen nicht entkommen können und dass for Schleifenvariablen ein einzelnes % (z. B. %i statt %%i ); und dass Sie Schleifenkörperbefehlen @ voranstellen müssen, um zu verhindern, dass sie wiedergegeben werden (z. B. for %i in (*.txt) do <strong i="56">@echo</strong> %i ).

Dieser Prozess ist für den Endbenutzer transparent, sodass sie sich nur darauf konzentrieren müssen, die Native-Shell-Befehlszeile _verbatim_ zu übergeben, wobei nur die Syntaxanforderungen von _PowerShell_ für Zeichenfolgenliterale erfüllt werden müssen:

Mit dem Formular ins @'<newline>...<newline>'@ können Sie Ihre Native-Shell-Befehlszeilen bereits so wie sie sind verwenden: siehe oben, wie die verschiedenen vorhandenen Zeichenfolgenliteralformulare verwendet werden können, die - aufgrund der Verfügbarkeit der _Interpolation_ hier- Zeichenfolgenform ( @"<newline>...<newline>"@ ) - beinhaltet die Möglichkeit, _PowerShell_-Variablen und -Ausdrücke in der Befehlszeile zu verwenden (die Unfähigkeit, dies zu tun, ist einer der Hauptmängel von --% ).

Was mein vorheriger Kommentar vorschlägt, ist eine neue allgemeine Form des rohen Zeichenfolgenliteral, die von diesem Vorschlag getrennt ist (und deren Invoke-NativeShell Anrufe dann bequemer.

Anstelle dessen, was Sie bereits tun können (unter der Annahme eines Befehls Invoke-NativeShell / ins ):

# ALREADY WORKS: Literal here-string
ins @'
python -c 'print("hi")' | cat -n
'@

Mit dem vorgeschlagenen neuen einzeiligen Rohliteral könnten Sie Folgendes vereinfachen:

# PROPOSED, more convenient syntax: new raw literal.
ins @' python -c 'print("hi")' | cat -n '@

Wenn die Besonderheiten dieser neuen rohen String-Literal-Syntax diskutiert werden müssen, lassen Sie es uns bitte in # 13204 tun, nicht hier.

Wenn ich also den Vorschlag noch einmal lese, um die ursprüngliche Absicht zu verstehen, muss ich wirklich nur nach einer besseren Syntax für /bin/sh -c '<command>' + der cmd-Version fragen, zusammen mit einer speziellen String-Escape-Logik für jede Ziel-Shell. Ich hatte meine anfängliche Konzeption auf eine spezielle Syntax gestützt und musste mit dem Parser und dem nativen Befehlsprozessor arbeiten. Außerdem war es sehr schwierig, die ProcessStartInfo.Arguments -Eigenschaft von .NET in Bezug auf die Übergabe von Anführungszeichen korrekt zu verwenden .

Tatsächlich wird der Arguments String nur wörtlich durchlaufen . Alles was wir tun müssen, ist einen solchen String direkt durchzuleiten.

In diesem Fall denke ich, dass jede neue Syntax völlig unnötig ist (sie wird nur die Tokenisierungslogik komplizieren und ich vermute, dass sie die Leute verwirrt) und stattdessen ist dies nur ein neuer Befehl, ins wie Sie sagen. .NET hat hier eine Logik dafür , die wir erneut implementieren müssen, aber das ist in Ordnung, da wir sowieso ein bisschen schlauer sein müssen.

Einige Kommentare zu @rjmholts Kommentar https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -660231641

Jeder Indikator für den Tokeniser, um einen neuen Modus zu starten, benötigt einen Escape-Mechanismus, da Sie den Indikator vom Tokeniser, dass Sie diesen Modus verlassen, von der wörtlichen Form dieses Zeichens unterscheiden müssen.

Ich bin damit einverstanden und glaube nicht, dass eine Inline-String-Syntax die Notwendigkeit eines Escape vollständig vermeiden kann.

Der Operator -% definiert zu viele Möglichkeiten, um den Modus zu verlassen (|, &&, ||,;, Zeilenumbruch) und lässt darin enthaltene Zeichenfolgen zu (Entfernen der Zeichen)

Wenn Sie von --% sprechen, beziehen Sie sich auf den vorhandenen Schalter --% ? Wenn ja, ist es besser, dies in Ihrem Kommentar klar zu machen, da jetzt --% als Anrufbetreiber vorgeschlagen wird.

Wenn Sie Argumente an einen ausführbaren Aufruf übergeben möchten, handelt es sich tatsächlich um eine Array-Syntax:

Der Vorschlag ist, die native Shell (sh oder cmd) aufzurufen und alles buchstäblich nach dem Anrufbetreiber --% , wie sh -c ... . Es ist also keine Powershell, die die Argumente an die ausführbare Zieldatei weitergibt. In diesem Fall brauchen wir keine Array-Syntax, oder?

Wie beenden wir die Syntax? Newline und; könnte sein?
Wie trennen wir Argumente? "Raum" vielleicht?

Ich denke, das wörtliche Parsen endet bei newline. Verwenden Sie \n , um eine neue Zeile als Teil des Arguments zu übergeben, genau wie Sie es direkt in bash tun.

Eine native Befehlszeile ist kein Array. Es ist entweder ein String oder ein AST

Sie haben keine Gründe für diese Behauptung angegeben.

Hier erstellt PowerShell den AST für einen Befehl (jeglicher Art, da er erst zur Laufzeit weiß, wann der Befehl die Art des Befehls auflöst):

Wenn dieser Vorschlag implementiert wird, wird ein spezielles Konstrukt eingeführt, bei dem PowerShell den AST überhaupt nicht erstellt.

Freut mich das zu hören, @rjmholt.

.NET hat hierfür eine Logik, die wir erneut implementieren müssen

Ich glaube nicht, dass wir tatsächlich etwas neu implementieren müssen, wie die Befehle in meinem vorherigen Kommentar zeigen: Verwenden Sie unter Windows .Arguments für die Weiterleitung der gesamten Befehlszeilenzeichenfolge an die WinAPI. Verwenden Sie unter Unix .ArgumentList , um das Argument _array_ zu erstellen, das direkt mit exec .

Wenn dieser Vorschlag implementiert wird, wird ein spezielles Konstrukt eingeführt, bei dem PowerShell den AST überhaupt nicht erstellt.

Nun, es würde einen anderen AST-Typ bauen, der für diesen Zweck entwickelt wurde. Wenn es nicht Teil des AST ist, ist es im Grunde dasselbe wie ein Kommentar, nichts passiert.

_Funktional_ werden alle Anforderungen bereits durch die vorhandenen Zeichenfolgenliteralformulare abgedeckt, _wenn Sie die native Befehlszeile als einzelne Zeichenfolge_ übergeben, was ich dringend empfehle, über ein neues Invoke-NativeShell / ins Cmdlet.

Ich persönlich mag die Cmdlet-Idee Invoke-NativeShell , die in gewisser Hinsicht besser ist als die des Anrufbetreibers --% . Das Hauptanliegen, das ich über diese Idee gehört habe, ist, dass sie mehr als --% + Strg + v tippt und die Unterscheidung zwischen wörtlicher Hier-Zeichenfolge und Interpolation der Hier-Zeichenfolge eine weitere Belastung für den Benutzer darstellt (_Ich persönlich denke, ins ist viel einfacher zu tippen als --% _).

Das Hauptanliegen, das ich über diese Idee gehört habe, ist, dass sie mehr tippt als --% + Strg + v und die Unterscheidung zwischen wörtlichem Here-String und Interpolation von Here-String eine weitere Belastung für den Benutzer darstellt (_Ich persönlich denke, ins ist viel einfacher zu tippen als --% _).

Vielleicht kann PSRL helfen. Ähnlich wie bei einem Key-Handler zum Umgeben eingefügter Pfade konnte PSRL die CommandAst lesen, sehen, dass es ins und die eingefügte Zeichenfolge ordnungsgemäß umgehen.

Nun, es würde einen anderen AST-Typ bauen, der für diesen Zweck entwickelt wurde. Wenn es nicht Teil des AST ist, ist es im Grunde dasselbe wie ein Kommentar, nichts passiert.

Sicher, aber der Baum wird trivial sein. Es wird nicht die Bedeutung der Befehle widerspiegeln, die ausgeführt werden sollen.

Starten Sie den Prozess cmd.exe, leiten Sie stdin / stdio um, und wir können buchstäblich eine Zeichenfolge verwenden, um NativeCommand mit bash aufzurufen

Freut mich das zu hören, @ daxian-dbw.
Ich liebe die Idee,

Die Unterscheidung zwischen der wörtlichen Hier-Zeichenfolge und der Interpolation der Hier-Zeichenfolge ist eine weitere Belastung für den Benutzer

Ich denke, das ist ein _plus_, weil Sie - anders als --% - mit @"<newline>...<newline>"@ die Werte von _PowerShell_-Variablen und -Ausdrücken direkt in die native Befehlszeile integrieren können.

Dies setzt natürlich voraus, dass Benutzer den Unterschied zwischen Zeichenfolgen in einfachen Anführungszeichen (wörtlich) und Zeichenfolgen in doppelten Anführungszeichen (interpolierend) und über selektives ` -Escaping von $ , aber ich denke, es ist wichtig (erweiterte) Option zu haben.

Um den Benutzern beim Ausschneiden und Einfügen zu helfen, sollte die vorgeschlagene PSReadLine-Funktion standardmäßig @'<newline>...<newline>'@ (wörtlich) sein.


Letztendlich möchten wir keine Denkweise fördern, bei der Benutzer die einzigartigen Syntaxanforderungen von PowerShell nicht kennen und verstehen müssen. Stattdessen möchten wir das Verständnis für sie fördern, angesichts der zusätzlichen Leistung, die sie bringen.

Um Benutzer zu ermutigen, die _own_-Syntax von PowerShell zu verwenden, die per Definition _cross-platform_ ist, muss sie vorhersehbar funktionieren .
Zu diesem Zweck muss # 1995 im Idealfall direkt

Zusammenfassen:

  • Etwas wie --% (das hier vorgeschlagene neue Operatorformular), per Definition eine plattformspezifische Funktion, ist in keiner Weise ein Ersatz für die Korrektur von # 1995 .

  • Invoke-NativeShell ( ins ) vermeidet alle Einschränkungen des vorgeschlagenen Operators --% und bietet die von @joeyaiello erwähnte "Falltür" -Lösung: eine schnelle Möglichkeit, eine geschriebene Befehlszeile auszuführen für die native Shell, ohne sie an die PowerShell-Syntax anpassen zu müssen.

  • --% sollte nur in seiner ursprünglichen _argument_-Form bleiben und sollte eine _Windows-only_-Funktion bleiben (die es _effektiv_, wenn auch nicht _technisch_ ist) - mit anderen Worten: Keine Änderung erforderlich.

    • Sobald # 1995 behoben ist, besteht der einzige Zweck von --% , _edge-Fälle unter Windows_ zu berücksichtigen: Sie können das explizite Zitieren der Befehlszeile steuern, die an unerwünschte Programme wie msiexec , die dies erfordern sehr spezifische Formen der argumentinternen Zitierung.

Eine der anderen Funktionen, die ich in PS v7.x wirklich haben möchte, ist die Möglichkeit, dass ein nativer Befehl, der mit einem Fehlercode beendet wird, mein Skript stoppt, wie z. B. set -e wie in diesem RFC beschrieben . Wenn die Lösung dafür so etwas wie ein Invoke-NativeCommand , würde das mit Invoke-NativeShell verwechseln?

Ich versuche nur, den Film ein wenig vorwärts abzuspielen, denn so sehr ich die native Funktion zum Aufrufen von Anrufen möchte, möchte ich wirklich, dass meine Build- und Testskripte beim Aufrufen von msbuild, cl, cmake, Conan, npm usw. fehlerhaft sind und diese nativen Befehle mit beendet werden Ein Exit-Code ungleich Null, ohne dass Sie daran denken müssen, $ LASTEXITCODE ständig zu überprüfen. Wenn die Implementierung nur über eine andere Präferenzvariable erfolgt, z. B. $PSNativeCommandInErrorActionPreference , würde sich dies vermutlich auf den Aufruf der nativen Shell auswirken - es handelt sich um einen nativen "Befehl" und alles?

Das Hauptanliegen, das ich über diese Idee gehört habe, ist, dass sie mehr tippt als --% + Strg + v und die Unterscheidung zwischen wörtlichem Here-String und Interpolation von Here-String eine weitere Belastung für den Benutzer darstellt (_Ich persönlich denke, ins ist viel einfacher zu tippen als --% _).

Vielleicht kann PSRL helfen. Ähnlich wie bei einem Key-Handler zum Umgeben eingefügter Pfade konnte PSRL die CommandAst lesen, sehen, dass es ins und die eingefügte Zeichenfolge ordnungsgemäß umgehen.

Aber was ist mit:

$c = gcm ins
& $c ...

PSRL wird es schwer haben zu erkennen, was dort vor sich geht. Es müsste zustandsbehaftet sein, den Alias ​​ins aufrufen, um -nativeshell aufzurufen, dann herauszufinden, dass Sie die Befehlsinfo aufrufen und dann die Argumente demunge? Ich mag die Idee, ein falsches Kommando als Spezialgehäuse zu verwenden, wirklich nicht.

Die Unterscheidung zwischen der wörtlichen Hier-Zeichenfolge und der Interpolation der Hier-Zeichenfolge ist eine weitere Belastung für den Benutzer

Ich denke, das ist ein _plus_, weil Sie - anders als --% - mit @"<newline>...<newline>"@ die Werte von _PowerShell_-Variablen und -Ausdrücken direkt in die native Befehlszeile integrieren können.

Ich folge dir nicht @ mklement0. Warum kann --% nicht gelehrt werden, dies zu tun? Ich habe dies bereits als Anwendungsfall angegeben.

Ich mag die Idee, ein falsches Kommando als Spezialgehäuse zu verwenden, wirklich nicht.

Hier gibt es keinen gefälschten Befehl, nur ein echtes Cmdlet, Invoke-NativeShell , das auf ins .

gcm ins ( Get-Command ins ) enthält Informationen zum Alias ins in Form einer Instanz von [System.Management.Automation.AliasInfo] , die Sie später an & für den Aufruf, wie bei jedem anderen Befehl oder Alias.

Daher funktioniert Ihr Beispiel genau wie beabsichtigt - es ist nur so, dass Sie, wenn Sie den Umweg gcm ( Get-Command ) verwenden, die entsprechende String-Literal-Syntax _yourself_ ohne das buchstabieren müssen Optionales Convenience-Befehlsgerüst, das PSRL bereitstellen könnte, wenn der Vorschlag von @SeeminglyScience umgesetzt wird:

$c = gcm ins
& $c @'
python -c 'print("hi")' | cat -n
'@

Oder wenn die in # 13204 vorgeschlagene neue rohe String-Literal-Syntax implementiert ist:

$c = gcm ins
& $c @' python -c 'print("hi")' | cat -n '@

Wenn Sie mit der Syntax der PowerShell-Zeichenfolgenliterale vertraut sind, reicht natürlich auch eine normale Zeichenfolge in einfachen Anführungszeichen aus: Verdoppeln Sie einfach die eingebettete ' in der gesamten '...' Zeichenfolge:

$c = gcm ins
& $c 'python -c ''print("hi")'' | cat -n'

Warum kann --% nicht gelehrt werden, dies zu tun?

  • --% fehlt ein schließendes Trennzeichen, wodurch die Verwendung in Powershell-Pipelines verhindert wird. Dies ist eine inakzeptable Einschränkung (beachten Sie, dass (...) Enclosure keine Option ist, da dies eine vorherige Erfassung aller Ausgaben impliziert Zeilen im Speicher anstatt Streaming-Verarbeitung).

  • Unter Unix kann --% nicht beide (nicht entkoppelte) $ als PowerShell-Metazeichen _und_ als POSIX-Shell-Metazeichen unterstützen.

Nochmals: Es gibt keine Rechtfertigung und keinen Vorteil, die Syntax einer anderen Shell in PowerShell zu integrieren. Übergeben Sie die native Shell-Befehlszeile als Zeichenfolge an ins , und alle konzeptionellen Probleme verschwinden.

Invoke-NativeShell impliziert, dass PowerShell nicht die native Shell ist. Ist diese Annahme immer richtig und soll es immer sein?

@oising der Anwendungsfall ist jemand, der einen nativen Befehl aus einer README-Datei oder etwas anderem kopiert und ins SPACE CTRL + v einzugeben, erstellen sie einfach ihre eigene Zeichenfolge und entkommen ihr wie gewohnt.

@ mklement0

Um Benutzer zu ermutigen, die _own_-Syntax von PowerShell zu verwenden, die per Definition _cross-platform_ ist, muss sie vorhersehbar funktionieren .
Zu diesem Zweck muss # 1995 im Idealfall direkt

Dies ist getrennt von der Diskussion über ins / --% richtig? Wenn Sie nicht einverstanden sind, können Sie klären, warum sich dieses Problem auf den neuen Befehl / die neue Syntax auswirkt?

@SeeminglyScience , ja, es ist separat, aber das war in diesem Thread nicht klar.
Der einzige Grund, warum # 1995 jemals hier aufgetaucht ist, ist, dass es ursprünglich zugunsten dieses Problems geschlossen wurde (obwohl es seitdem glücklicherweise wieder geöffnet wurde), und das OP hier gibt immer noch an, dass dieses Problem es beheben wird ("Dies sollte auch diese Probleme lösen:") .
Daher wollte ich betonen, dass ins / --% die Adresse # 1995 adressieren würde, was dringend eine eigene Korrektur erfordert.
(Nur wenn die Übergabe von PowerShell-eigenen Argumenten ordnungsgemäß funktioniert, können wir den Benutzern guten Gewissens empfehlen, die PowerShell-eigene Syntax vollständig zu lernen und zu akzeptieren - was wir sollten.)

@ SP3269 , wir können diese Brücke überqueren, wenn wir dazu kommen 😁.

Aber ernsthaft:

Wenn Sie sich "native" als "speziell für die Host-Plattform [Familie] geschrieben" vorstellen, würde der Name immer noch passen, selbst wenn PowerShell jemals die Standardsystem-Shell auf einer bestimmten Plattform werden sollte (hier ist zu hoffen, aber ich denke nicht das ist realistisch, zumindest nicht in absehbarer Zukunft.

Alternativen:

  • Invoke-[System]DefaultShell wäre der genaueste Name (obwohl er eindeutig nicht mehr zutreffen würde, sollte PowerShell jemals zur Standard-Shell eines Systems werden), aber "native" scheint der in der PowerShell-Welt am häufigsten verwendete Begriff zu sein.

  • Invoke-LegacyShell hat unter Windows eine Rechtfertigung, aber ich glaube nicht, dass Unix-Benutzer diesen Begriff zu freundlich nehmen würden.

Ich glaube, ich fange an, die völlige Verwirrung zu enträtseln, die ich erlebe, wenn ich versuche, die Motive, Ideen und Probleme aller hier im Auge zu behalten. Es scheint, dass wir (zumindest @ mklement0 und ich) dies als zwei verschiedene Lösungen für zwei verschiedene Ansichten desselben konzeptuellen Problems betrachten. Was ich vorschlug, war die Verwendung von --% als Hinweis auf den _parser_, um die Art und Weise zu ändern, wie Dinge in Verbindung mit dem Aufrufoperator & analysiert werden (nicht unbedingt mit --% Standalone). Auf der anderen Seite scheint Michael ins als Drop-In-Cmdlet zu betrachten, das den internen nativen Befehlsbroker und Argumentparser von Powershell ersetzt, und versucht nicht, den Parser von Powershell zu ändern, sondern verwendet stattdessen strings / here -strings, um die beabsichtigten Parameter zu erfassen (was ich sage, könnte auch mit --% .)

-% fehlt ein schließendes Trennzeichen, das die Verwendung in Powershell-Pipelines verhindert

Diesen Punkt verstehe ich überhaupt nicht. Es fehlt nicht mehr ein schließendes Trennzeichen als der Befehl ins / invoke-native. Wenn Sie die LHS abgrenzen oder von ihr füttern müssen, verwenden Sie hier Zeichenfolgen, andernfalls wird dies als einzelne Anweisung betrachtet.

Unabhängig davon, welcher Weg gewählt wird, scheint es notwendig zu sein, eine native Shell auszuwählen, um die Befehlszeile zu versenden. Ich schlage vor, wir verwenden die Umgebungsvariablen COMSPEC unter Windows (standardmäßig %systemroot%\system32\cmd.exe ) und SHELL unter Linux (standardmäßig /bin/bash ) - falls SHELL dies nicht tut. Wenn es keine gibt, sollten wir auf /bin/sh abzielen (was in den meisten Fällen sowieso ein Symlink zu Bash ist.)

-% fehlt ein schließendes Trennzeichen, das die Verwendung in Powershell-Pipelines verhindert

Diesen Punkt verstehe ich überhaupt nicht. Es fehlt nicht mehr ein schließendes Trennzeichen als der Befehl ins / invoke-native. Wenn Sie die LHS abgrenzen oder von ihr füttern müssen, verwenden Sie hier Zeichenfolgen, andernfalls wird dies als einzelne Anweisung betrachtet.

Grundsätzlich können Sie Dinge wie cmd --% /c someapp | someOtherApp nicht aufrufen und cmd die Pipeline verwalten lassen. PowerShell interpretiert eine Pipeline (und einige andere Dinge wie && und || die ansonsten für Unix-Benutzer nützlich wären) weiterhin als PowerShell-Token, anstatt sie als Teil von an den nativen Befehl zu übergeben die Argumentzeichenfolge.

und SHELL unter Linux (standardmäßig /bin/bash )

Nein: Die _system_-Shell auf Unix-ähnlichen Plattformen ist _invariably_ /bin/sh .
Dies unterscheidet sich von einer bestimmten interaktiven Shell des Benutzers (die sich in $env:SHELL widerspiegelt - aber derzeit leider nicht, wenn _PowerShell_ die Shell des Benutzers ist, siehe # 12150), die (a) konfigurierbar und (b) häufig ist _defaults to_ /bin/bash , aber auch das ist nicht selbstverständlich, wie macOS kürzlich bei der Umstellung auf /bin/zsh als standardmäßige interaktive Shell für neue Benutzer belegt.

Ich schlage vor, wir verwenden die Umgebungsvariablen COMSPEC unter Windows

Guter Punkt, das ist der bessere Weg, um auf die System-Shell (Befehlsinterpreter) unter Windows zu verweisen - ich habe den obigen Beispielbefehl entsprechend aktualisiert.

Eine der anderen Funktionen, die ich in PS v7.x wirklich haben möchte, ist die Möglichkeit, dass ein nativer Befehl, der mit einem Fehlercode beendet wird, mein Skript stoppt, wie z. B. set -e wie in diesem RFC beschrieben . Wenn die Lösung dafür so etwas wie ein Invoke-NativeCommand , würde das mit Invoke-NativeShell verwechseln?

Ich versuche nur, den Film ein wenig vorwärts abzuspielen, denn so sehr ich die native Funktion zum Aufrufen von Anrufen möchte, möchte ich wirklich, dass meine Build- und Testskripte beim Aufrufen von msbuild, cl, cmake, Conan, npm usw. fehlerhaft sind und diese nativen Befehle mit beendet werden Ein Exit-Code ungleich Null, ohne dass Sie daran denken müssen, $ LASTEXITCODE ständig zu überprüfen. Wenn die Implementierung nur über eine andere Präferenzvariable erfolgt, z. B. $PSNativeCommandInErrorActionPreference , würde sich dies vermutlich auf den Aufruf der nativen Shell auswirken - es handelt sich um einen nativen "Befehl" und alles?

Zu diesem Zweck haben wir Start-NativeExecution in unserem build -Modul.

Ich habe das gesehen. Wie genau würden Sie Start-NativeExecution mit Invoke-NativeShell kombinieren, wenn Sie möchten, dass der spätere Code effektiv auf einen Exit-Code ungleich Null gesetzt wird? Ich hoffe wirklich, dass so etwas wie PR # 3523 es zusammen mit dieser nativen Funktion schafft, weil ich möchte, dass die beiden zusammenarbeiten. Ich nehme an, wenn call native als Cmdlet implementiert wird (Invoke-NativeShell vs -%), könnte -ErrorAction Stop implementiert werden, damit es auf einen Exit-Code ungleich Null geworfen wird (Beendigungsfehler).

-% fehlt ein schließendes Trennzeichen, das die Verwendung in Powershell-Pipelines verhindert

Diesen Punkt verstehe ich überhaupt nicht. Es fehlt nicht mehr ein schließendes Trennzeichen als der Befehl ins / invoke-native. Wenn Sie die LHS abgrenzen oder von ihr füttern müssen, verwenden Sie hier Zeichenfolgen, andernfalls wird dies als einzelne Anweisung betrachtet.

Grundsätzlich können Sie Dinge wie cmd --% /c someapp | someOtherApp nicht aufrufen und cmd die Pipeline verwalten lassen. PowerShell interpretiert eine Pipeline (und einige andere Dinge wie && und || die ansonsten für Unix-Benutzer nützlich wären) weiterhin als PowerShell-Token, anstatt sie als Teil von an den nativen Befehl zu übergeben die Argumentzeichenfolge.

Ja, ich verstehe das aktuelle Verhalten @ vexx32 . Aber wir reden nicht (zumindest nicht) über das aktuelle Verhalten von --% - wir reden darüber, es zu verbessern, nein? Warum habe ich das Gefühl, dass wir hier alle miteinander reden? :) :)

Wie ich schon sagte, könnten wir & verbessern , um mit --% so dass dies möglich ist . Ich denke auch, dass wir uns über implizite native Ausführung und den expliziten Aufruf einer Shell mit Argumenten im Klaren sein müssen. Dies ist eine ständige Quelle der Verwirrung - selbst für erfahrene Windows-Benutzer -, dass cmd.exe (eine Shell) irgendwie benötigt wird, um andere ausführbare Dateien "auszuführen"; Gleiches gilt für Linux / OSX.

Ja, ich verstehe das aktuelle Verhalten @ vexx32 . Aber wir reden nicht (zumindest nicht) über das aktuelle Verhalten von --% - wir reden darüber, es zu verbessern, nein? Warum habe ich das Gefühl, dass wir hier alle miteinander reden? :) :)

Die aktuelle Tonhöhe für die Anpassung von --% lautet also, dass, wenn sich dort normalerweise der Befehlsname befindet, alles rechts davon ( wie bis zu einer neuen Zeile) analysiert und direkt an bash / cmd gesendet wird. Es gibt keinen Platz für eine PowerShell-Syntax wie hier-Strings und was nicht, weil es dann dieselben Probleme gibt, die --% und der native Befehlsprozessor derzeit haben.

Ich habe das gesehen. Wie genau würden Sie Start-NativeExecution mit Invoke-NativeShell kombinieren, wenn Sie möchten, dass der spätere Code effektiv auf einen Exit-Code ungleich Null gesetzt wird? Ich hoffe wirklich, dass so etwas wie PR # 3523 es zusammen mit dieser nativen Funktion schafft, weil ich möchte, dass die beiden zusammenarbeiten. Ich nehme an, wenn call native als Cmdlet implementiert wird (Invoke-NativeShell vs -%), könnte -ErrorAction Stop implementiert werden, damit es auf einen Exit-Code ungleich Null geworfen wird (Beendigungsfehler).

Derzeit wird es Start-NativeExecution { Invoke-NativeShell <strong i="10">@whatever</strong> } .

Diesen Punkt verstehe ich überhaupt nicht. Es fehlt nicht mehr ein schließendes Trennzeichen als der Befehl ins / invoke-native. Wenn Sie die LHS abgrenzen oder von ihr füttern müssen, verwenden Sie hier Zeichenfolgen, andernfalls wird dies als einzelne Anweisung betrachtet.

Invoke-NativeShell benötigt kein schließendes Trennzeichen, da es sich um einen regulären Befehl handelt, während der Horror von --% nicht ist.

und SHELL unter Linux (standardmäßig /bin/bash )

Nein: Die _system_-Shell auf Unix-ähnlichen Plattformen ist _invariably_ /bin/sh .

Ich denke, das Ding sollte gleichbedeutend sein mit dem Erstellen und Ausführen einer ausführbaren Skriptdatei, die die Aufgabe der Auswahl der richtigen Shell an das Betriebssystem delegiert, und wir sollten uns nicht darum kümmern. Einschließlich, wenn es mit #! beginnt, soll es so sein.

Die Idee hinter Invoke-NativeShell ist es, einen praktischen Wrapper um die _CLI_ der nativen Shell bereitzustellen.

  • Unter Unix ist dies völlig ausreichend, und das Erstellen einer zusätzlichen Skriptdatei führt nicht nur zu zusätzlichem Overhead, sondern meldet dann einen zufälligen Wert in $0 (dem Pfad der Skriptdatei), während der Aufruf über die CLI vorhersehbar /bin/sh enthält sh -c 'echo $0' foo ).

    • (Nebenbei bemerkt: Das Erstellen eines Shell-Skripts mit der Shebang-Zeile #!/bin/sh bedeutet, dass die System-Shell genauso explizit auf den vollständigen Pfad ausgerichtet wird wie das direkte Aufrufen von /bin/sh ).
  • Unter Windows würde die Ausführung über eine Batch-Datei Vorteile bringen (jedoch nicht zum Delegieren der Aufgabe des Lokalisierens der System-Shell, was $env:ComSpec vorhersehbar tut), da dadurch die Probleme mit dem Entkommen von % vermieden würden und for Verhalten oben erwähnt.

Um letzteres aus Höflichkeit anzusprechen (damit Benutzer keine eigenen Aux-Batch-Dateien schreiben müssen), könnten wir aus Gründen der konzeptionellen Klarheit eine anbieten
-AsBatchFile Schalter als _opt-in_.

Das heißt, wenn Konsens darüber besteht, dass die Mehrheit der öffentlichen cmd Befehlszeilen, die zum Ausschneiden und Einfügen verwendet werden, unter Berücksichtigung der Semantik von _batch-file_ geschrieben wurden ( %%i statt %i als Schleifenvariable, Fähigkeit, einem wörtlichen % als %% zu entkommen), möglicherweise immer mit einem Aux. Die Batch-Datei _on Windows_ (während Sie sich an die CLI unter Unix halten) ist die bessere Lösung. Ich bin mir dessen nicht sicher, aber sie müsste klar dokumentiert werden, insbesondere, weil sie ein Verhalten zeigt, das sich von anderen Skriptsprachen unterscheidet.

@ Rkeithhill

Für mich lohnt es sich, den RFC zur

Beide stellen notwendige Schritte dar, um externe Programme (native Dienstprogramme) zu erstklassigen Bürgern in PowerShell zu machen (soweit dies konzeptionell möglich ist), was sie immer hätten sein sollen.

Ein erstklassiger Bürger zu sein bedeutet: direkter Aufruf (wobei & nur aus syntaktischen Gründen situativ benötigt wird), nicht über ein Cmdlet.
Mit anderen Worten: Es sollte niemals ein Invoke-NativeCommand Cmdlet oder ein Start-NativeExecution Cmdlet geben.

(Nebenbei bemerkt: Start-NativeExecution ist falsch benannt, da viele Funktionen im build -Modul sind; Start signalisiert _asynchrone_ Operation, während die meisten dieser Funktionen _synchron sind_ - siehe die Diskussion über Start-Sleep unter https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4474).

Daher erfordert Invoke-NativeShell keine besondere Berücksichtigung: PowerShell setzt, wie bereits, $LASTEXITCODE basierend auf dem Exit-Code, der vom ausführbaren Prozess der nativen Shell gemeldet wird ( cmd / sh ) aufgerufen.

Dann wirkt sich der im RFC vorgeschlagene Mechanismus darauf aus, wie er auf direkt aufgerufene ausführbare Dateien wirkt (z. B. wenn $PSNativeCommandErrorAction = 'Stop' ist, würde ein Skriptbeendigungsfehler auftreten, wenn $LASTEXITCODE ungleich Null ist. )

(Eine kurze Seite - diese Diskussion gehört nicht hierher: Wie für einen Per-Call-Mechanismus: so etwas wie
/bin/ls nosuch || $(throw 'ls failed) funktioniert ebenso wie /bin/ls nosuch || $(exit $LASTEXITCODE) (entspricht dem /bin/ls nosuch || exit einer POSIX-Shell); # 10967 diskutiert, warum wir $(...) leider nicht vermeiden können (ohne größere Änderungen an der Grammatik); Ebenso kann es wahrscheinlich nicht vermieden werden, explizit auf $LASTEXITCODE verweisen zu müssen.)

(Nebenbei bemerkt: Das Erstellen eines Shell-Skripts mit der Shebang-Zeile #!/bin/sh bedeutet, dass die System-Shell genauso explizit auf den vollständigen Pfad ausgerichtet wird wie das direkte Aufrufen von /bin/sh ).

Das wollte ich nicht sagen. Ich wollte sagen, dass PowerShell nicht wissen muss, was die Standard-Shell ist, da Betriebssysteme diese Funktion integriert haben. Linux weiß, wie ein ausführbares Skript ausgeführt wird, falls es nicht mit #! beginnt oder mit beginnt etwas anderes, wie #!/usr/bin/env python und Windows weiß, was zu tun ist, um ein .CMD -Skript aufzurufen, also sollten wir IMHO auch nicht in %COMSPEC% 'n'stuff hineinschauen. Windows kann auch andere Skripte ausführen, basiert jedoch auf der Erweiterung der Skriptdatei, sodass dies offensichtlich nicht für diesen Fall gilt.

Auch hier sollte das Mandat von Invoke-NativeShell , die _CLI_ der nativen Shell aufzurufen und keine zusätzlichen Skriptdateien (mit Nebenwirkungen) hinter den Kulissen zu erstellen (außer aus verschiedenen Gründen, wie oben erläutert).

In Bezug auf die zuverlässige Bestimmung der System-Shell gibt es hier kein Problem zu lösen: Die Hardcodierung von /bin/sh unter Unix ist ebenso angemessen wie die Beratung von $env:ComSpec unter Windows.

Nein, Unix-Plattformen auf Systemaufrufebene wissen nicht, wie eine ausführbare Klartextdatei ohne Shebang-Zeile ausgeführt werden soll. Dass Sie solche Dateien immer noch aufrufen können, ist eine praktische Funktion von POSIX-ähnlichen Shells: Sie versuchen exec und fallen dann zurück, um solche Dateien so zu interpretieren, wie sie für sie geschrieben wurden. Das heißt, es ist die POSIX-ähnliche Shell, die ausgeführt wird, die am Ende die Datei ausführt - während PowerShell einfach fehlschlägt, weil es diesen Höflichkeits-Fallback nicht implementiert (Sie erhalten Program 'foo' failed to run: Exec format error ).

Es ist daher nicht ratsam, solche Skripte zu erstellen.

Nein, Unix-Plattformen auf Systemaufrufebene wissen nicht, wie eine ausführbare Klartextdatei ohne Shebang-Zeile ausgeführt werden soll. Dass Sie solche Dateien immer noch aufrufen können, ist eine praktische Funktion von POSIX-ähnlichen Shells: Sie versuchen exec und fallen dann zurück, um solche Dateien so zu interpretieren, wie sie für sie geschrieben wurden. Das heißt, es ist die POSIX-ähnliche Shell, die ausgeführt wird, die letztendlich die Datei ausführt - während PowerShell einfach fehlschlägt, weil es diesen Höflichkeits-Fallback nicht implementiert (Sie erhalten Program 'foo' failed to run: Exec format error ).

csh ist nicht POSIX-ähnlich und schlägt bei exec einem bloßen Skript nicht fehl. Es ruft sh . perl .

Ich hoffe, es ist offensichtlich, dass dies meinen Punkt macht: Es liegt an jeder Shell, zu entscheiden, was zu tun ist, und verschiedene Shells / Skriptsprachen tun unterschiedliche Dinge; PowerShell schlägt derzeit nur fehl - wenn eine Auswahl getroffen werden musste und Sie wollten, dass diese Auswahl /bin/sh , sind wir wieder auf dem ersten Platz: /bin/sh muss angenommen werden.

Ich hoffe, es ist offensichtlich, dass dies meinen Punkt macht: Es liegt an jeder Shell, zu entscheiden, was zu tun ist, und verschiedene Shells / Skriptsprachen tun unterschiedliche Dinge; PowerShell schlägt derzeit nur fehl - wenn eine Auswahl getroffen werden musste und Sie wollten, dass diese Auswahl /bin/sh , sind wir wieder auf dem ersten Platz: /bin/sh muss angenommen werden.

exec in perl ist ein direkter Systemaufruf, perl ändert ihn in keiner Weise. Insbesondere wählt es den Interpreter nicht aus.

Dies setzt natürlich voraus, dass Benutzer den Unterschied zwischen einfach (wörtlich) und doppelt (interpolierend) angegebenen Zeichenfolgen und der selektiven ` -Escaping von $ , aber ich denke, dass dies wichtig ist (erweiterte) Option zu haben.

Vielleicht bin ich es nur, aber ich würde tausend " gegen ein -f .

@ yecril71pl

exec in perl ist ein direkter Systemaufruf, perl ändert ihn in keiner Weise. Insbesondere wird der Interpreter nicht ausgewählt.

Das kann nicht wahr sein, wenn es mit Skripten ohne #! Zeile funktioniert. Der exec syscall unter Linux funktioniert nicht ohne #! , da man leicht testen kann:

user@Programming-PC:/tmp/exec_test$ ls
test.c  testscript
user@Programming-PC:/tmp/exec_test$ cat test.c
#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv) {
  char *binaryPath = "./testscript";

  execl(binaryPath, binaryPath, NULL);

  perror("Error");

  return 0;
}
user@Programming-PC:/tmp/exec_test$ gcc test.c -o testexecutable
user@Programming-PC:/tmp/exec_test$ chmod +x testscript 
user@Programming-PC:/tmp/exec_test$ cat testscript 
echo test from script
user@Programming-PC:/tmp/exec_test$ #works from shell:
user@Programming-PC:/tmp/exec_test$ ./testscript 
test from script
user@Programming-PC:/tmp/exec_test$ #doesn't work via exec syscall:
user@Programming-PC:/tmp/exec_test$ ./testexecutable 
Error: Exec format error
user@Programming-PC:/tmp/exec_test$ vim testscript 
user@Programming-PC:/tmp/exec_test$ cat testscript 
#!/bin/sh
echo test from script
user@Programming-PC:/tmp/exec_test$ #exec syscall works with #! line:
user@Programming-PC:/tmp/exec_test$ ./testexecutable 
test from script
user@Programming-PC:/tmp/exec_test$ uname -a
Linux Programming-PC 4.4.0-176-generic #206-Ubuntu SMP Fri Feb 28 05:02:04 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
user@Programming-PC:/tmp/exec_test$ 

Bearbeiten: Ich habe gerade bemerkt, dass, wenn Sie die Bibliotheksfunktion execlp dann laut Manpage:

Wenn der Header einer Datei nicht erkannt wird (die versuchte Ausführung (2) ist mit dem Fehler ENOEXEC fehlgeschlagen), führen diese Funktionen die Shell (/ bin / sh) mit dem Pfad der Datei als erstem Argument aus. (Wenn dieser Versuch fehlschlägt, wird keine weitere Suche durchgeführt.)

Dies ist jedoch keine Funktion von "Linux", sondern der Gnu C-Bibliothek - und laut Manpage wird explizit / bin / sh und keine benutzerdefinierbare Shell verwendet. (Hardcodiert in der Quelle von posix/execvpe.c ( new_argv[0] = (char *) _PATH_BSHELL; ) und sysdeps / unix / sysv / linux / paths.h (oder sysdeps / generic / paths.h - Ich weiß nicht, welcher Header ist gebraucht ...) ( #define _PATH_BSHELL "/bin/sh" ))

Dies ist jedoch keine Funktion von "Linux", sondern der Gnu C-Bibliothek - und laut Manpage wird explizit / bin / sh und keine benutzerdefinierbare Shell verwendet. (Hardcodiert in der Quelle von posix/execvpe.c ( new_argv[0] = (char *) _PATH_BSHELL; ) und sysdeps / unix / sysv / linux / paths.h (oder sysdeps / generic / paths.h - Ich weiß nicht, welcher Header ist gebraucht ...) ( #define _PATH_BSHELL "/bin/sh" ))

Da es von sysdeps/**/linux , ist es eine Linux-Sache. Es kann auch die generische Sache sein, aber es ist überschreibbar - offensichtlich nicht lokal, sondern pro Betriebssystem.

Ja, ich verstehe das aktuelle Verhalten @ vexx32 . Aber wir reden nicht (zumindest nicht) über das aktuelle Verhalten von --% - wir reden darüber, es zu verbessern, nein? Warum habe ich das Gefühl, dass wir hier alle miteinander reden? :) :)

Die aktuelle Tonhöhe für die Anpassung von --% ist also, dass, wenn sich dort normalerweise der Befehlsname befindet, alles rechts davon analysiert wird, wie es ist (bis zu einer neuen Zeile) und direkt an bash / cmd gesendet wird. Es gibt keinen Platz für eine PowerShell-Syntax wie hier-Strings und was nicht, weil es dann dieselben Probleme gibt, die --% und der native Befehlsprozessor derzeit haben.

Oh Herr ... Versuchen alle nur, mich zu trollen? 😄 Nein, Patrick - wenn Sie Powershell-Ausdrücke verwenden möchten, verwenden Sie Here-Strings mit --% . Wenn Sie mehrzeilig sein möchten, verwenden Sie hier-Zeichenfolgen. Wenn Sie mehrzeilig ohne Variablensubstitution möchten, können Sie hier einfache Anführungszeichen verwenden.

Wie auch immer, ich habe keine Haut in diesem Spiel und ich denke, ich habe genug gesagt. Ich denke immer noch, dass etwas ernsthaft falsch daran ist, ein Cmdlet zu verwenden, um eine andere Shell aus einer Shell heraus zu verschieben. Es ist nur ungeschickt. Es ist besonders umständlich, wenn Sie ein Cmdlet verwenden, um eine andere Shell auszuführen und einen nativen Befehl auszuführen, für den diese sekundäre Shell überhaupt nicht erforderlich ist. Das Ganze riecht einfach schlecht.

bearbeiten: letztes Wort

Der Titel dieses Elements lautet "Native Operator anrufen". Ich sehe eine verpasste Gelegenheit, wenn wir bereits einen Anrufbetreiber & und wir bereits ein Token haben, das dafür ausgelegt ist, Argumente an native Befehle zu übergeben, --% . Im Moment wird die Verwendung von & mit --% NICHT erkannt und führt daher NICHT zu einer grundlegenden Änderung, falls wir uns entscheiden, dieses Szenario zu aktivieren und es spezifisch zu verhalten, um die hier diskutierten Probleme zu beheben.

Wenn Sie Powershell-Ausdrücke verwenden möchten, verwenden Sie Here-Strings mit --% . Wenn Sie mehrzeilig sein möchten, verwenden Sie hier-Zeichenfolgen.

Der Punkt des Operators ist, dass er keine PowerShell-Syntax

Wenn Sie zum Beispiel Folgendes hatten:

--% @'
echo my native command
'@

es würde bedeuten:

cmd.exe /c "@'"
echo my native command
'@

Es sei denn, Sie erhalten einen Parserfehler, da '@ nur der Anfang einer Zeichenfolgenkonstante in Anführungszeichen ist, die @ enthält .

Ich denke, vielleicht stellen Sie sich die Implementierung anders vor, so dass sie mehr oder weniger der Tonhöhe für ins , aber die obige ist die aktuelle Tonhöhe, die für --% diskutiert wird.

Erwähnenswert ist auch, dass auf diese Weise auch die vorhandene Funktionalität funktioniert. Dieses Beispiel funktioniert meistens genauso:

cmd /c --% @'
echo my native command
'@

Ich wiederhole:

Im Moment wird die Verwendung von & with -% NICHT erkannt / undefiniert und führt daher NICHT zu einer Änderung, falls wir uns entscheiden, dieses Szenario zu aktivieren und es spezifisch zu verhalten, um die hier diskutierten Probleme zu beheben .

Lassen Sie es mich noch klarer formulieren: Lassen Sie die Zeichenfolgen hier --% folgen, wenn Sie sie mit &

Hah, das ist eine große Sache - ja, das habe ich verpasst, sorry @oising.

Über diese Idee denke ich, dass es ein wenig verwirrend wäre, wenn --% in einigen Kontexten aufhören würde zu analysieren und in anderen "bessere Arbeit bei der Bindung nativer Argumente" leisten würde (ebenso, wenn es wie Invoke-NativeShell funktioniert).

Vielen Dank, dass Sie tiefer in exec , @TSlivede gegraben haben. Lassen Sie mich versuchen, diese Tangente zusammenzufassen und daher hoffentlich zu schließen. gilt sowohl für Linux als auch für macOS:

Unter Unix exec im Namen (die meisten davon als Präfix).

  • Das _system_ funktioniert ( man section 2 ), execve im Herzen, _fail_, wenn Sie versuchen, ein Skript ohne Shebang auszuführen.

  • Unter den Bibliotheksfunktionen, die auf den Systemfunktionen aufgebaut sind ( man Abschnitt 3 - man 3 exec ), nur diejenigen, die p (für "Pfad", I. Angenommen, Sie haben den Fallback für die Weitergabe an die System-Shell (z. B. execlp ).

    • Die GNU-Bibliotheksimplementierungen dieser Funktionen codieren /bin/sh hart, wie @TSlivede gezeigt hat, während die BSD-basierten nur sh und sich auf sh , um dabei zu sein $env:PATH (obwohl auf der Seite man seltsamerweise /bin/sh ).

Wie bereits erwähnt, greifen die wichtigsten POSIX-ähnlichen Shells ( bash , dash , ksh und zsh ) für die direkte Ausführung auf die Ausführung von Skripten ohne Shebang zurück sich selbst, was impliziert, dass sie die Fallback-Varianten unter den exec Bibliotheksfunktionen nicht verwenden; Im Gegensatz dazu entschied sich perl für seine eigene exec -Funktion, sich auf den Fallback zu verlassen. In ähnlicher Weise hängen die exec _builtins_ der wichtigsten POSIX-ähnlichen Shells vom Fallback ab, außer in bash .

Da unter Unix die Verwendung einer Skriptdatei hinter den Kulissen nicht erforderlich ist, können wir dieselben Annahmen über den System-Shell-Pfad treffen wie die Funktionen der Fallback-Bibliothek, und ich empfehle /bin/sh über sh für Sicherheit und Vorhersehbarkeit:

Trotz der POSIX-Spezifikation _not_, die den Speicherort von sh vorschreibt (das heißt, genau genommen sind die BSD-Bibliotheksfunktionen die kompatiblen):

  • /bin/sh ist sicher anzunehmen, da es sich um den _de facto_ Standardspeicherort handelt, nicht zuletzt, weil das Schreiben einer tragbaren Unix-Shell _notwendig_ auf sh über den vollständigen Pfad verweist, da Shebang-Zeilen nur _voll, wörtlich_ unterstützen Pfade ( #!/bin/sh ).

  • Umgekehrt kann es ein Sicherheitsrisiko sein, sich auf das Auffinden von sh über $env:PATH zu verlassen, da $env:PATH manipuliert werden könnte, um ein anderes sh aufzurufen.

Das gleiche Risiko besteht übrigens für das Auffinden von cmd.exe über $env:ComSpec Vielleicht ist es also besser, die WinAPI-Funktion GetSystemDirectory aufzurufen und \cmd.exe an anzuhängen sein Ergebnis (wir brauchen sowieso einen Fallback, wenn $env:ComSpec nicht definiert ist).

/bin/sh mit Sicherheit angenommen werden, da es sich um den de facto Standardspeicherort handelt, nicht zuletzt, weil das Schreiben einer tragbaren Unix-Shell _notwendig_ auf sh über den vollständigen Pfad verweist, da Shebang-Zeilen nur _voll, wörtlich_ unterstützen Pfade ( #!/bin/sh ).

Normalerweise sagen Sie #!/usr/bin/env sh (außer in Systemskripten).

  • Normalerweise liefert das Nicht-Googeln von "#! / Bin / sh" ungefähr 3.900.000 Übereinstimmungen, "#! / Usr / bin / env sh" ungefähr 34.100 Übereinstimmungen.

  • Normalerweise sollten Sie nicht: Sie möchten vorhersehbar auf die System-Shell abzielen, /bin/sh , nicht das Dienstprogramm sh , das in $env:PATH an erster Stelle steht.

Der einzige Grund, auf eine ausführbare Datei mit dem Namen sh zu zielen, besteht darin, portabel auf die _system_-Shell mit dem kleinsten gemeinsamen Nenner und der Annahme zu gehen, dass nur POSIX-Funktionen vorhanden sind, dh /bin/sh .

Normalerweise liefert das Nicht-Googeln von "#! / Bin / sh" ungefähr 3.900.000 Übereinstimmungen, "#! / Usr / bin / env sh" ungefähr 34.100 Übereinstimmungen.

Es gibt keine Möglichkeit, eine Websuche auf Benutzerskripte zu beschränken, insbesondere da viele Benutzerskripte überhaupt nicht als ausführbar markiert sind und selbst wenn dies der Fall ist, sie sich möglicherweise auf execlp . Aber selbst wenn die meisten Benutzerskripte dies sagten, sollten wir nicht _normal_ für _normal_ nehmen. Die von PowerShell auszuführenden Skripts sind Benutzerskripte. Wenn Benutzer eine Shell möchten, nennen sie sh , nicht /bin/sh , es sei denn, sie sind paranoid.

@oising

Übergeben der Native-Shell-Befehlszeile als einzelne Zeichenfolge (in welcher Zeichenfolge (-literal) am einfachsten ist) und nur als einzelne Zeichenfolge ( oben diskutiert) - vorausgesetzt, Sie stimmen dem zu - klingt nach Gemeinsamkeiten.

Das Übergeben der Befehlszeile als einzelne Zeichenfolge ist die Voraussetzung für:

  • in der Lage zu sein, einen solchen Aufruf in eine PowerShell-Pipeline aufzunehmen.
  • _PowerShell-Variablen und Ausdruck_ in die Native-Shell-Befehlszeile integrieren können.

Das Übergeben einer einzelnen Zeichenfolge ist ein direkter konzeptioneller Ausdruck dessen, was der Aufruf bewirkt. Im Gegensatz dazu bringt der Versuch, die Syntax einer anderen Shell mit _individuellen_ Argumenten (optional mit bloßen Worten) direkt in PowerShell zu integrieren, unlösbare Probleme mit sich, die zu einem verwirrenden Kompromiss mit grundlegenden Einschränkungen führen, den das vorhandene --% bereits verkörpert.


Während Sie argumentieren könnten, dass es dann nicht so wichtig ist, ob wir einen Operator oder ein Cmdlet auswählen, an das diese einzeilige Befehlszeile übergeben wird (abgesehen von Fragen der Entdeckbarkeit), habe ich konzeptionelle Bedenken hinsichtlich der Verwendung beider & und --% (vorangestellt von & ):

  • Das Problem bezüglich & : Wenn wir eine Zeichenfolge in Anführungszeichen übergeben müssen, die die Befehlszeile enthält, arbeiten wir effektiv im Ausdrucksmodus, während der & derzeit den _argument_- Modus impliziert (was bedeutet: _individuelle_ Argumente, die nur bei Bedarf zitiert werden). Daher ist die Beteiligung von & verwirrend.

  • Die Sorge bezüglich --% betrifft den Nur-Windows-Ursprung und die durcheinandergebrachte Semantik von --% als Stop-Parsing-Symbol. Es wäre besonders verwirrend, wenn --% als Stop-Parsing-Symbol im _argument_-Modus betrieben würde (z
    & /bin/ls --% dir1 dir2 ), während & --% im _expression_-Modus betrieben wird (z. B. & --% '/bin/ls dir1 dir2 >out.txt' )


Lassen Sie mich einen Schritt zurücktreten und --% , das Stop-Parsing-Symbol, wie es derzeit implementiert ist:

Es war ein Versuch, zwei letztendlich nicht miteinander verbundene Probleme zu lösen (damals nur Windows):

  • (a) Ein Anwendungsfall, der auch für andere Plattformen gilt: Lassen Sie mich Befehlszeilen wiederverwenden, die für cmd.exe / die native Shell unverändert geschrieben wurden, damit ich sie nicht an die PowerShell-Syntax anpassen muss.

  • (b) Ein Problem, das immer nur für Windows gilt: Lassen Sie mich eine _rogue-CLI_ aufrufen (dh einen Aufruf einer _exter_ externen Konsolenanwendung), die einen ganz bestimmten Zitierstil erfordert, den PowerShells generisches erneutes Zitieren hinter den Kulissen nicht bieten kann , indem ich die Befehlszeile erstellen durfte, die letztendlich für solche CLIs _verbatim_ an die WinAPI übergeben wurde. (Um die Problemstellung darauf zu beschränken, wird davon ausgegangen, dass das erneute Zitieren von PowerShell im Allgemeinen ordnungsgemäß funktioniert, was es nie getan hat - das ist das Thema von # 1995).

--% wurde als durcheinandergebrachte _Konflation_ von Lösungsversuchen zu (a) und (b) implementiert und löste am Ende keines der beiden Probleme richtig:

  • Es wurde als Lösung für (a) beworben, weist jedoch in Wirklichkeit schwerwiegende Einschränkungen auf, da _die einzige cmd exe-Funktion, die es emuliert, die Erweiterung von Verweisen auf Umgebungsvariablen im Stil von %...% :

    • Es wird immer nur ein _single_ cmd Befehl unterstützt, vorausgesetzt, ein | (und
      && und || , obwohl sie zu diesem Zeitpunkt noch nicht implementiert waren, beenden implizit den Pass-to- cmd Teil.

    • Ein _single_ & - das ist cmd s Trennzeichen für mehrere Befehle - wurde durchlaufen, aber auch das führte nicht zu einer Unterstützung für mehrere Befehle, weil - da cmd nicht ist. t tatsächlich beteiligt - das & wird _verbatim an das Zielprogramm_ übergeben; z.B
      echoArgs.exe --% a & b gibt a und ruft dann b , sondern übergibt wörtliche Argumente a , & und b bis echoArgs .

    • cmd Escape-Zeichen von ^ ) in nicht zitierten Argumenten wird nicht berücksichtigt.

    • Als Konsequenz werden %...% Token, die sich auf vorhandene Umgebungsvariablen beziehen, _variabel_ erweitert; Ein Versuch, dies über ^ zu verhindern, funktioniert nicht richtig (z
      echoArgs.exe Don't expand %^USERNAME% , das in cmd die Expansion verhindert und die ^ _ entfernt, behält die ^ wenn es von PowerShell als echoArgs.exe --% Don't expand %^USERNAME% aufgerufen wird

  • Als Lösung für (b) fiel es ebenfalls zu kurz:

    • Genau wie bei (a) gibt es keine Möglichkeit, _PowerShell_-Variablen und -Ausdrücke in den Befehl aufzunehmen - es sei denn, Sie definieren umständlich zuerst ein _aux. Umgebungsvariable_, auf die Sie dann über %...% nach --% verweisen; z.B
      $env:FOO='foo'; echoArgs.exe --% %FOO%!

Die richtige Lösung für (a) wäre Invoke-NativeShell / ins , wie hier beschrieben.

Die richtige Lösung für (b) wäre gewesen:

  • unterstütze --% so speziell wie das erste Argument
  • und genau wie bei ins muss ein einzelner String folgen, der die Pass-Through-Befehlszeile als Ganzes bildet, und zwar mit der Fähigkeit, PowerShell-Variablen und -Ausdrücke durch String-Interpolation (oder) einzubeziehen andere Möglichkeiten zum Erstellen der Zeichenfolge)
  • Beispiel: msiexec --% "/i installer.msi INSTALLLOCATION=`"$loc`"" , wobei $loc 'c:\foo\bar none' , würde dazu führen, dass die wörtliche Befehlszeile /i installer.msi INSTALLLOCATION="c:\foo\bar none" durchlaufen wird, was die nicht standardmäßigen Anforderungen von msiexec erfüllt dass in <prop>=<value> Argumenten nur der <value> Teil in doppelte Anführungszeichen gesetzt wird.

Das ist zwar nicht praktisch, aber es ist der Preis für eine robuste Lösung der Anarchie, die ein befehlszeilenbasiertes Argument ist, das unter Windows weitergegeben wird.

@ mklement0 Vielen Dank für die geduldige und nachdenkliche Antwort (wie immer).

Also stimme ich voll und ganz zu;

Die richtige Lösung für (b) wäre gewesen:

  • support -% so speziell wie das erste Argument
  • und genau wie bei ins muss von einer einzelnen Zeichenfolge gefolgt werden, die die gesamte Pass-Through-Befehlszeile bildet String konstruieren)

    • Beispiel: msiexec -% "/ i installer.msi INSTALLLOCATION = "$loc " ", wobei $ loc 'c: foobar none' enthält, würde zu einer wörtlichen Befehlszeile führen / i installer.msi INSTALLLOCATION =" c: foobar none "wird durchlaufen und erfüllt die nicht standardmäßige Anforderung von msiexec, dass in=Argumente nur dieTeil doppelt zitiert werden.

Dies ist, was ich versucht habe, als verallgemeinerte Lösung für (a) und (b) zu erläutern, außer ich verstehe immer noch nicht wirklich, dass (a) als unabhängiges Problem existieren sollte, um es zu lösen? Was ich wirklich zu kämpfen habe, ist, wenn (b) spezifisch für & implementiert ist, warum kann das nicht auch (a) abdecken? zB Was gibt ins ... , was & cmd --% ... nicht kann? Und es wird keine bahnbrechende Änderung sein, außer dass & --% als Argument übergeben wird (was lächerlich unwahrscheinlich erscheint). Bonus, dass Sie sich keine Sorgen um comspec machen müssen, Muscheln oder Wasauchimmer. Lassen Sie den Anrufer entscheiden.

@ PowerShell / Powershell-Komitee hat dies überprüft:

  • Dies ist eine experimentelle Funktion , um echtes Nutzungsfeedback zu erhalten. wir sind immer noch offen für das eigentliche Siegel; Wir verschieben /bin/sh vs /bin/env sh auf die PR-Überprüfung
  • Wir werden --% als neuen Operator speziell für das "Stackoverflow" -Szenario weiterentwickeln, bei dem der Benutzer eine Befehlszeile ausschneidet und in PowerShell einfügt und diese einfach funktionieren sollte (natürlich mit dem neuen Operator davor) )
  • Ein Invoke-NativeShell Cmdlet ist von diesem Vorschlag getrennt und kann von der Community in PowerShellGallery veröffentlicht werden. Das Erfordernis einer Here-Zeichenfolge löst das Problem der Benutzererfahrung nicht, ohne vorher zu wissen, ob sie als Here-Zeichenfolge verpackt werden soll
  • Wir möchten keine wichtige Änderung am Schalter --% vornehmen, bei der vorhandene Benutzer möglicherweise von diesem Verhalten abhängen
  • Benutzer, die ein Streaming-Verhalten wie --% switch benötigen, sollten diesen Schalter weiterhin verwenden und Sonderzeichen nach Bedarf umgehen
  • Es gibt immer noch ein Entdeckungsproblem für Benutzer, die über --% (oder eine andere Lösung) Bescheid wissen müssen.
  • Derzeit wird keine Änderung an PSReadLine vorgenommen, um eingefügten Inhalt als Hier-Zeichenfolge zu ändern und die Absicht des Benutzers vorherzusagen
  • Wir betrachten nicht mehrere Lösungen, die Here-String- oder Non-Here-String-Szenarien (Erweiterungsszenarien) ermöglichen. zB & --% vs --%

Ich möchte auch hinzufügen, dass es trotz @ SteveL-MSFT und dem Schließen von # 1995 zur gleichen Zeit, als ich dieses öffnete, nicht unsere Absicht war, dies als endgültige Lösung für dieses Problem zu kommunizieren.

Ich bin der Ansicht, dass die Vorteile dieser besonderen Funktion viel bedeutender sind als die Auswirkungen # 1995. Ich denke, es gibt eine Menge StackOverflow-Beispiele und Dokumentbeispiele für nicht PS-erleuchtete Tools, die Leute gerne ausschneiden / einfügen würden (ich selbst eingeschlossen).

Es besteht außerdem der Wunsch, eine ordnungsgemäße Flucht in die eigene Sprache von PowerShell zu ermöglichen, aber ich sehe nicht, dass Leute in der Wildnis massenhaft # 1995 treffen (aber ich werde dort drüben näher darauf eingehen).

  • Wir werden --% als neuen Operator weiterentwickeln

Es tut mir leid, dies herauszuziehen, und ich suche keine zusätzliche Diskussion, aber welche Art von Operator? (Ich interessiere mich hauptsächlich für Syntax.)

$a = --% find . -iname *$pdf -print0 | xargs -0 ls -ltr

Wer bekommt die Pfeife? Was passiert mit dem $ ? Ähnlich verhält es sich mit & , && und || .

Unter welchen Bedingungen kann --% in einer PS-Pipe verwendet werden, wenn diese vorwärts geht?

Vielen Dank.

Es tut mir leid, dies herauszuziehen, und ich suche keine zusätzliche Diskussion, aber welche Art von Operator? (Ich interessiere mich hauptsächlich für Syntax.)

AST-weise wird es wahrscheinlich technisch gesehen kein Operator sein, aber es ist ein eigener AST-Typ. Wenn es als Operator implementiert wird, wäre es ein unärer Operator.

$a = --% find . -iname *$pdf -print0 | xargs -0 ls -ltr

Wer bekommt die Pfeife? Was passiert mit dem $ ? Ähnlich verhält es sich mit & , && und || .

Die Zeichenfolge find . -iname *$pdf -print0 | xargs -0 ls -ltr unverändert an die native Shell gesendet. $a würde trotzdem gefüllt sein, so dass Sie in der nächsten Zeile das zu etwas weiterleiten könnten.

Unter welchen Bedingungen kann --% in einer PS-Pipe verwendet werden, wenn diese vorwärts geht?

Wenn ~ im ersten Befehlselement-Slot ~ am Anfang einer Anweisung steht, wahrscheinlich nicht. Das ist einer der Vorteile der neuen Syntax. Wenn nicht ~ im ersten Slot ~ zu Beginn, wird es weiterhin so funktionieren wie heute.

(@ daxian-dbw @ SteveL-MSFT Wenn etwas davon falsch ist, korrigieren Sie bitte ❤️)

@SeeminglyScience ist genau richtig

Wenn im ersten Befehlselement-Slot, wahrscheinlich nicht. Das ist einer der Vorteile der neuen Syntax. Wenn nicht im ersten Slot, funktioniert es weiterhin wie heute.

Es kann schwierig sein, die Ausgabe des Upstream-Befehls an den nativen Befehl zu übergeben, der von --% aufgerufen wird. --% übergibt den String im Grunde nur an eine native Shell, wie bash , aber die Ausgabe des Upstream-Befehls kann nicht nur an bash , sondern sollte an die nativer Befehl selbst, z. B. find .

Ich denke, Streaming-Verhalten spielt bei dieser Funktion keine Rolle, da es sich hauptsächlich um das Szenario "StackOverflow" handelt. Ich denke, wir sollten --% einer besonderen Aussage machen, damit klar ist, dass es mit _PowerShell_-Pipelines nicht funktioniert.

Ahh, ich wusste, dass es einen Grund gab, warum "Befehlselement-Slot" falsch war. Ich meinte zu Beginn einer Aussage. Ich werde es reparieren, danke!

Die Ausgabe des Upstream-Befehls kann nicht einfach an bash

Bedeutet _upstream_ _us_? Ich denke kann: 'ls' | bash . Keine besonders kluge Sache, aber möglich.

Ich habe mit #! als Siegel vorgeschlagen. In diesem Fall würden Sie einfach Folgendes eingeben:

#!/bin/sh ls | grep foo
#!sh | grep foo

(wobei im zweiten Fall davon sh wird, dass sich sh zweimal gestartet. Es gibt jedoch ein Problem damit, da Shebang-Dateien heute funktionieren. Sie funktionieren in PowerShell, da der Shebang als Kommentar ignoriert wird. Wenn wir uns entscheiden, die erste Zeile in einem Skript zu ignorieren, wenn es sich um einen Kommentar handelt, haben wir immer noch ein Problem in der interaktiven Shell, bei der jeder neue Satz von Zeilen ein neues Skript ist und es heute keine Möglichkeit gibt, festzustellen, ob dieses Skript als ausgeführt wird Skript oder interaktiv.

Eine Alternative könnte darin bestehen, die Markdown-Syntax auszuleihen:

powershell ```bash ls | grep foo ```

In diesem Beispiel würden wir die Markdown-Code-Fencing-Syntax verwenden. Dies würde auch das Mehrzeilenproblem lösen und ein Konzept verwenden, das nicht ganz neu ist. Angesichts der Tatsache, dass wir immer noch ein anfängliches Erkennungsproblem für neue Benutzer haben, denke ich nicht, dass dies umso schlimmer ist, als Sie die nachfolgenden Triple-Backticks benötigen.

In diesem Fall könnte dies möglicherweise funktionieren:

powershell $a = ```bash ls | grep foo ``` | select-string bar

@ SteveL-MSFT Markdown Syntax macht sehr viel Sinn. Wenn wir spezifizieren können. auch andere Typen wie Python oder vielleicht durch Erweiterung?

Der Schebang #! Würde bestehende Kommentare stören.

Das scheint mir massiv überkompliziert zu sein, sowohl beim Parsen als auch bei der tatsächlichen Verwendung durch Benutzer.

Und ja, wir müssen die Leute nicht mit Zeilen verwechseln, die Kommentare sein sollten, die sich nicht als Kommentare herausstellen. #Requires ist schon etwas komisch, ein voller Shebang wird Windows-Leute bestenfalls verwirren, und wahrscheinlich auch Linux-Leute.

Ich persönlich würde es vorziehen, dieses Feature nicht mit Shebang zu mischen, es ist meiner Meinung nach verwirrend.
Wenn wir anfangen, die Markdown-Codeblock-Syntax zu verwenden, ist es für die Benutzer nur eine Frage der Zeit, die Ausführung von beliebigem Sprachcode direkt in PowerShell anzufordern. Ich glaube nicht, dass wir diesen Weg gehen wollen ...

Wenn wir anfangen, die Markdown-Codeblock-Syntax zu verwenden, ist es für die Benutzer nur eine Frage der Zeit, die Ausführung von beliebigem Sprachcode direkt in PowerShell anzufordern.

Ich würde buchstäblich nie aufhören, nach Inline-C # zu fragen, obwohl ich weiß, dass es eine schlechte Idee ist.

Die Markdown-Syntax führt keinen beliebigen Sprachcode ein, da das eingebettete Skript in einem untergeordneten Prozess ausgeführt wird. Sie können jetzt beliebigen Sprachcode in @' einfügen, und es passiert nichts Schlimmes.

In der Teamdiskussion und auch mit Jeffrey sollten wir nicht gegen Leute sein, die andere Sprachen in einem PowerShell-Skript ausführen möchten. Wie @ yecril71pl feststellte, unterstützen wir andere Sprachen nicht direkt, sondern verlassen uns auf einen anderen Prozess, um diesen Code zu interpretieren und auszuführen. Das Szenario hier ist, dass ein Benutzer einen Python- oder Bash-Skriptblock sieht und ihn nur so verwenden möchte, wie er in seinem PowerShell-Skript ist. Es gibt nichts, was die Leute dazu verpflichtet. Sie können entscheiden, ob sie dieses andere Skript auf PowerShell portieren möchten, wenn sie dies bevorzugen. Dies bietet Benutzern lediglich die Möglichkeit, in PowerShell auszuschneiden und einzufügen, um Aufgaben zu erledigen.

Wir müssen die einzeiligen und mehrzeiligen Vorschläge nicht mischen, da wir beide unterstützen könnten, obwohl der Nachteil jetzt ist, dass wir zwei Möglichkeiten haben, sehr ähnliche Dinge zu tun, aber mit unterschiedlicher Syntax.

Ich würde es vorziehen, das einzeilige Zeug fallen zu lassen. Außerirdische Codeinseln sollten prominent sein, sonst wird niemand verstehen, was los ist.

@ yecril71pl Ich

In der Teamdiskussion und auch mit Jeffrey sollten wir nicht gegen Leute sein, die andere Sprachen in einem PowerShell-Skript ausführen möchten.

Absolut 100%, aber das bedeutet nicht, dass es direkt in die Sprache eingebaut werden muss.

Wie @ yecril71pl feststellte, unterstützen wir andere Sprachen nicht direkt, sondern verlassen uns auf einen anderen Prozess, um diesen Code zu interpretieren und auszuführen. Das Szenario hier ist, dass ein Benutzer einen Python- oder Bash-Skriptblock sieht und ihn nur so verwenden möchte, wie er in seinem PowerShell-Skript ist.

Das geht doch schon mit Here-Strings oder? Können Sie ein wenig erläutern, welche Vorteile dies gegenüber bestehenden Methoden bietet?

Warum auch bei Python and Bash anhalten? Warum nicht C #, CIL, C ++?

Es gibt nichts, was die Leute dazu verpflichtet. Sie können entscheiden, ob sie dieses andere Skript auf PowerShell portieren möchten, wenn sie dies bevorzugen. Dies bietet Benutzern lediglich die Möglichkeit, in PowerShell auszuschneiden und einzufügen, um Aufgaben zu erledigen.

Ich bin nicht dagegen, weil ich denke, dass ich gezwungen sein werde, es zu benutzen. Ich mag es nicht, weil es aus sprachlicher Sicht schwierig sein wird, es zu pflegen, ein Albtraum für Redakteure, und letztendlich Skripte zu fördern, die nicht lesbar sind, wenn Sie nicht mehrere Sprachen beherrschen.


Wirklich, obwohl dies ein ziemlich anderes Konzept ist als das ursprüngliche Thema dieses Threads mit viel umfassenderen Auswirkungen. Ich würde empfehlen, ein neues Problem dafür zu erstellen.

Das geht doch schon mit Here-Strings oder? Können Sie ein wenig erläutern, welche Vorteile dies gegenüber bestehenden Methoden bietet?

Ein universeller Code-Editor kann sie erkennen und entsprechend dekorieren, während mit @' nichts zu tun ist, da der Editor keine Ahnung hat, was sich darin befindet.

Warum auch bei Python and Bash anhalten? Warum nicht C #, CIL, C ++?

Weil sie keine Skriptsprachen sind?

Ein universeller Code-Editor kann sie erkennen und entsprechend dekorieren, während mit @' nichts zu tun ist, da der Editor keine Ahnung hat, was sich darin befindet.

Ein Editor kann erkennen, dass bash -c @' Bash genauso einfach enthält wie ein Codezaun. Der herausfordernde Teil bestimmt auch nicht, welche Sprache ein Codeabschnitt ist, sondern das Jonglieren von Sprachservern und Syntax-Textmarkern. Die einzige Möglichkeit, ohne erheblichen Zeit- und Arbeitsaufwand zu funktionieren, besteht darin, es im Wesentlichen so zu gestalten, als wäre es eine Hier-Zeichenfolge.

Weil sie keine Skriptsprachen sind?

C # und C ++ haben beide skriptorientierte Teilmengen. Selbst wenn dies nicht der Fall wäre, ist "Skriptsprache" subjektiv und ohne wirkliche Definition. Es ist nicht hilfreich als endgültige Grenze für das, was für die Aufnahme in Betracht gezogen werden würde und was nicht.

C # und C ++ haben beide skriptorientierte Teilmengen. Selbst wenn dies nicht der Fall wäre, ist "Skriptsprache" subjektiv und ohne wirkliche Definition.

Eine Sprache ist eine (eigenständige) Skriptsprache, wenn Sie normalerweise interpet options… script arguments… aufrufen und dieser Aufruf nichts außer dem hinterlässt, was er verlassen wollte, ausgenommen temporäre Inhalte und privat für den Dolmetscher. Dies bedeutet auch, dass normalerweise eine Kopie des Interpreters erforderlich ist, um ausgeführt zu werden. Es gibt eingebettete Skriptsprachen, die Sie nicht auf diese Weise ausführen können.

@SeeminglyScience

Das geht doch schon mit Here-Strings oder? Können Sie ein wenig erläutern, welche Vorteile dies gegenüber bestehenden Methoden bietet?

Menschen können heute Here-Strings verwenden oder sich allen Argumenten entziehen, die an native Befehle übergeben wurden, wenn sie herausfinden können, wie sie es richtig machen können. Hier soll es für neue Benutzer einfacher werden, Szenarien zum Ausschneiden und Einfügen (Stackoverflow) zu erstellen.

Warum auch bei Python and Bash anhalten? Warum nicht C #, CIL, C ++?

Die hier unterstützten Szenarien sind ein Textblock, der an einen nativen Befehl übergeben wird. Jede Sprache kann unterstützt werden, wenn sie diese Aufrufkonvention unterstützt. Es gibt keinen Vorschlag, eine temporäre Quelldatei zu erstellen und kompilieren zu lassen.

Ich bin nicht dagegen, weil ich denke, dass ich gezwungen sein werde, es zu benutzen. Ich mag es nicht, weil es aus sprachlicher Sicht schwierig sein wird, es zu pflegen, ein Albtraum für Redakteure, und letztendlich Skripte zu fördern, die nicht lesbar sind, wenn Sie nicht mehrere Sprachen beherrschen.

Es ist nicht zu erwarten, dass Sie eine gemischte Syntaxfarbe erhalten. Jedes verschachtelte Skript aus einer anderen Sprache wird nur monoton.

Wirklich, obwohl dies ein ziemlich anderes Konzept ist als das ursprüngliche Thema dieses Threads mit viel umfassenderen Auswirkungen. Ich würde empfehlen, ein neues Problem dafür zu erstellen.

Die vorgeschlagene Implementierung unterscheidet sich von der ursprünglichen Ausgabe, aber das Problem ist das gleiche, nämlich das Ausschneiden und Einfügen und das Szenario "Just Work".

Menschen können heute Here-Strings verwenden oder sich allen Argumenten entziehen, die an native Befehle übergeben wurden, wenn sie herausfinden können, wie sie es richtig machen können. Hier soll es für neue Benutzer einfacher werden, Szenarien zum Ausschneiden und Einfügen (Stackoverflow) zu erstellen.

Richtig, aber wie ist es eigentlich einfacher? Ich verstehe, dass es subjektiv ist, aber diese scheinen mir nicht so unterschiedlich zu sein:

~~~ Powershell
$ a = `` `bash
finden . -iname * $ pdf -print0 | xargs -0 ls -ltr

~~~

vs

```powershell
$a = bash -c @'
   find . -iname *$pdf -print0 | xargs -0 ls -ltr
'@

Sie scheinen fast gleich zu sein, mit der Ausnahme, dass letzteres deutlich klarer ist, was passieren wird.

Die hier unterstützten Szenarien sind ein Textblock, der an einen nativen Befehl übergeben wird. Jede Sprache kann unterstützt werden, wenn sie diese Aufrufkonvention unterstützt. Es gibt keinen Vorschlag, eine temporäre Quelldatei zu erstellen und kompilieren zu lassen.

Nun, C # würde keine temporäre Quelldatei benötigen. Vielleicht auch nicht CIL (keine Ahnung von C ++). In beiden Fällen ist ein Codezaun für mich verwirrend, obwohl er ausschließlich syntaktischer Zucker zum Aufrufen einer ausführbaren Datei (z. B. bash / python / cmd) ist. Codezäune implizieren Sprachunterstützung imo.

Es ist nicht zu erwarten, dass Sie eine gemischte Syntaxfarbe erhalten. Jedes verschachtelte Skript aus einer anderen Sprache wird nur monoton.

Für Parser auf tmLanguage-Basis wird es immer noch ein Albtraum sein. Sie können kaum mit der Syntax umgehen, die sie von atm erwarten. Mehr als das, wenn das der Fall ist, verstehe ich nicht, warum es sich von einem Here-String unterscheidet.

Die vorgeschlagene Implementierung unterscheidet sich von der ursprünglichen Ausgabe, aber das Problem ist das gleiche, nämlich das Ausschneiden und Einfügen und das Szenario "Just Work".

Fair, aber wir haben bereits 145 Kommentare zur Diskussion der zuvor vorgeschlagenen Lösungen. Vor allem, da dieser Thread bereits zu einem Abschluss gekommen ist, werden Sie wahrscheinlich viel mehr Beteiligung an einem neuen, dedizierten Thread sehen.

Nun, C # würde keine temporäre Quelldatei benötigen. Vielleicht auch nicht CIL (keine Ahnung von C ++). In beiden Fällen ist ein Codezaun für mich verwirrend, obwohl er ausschließlich syntaktischer Zucker zum Aufrufen einer ausführbaren Datei (z. B. bash / python / cmd) ist. Codezäune implizieren Sprachunterstützung imo.

Ich möchte nur den Kommentar von @SeeminglyScience dazu das C # -Skript ausführen, ohne es mit der Bibliothek Microsoft.CodeAnalysis.CSharp.Scripting kompilieren zu müssen. Das ist es, was dotnet-interactive im C # Jupyter-Kernel verwendet. Daher ist es für Benutzer möglicherweise nicht unangemessen, mit einer solchen Syntax nach C # -Skripten oder sogar nach F # -Skripten zu fragen.

Sie können sagen, dass die Code-Fencing-Syntax in PowerShell zum Aufrufen eines nativen Befehls dient, was theoretisch Folgendes ermöglicht:

    ```c:\mypath\MyArbitraryExe
        args to my arbitrary executable
    ```

Aber dann unterscheidet es sich von dem etablierten Verständnis der Benutzer von Markdown, was, wie @SeeminglyScience hervorhob , Sprachunterstützung impliziert. Die Verwendung einer ähnlichen Syntax mit unterschiedlicher Semantik ist meiner Meinung nach verwirrend.

was theoretisch erlaubt

Sie fügen das Skript ein, nicht die Argumente, und das Skript entspricht dem Eingabestream. Wenn dies SO-Unterstützung bieten soll, sollten keine vollständigen Pfade zugelassen werden.

Sie fügen das Skript ein, nicht die Argumente, und das Skript entspricht dem Eingabestream

Das Skript ist das Argument für bash / cmd / python.

Wenn dies SO-Unterstützung bieten soll, sollten keine vollständigen Pfade zugelassen werden.

Dann schalten Sie es für dieses Beispiel:

~ Powershell$ a = ipconfig /all ~

Oder fügen Sie zuerst die beliebige Exe zum Pfad hinzu.

Sie fügen das Skript ein, nicht die Argumente, und das Skript entspricht dem Eingabestream

Das Skript ist das Argument für bash / cmd / python.

Keiner von ihnen erlaubt es, ein Skript als Positionsargument zu übergeben. CMD erlaubt nicht einmal eine Batch-Datei.

Wenn dies SO-Unterstützung bieten soll, sollten keine vollständigen Pfade zugelassen werden.

Dann schalten Sie es für dieses Beispiel:

$ a = `` `ipconfig
/alle

Das würde nicht funktionieren.

Es scheint, als würden wir sehr willkürlich viele Sonderfälle machen, damit diese Abschriftenidee funktioniert.

Der Hauptgrund, warum dies in Markdown sogar unterstützt wird, ist die Hervorhebung der Syntax. Das wird eine ständige Anfrage sein, wenn wir so etwas in die Sprache aufnehmen.

Dies ist keine effektive Lösung.

Ich habe mit #! als Siegel vorgeschlagen

Um meine 2 Cent hinzuzufügen, halte ich es für einen Fehler, diese Diskussion neu zu starten, nachdem eine Lösung angekündigt wurde. und ich persönlich mag sowohl die Shebang- als auch die Markdown-Syntax nicht (und beide brechen Änderungen).

Es macht mir nichts aus, Diskussionen neu zu starten, wenn jemand etwas nachweislich Besseres gefunden hat. In der Tat ist es jetzt besser, dies zu tun, als nachdem die Funktion ausgeliefert wurde. Ich denke einfach nicht, dass #! oder der Markdown-Ansatz eine bessere Lösung ist, um einer nativen Exe eine "nicht PowerShell-verfälschte" Befehlszeilenzeichenfolge bereitzustellen. Vielleicht muss ich es nur versuchen, um mich daran zu gewöhnen, aber meine erste Reaktion ist ... ähm, eew.

_Wenn wir (und PowerShell) wissen, dass wir eine native ausführbare Datei ausführen, warum brauchen wir dann überhaupt "nativen Operator aufrufen"? _

Wenn PowerShell eine Eingabezeichenfolge analysiert und CommandAst abruft, ermittelt PowerShell den Befehl.
Wenn der Befehl ein nativer Befehl ist, konvertiert PowerShell den Ast in NativeCommandProcessor.
Wir können einen neuen (experimentellen NativeCommandProcessor) für den Fall implementieren, der die Ast-Ausdehnung (dh die Eingabezeichenfolge) neu analysiert, um den Namen / Pfad der nativen ausführbaren Datei und den Rest der Zeichenfolge als Argument oder Argumente nach Betriebssystemregeln zu erhalten.

Wenn ein Benutzer das Kopieren und Einfügen aus der Shell möchte, sollte er "cmd" eingeben"oder" Bash"- das funktioniert ohne Bearbeitung.
Wenn ein Benutzer eine native Befehlszeile kopieren und einfügen möchtefunktioniert auch wie g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Wenn ein Benutzer das Kopieren und Einfügen aus der Shell möchte

Wir haben Get-/Set-Clipboard dafür.

für den Fall, dass die Ast-Ausdehnung (das ist die Eingabezeichenfolge) erneut analysiert wird, um den nativen ausführbaren Namen / Pfad und den Rest der Zeichenfolge als Argument oder Argumente nach Betriebssystemregeln abzurufen.

Ich denke hier, dass Sie dies entweder tun:

  • Analysieren Sie den Befehl AST ineffizient, um den Umfang später als Zeichenfolge zu analysieren und den ursprünglichen AST wegzuwerfen, ODER
  • Sie nehmen den AST und versuchen, ihn wieder in einen String umzuwandeln (wie es der aktuelle NativeCommandProcessor tut), aber dabei gehen Dinge verloren

Stattdessen müssen wir die Zeichenfolge von Anfang an beibehalten. Ich denke, PowerShell verfügt bereits über drei Arten von Zeichenfolgentoken, aber die Hauptanforderung in dieser Diskussion scheint darin zu bestehen, den Dingen nicht entkommen zu wollen. Jeder String muss in der Lage sein, seinem Terminator zu entkommen, aber die dort diskutierte Lösung scheint newline als Terminator zu verwenden und nicht in der Lage zu sein, newlines zu maskieren (es gibt immer Semikolons).

Ich bin nicht vollständig mit einem Nur-Zeilen-Terminator verbunden, aber es scheint die einzige Lösung zu sein, um nichts entkommen zu wollen. Grundsätzlich würde dies dazu führen, dass ein "nativer Anrufoperator" ähnlich wie heute ein Zeilenkommentar tokenisiert - vom Operator bis zum Zeilenende wird die Zeichenfolge an die "native Shell" übergeben.

Damit:

--% ps -o pid,args -C bash | awk '/running_script/ { print $1 }'

wird tokenisieren als:

--% ps -o pid,args -C bash | awk '/running_script/ { print $1 }'
|-||-----------------------------------------------------------|
 |                                  \
Native call operator        Invocation to be passed to native shell

(Beachten Sie, dass wir sogar das Leerzeichen direkt nach --% an die native Shell senden, da die Zeichenfolge unmittelbar nach --% beginnt.)

Dann wird dieses Token in einen sehr einfachen AST eingewickelt, wie:

// We need to carefully work out what AST this inherits from
// This syntax has almost no interoperability with PowerShell by design,
// so can't implement fields like redirections or backgrounding faithfully.
// But in order to participate in pipelines and similar, would need to extend a more concrete class
public class NativeCallInvocationAst : StatementAst
{
    public string NativeInvocation { get; }
}

Nachdem dies kompiliert und an die Pipe gesendet wurde, wird ein neuer nativer Befehlsprozessor verwendet, um die Zeichenfolge direkt als Argument an die native Shell zu senden.

Während das alles umsetzbar zu sein scheint, sind einige Fragen in meinem Kopf:

  • Ist die native Shell konfigurierbar? Wenn nicht, wie rechtfertigen wir das? Wenn ja, sollte es als Teil der Syntax explizit sein (was die Syntax kompliziert) oder als Präferenzvariable implementiert werden (was das Erkennen und Verwalten erschwert)?
  • Wird eine solche Implementierung auffindbar sein und die Menschen nicht verwirren? Werden Benutzer verstehen, wie wenig PowerShell hier tut oder was passiert, wenn sie | oder & oder sogar Zeichenfolgen verwenden?
  • Wie viele Szenarien werden hier von einer einzeiligen wörtlichen Zeichenfolge bedient? Werden wir dies implementieren, um herauszufinden, dass viele Leute mehrzeilige Aufrufe wünschen? Ist die Notwendigkeit, das Entkommen eines einzelnen Anführungszeichens zu vermeiden, groß genug, um diese Einschränkungen hinzuzufügen?
  • Dies scheint ein sehr spezifisches Szenario für eine völlig neue Sprachsyntax zu sein. Wenn wir eine neue wörtliche Zeichenfolgensyntax erstellen, warum koppeln wir sie dann direkt mit dem Konzept des nativen Aufrufs? Wenn wir einen neuen Aufrufmechanismus erstellen, warum koppeln wir ihn dann direkt mit dem Konzept anderer Shells? Gute Syntax und gute Sprachkonstrukte setzen sich sowohl syntaktisch als auch funktional zusammen und versuchen, nicht zu viel in die Sprache selbst zu backen (stattdessen eine Plattform mit Elementen bereitzustellen, die der Benutzer als Programm zusammenstellen kann) - entspricht unsere vorgeschlagene native Aufrufsyntax der Messlatte? damit eine neue elementare Syntax zu Programmen zusammengesetzt wird? Gibt es einen besseren alternativen Ansatz, der dieses Problem auf einfache Weise löst und es uns dennoch ermöglicht, die Bausteine ​​entkoppelt zu lassen, damit sie zur Lösung anderer Probleme zusammengesetzt werden können?
  • Würden andere Shells oder andere Sprachen dies implementieren oder haben sie es bereits? Auf Syntaxebene oder auf einer anderen Ebene? Wenn das so ist, wie?

Ich denke hier, dass du das auch tust

@rjmholt CommandAst hat die Extent-Eigenschaft mit Eingabezeichenfolge. Das erneute Parsen ist so einfach wie das Teilen durch das erste Leerzeichen unter Windows oder das Teilen durch Leerzeichen unter Unix. Ich gehe davon aus, dass wir nichts in der Saite verloren haben und eine gute Leistung erzielen.

Wie auch immer dies implementiert wird, ich denke, wir brauchen eine Reihe von Anwendungsfällen, gegen die wir testen können. Lassen Sie mich Folgendes "vorschlagen":

  • Anwendungsfall 1: Ich möchte, dass ein einzelner Exe-Literal-Aufruf (keine Interpolation) funktioniert, ohne über das Zitieren / Escape-Regeln Bescheid zu wissen, z. B.: g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 . Die aktuelle Problemumgehung besteht darin, herauszufinden, was zu zitieren ist, z. B. g++ timer.cpp '@conanbuildinfo.args' -o timer --std=c++11 Ein komplizierteres Beispiel könnte Befehlszeilen umfassen, die sich über mehrere Zeilen erstrecken, z.
tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found 

Aktuelle Problemumgehung (Backtick gegen \ tauschen) zB:

tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz `
    --exclude=/proc `
    --exclude=/lost+found
  • Anwendungsfall 1a: Ich möchte, dass ein einzelner Exe-Aufruf mit PS-Interpolation funktioniert, z. B.: g++ $CppFile @conanbuildinfo.args -o timer --std=c++11 . Die aktuelle Problemumgehung besteht darin, herauszufinden, was zu zitieren ist (einfach oder doppelt) und was zu umgehen ist, z. B. g++ $CppFile '@conanbuildinfo.args' -o timer --std=c++11 Ein komplizierteres Beispiel könnte Befehlszeilen umfassen, die sich über mehrere Zeilen erstrecken, z.
tar -cvpzf "/share/Recovery/Snapshots/${ComputerName}_`$(date +%Y%m%d).tar.gz" \
    --exclude=/proc \
    --exclude=/lost+found 

Aktuelle Problemumgehung, erstes Argument in doppelten Anführungszeichen, Backtick gegen \ tauschen und zu Beginn von `$ (Datum + ...) den $ entkommen.

  • Anwendungsfall 2: Ich möchte einen Literal- Pipeline-Befehl in einer anderen Shell ausführen, z. B.: ps -o pid,args -C bash | awk '/running_script/ { print $1 }' und wsl ls $foo && echo $PWD . _Ich bin mir nicht sicher, ob dies ein separater Anwendungsfall vom vorherigen sein muss._ Die aktuelle Problemumgehung besteht darin, den Befehl bash zu zitieren, z. B.: bash -c 'ps -o pid,args -C bash | awk ''/-bash/ { print $1 }''' . Dieser Anwendungsfall kann auch mehrzeilige Zeichenfolgen enthalten, z.
ps -o pid,args -C bash | \
awk '/running_script/ { print $1 }'
  • Anwendungsfall 2a: Ich möchte einen Pipeline-Befehl in einer anderen Shell mit einer PS-Interpolation ausführen. Ist dies ein Anwendungsfall?

  • Anwendungsfall 3: Ich benötige npm run $scriptName um weiterarbeiten zu können, dh Variablen interpolieren . (Dies kann implizit sein und muss nicht angegeben werden).

Es stellt sich die Frage, ob es Anwendungsfälle für interpolating Strings geben sollte, dh g++ $CppFile @conanbuildinfo.args -o timer --std=c++11 oder ob wir nur darauf eingehen und sagen, dass die aktuellen Fluchtregeln dies abdecken?

Dies sind wahrscheinlich nicht die richtigen (oder alle) Anwendungsfälle, aber ich denke, es wäre hilfreich, dokumentierte Anwendungsfälle zu haben und auf diese verweisen zu können, wenn diese Funktion diskutiert wird. Andernfalls kann man sich leicht darin verlieren, wie die Funktion tatsächlich adressiert (oder nicht) ) Kundenanwendungsfälle.

Auch der zweite Anwendungsfall scheint mir einen "Shell-Aufruf" zu erfordern. Ich bin mir nicht sicher, ob der erste das tut. Scheint, als könnte die Exe direkt aufgerufen werden.

@rkeithhill Können Sie der Vollständigkeit Problemumgehungen hinzufügen?

@oising , ich schätze auch Ihre nachdenkliche Antwort.

Was gibt ins ... , was & cmd --% ... nicht kann?

--% in seiner aktuellen _parameter_ (Symbol) -Form - deren Verhalten wir vermutlich beibehalten möchten - kann nicht für cmd /c --% , da _multi-command_ cmd nicht unterstützt wird ^ als Escape-Zeichen), vorausgesetzt, der erste nicht zitierte | wird als _PowerShell's_-Pipe-Operator interpretiert und & wird wörtlich übergeben.
Das Gleiche gilt für Unix, wo --% im Allgemeinen nahezu nutzlos ist und mit sh -c insgesamt fehlschlägt, da sh die Befehlszeile als _single_-Argument erwartet (z. B. sh -c --% echo hi ergibt eine leere Zeile, da echo allein als Befehl ausgeführt wird.

ins mit der Anforderung, die Pass-Through-Befehlszeile durch Übergabe als einzelne Zeichenfolge abzugrenzen, löst diese Probleme und ermöglicht, wie bereits erwähnt, die Verwendung der Zeichenfolgeninterpolation, um _PowerShell_-Variablen- und Ausdruckswerte einzubeziehen.

Und letzteres bringt uns zu dem Grund ,

  • Es gibt keinen Übergangspfad von der Ausführung einer bereits vorhandenen Befehlszeile genau so wie sie ist, bis zur Einbeziehung von PowerShell-Variablen- und Ausdruckswerten.

    • Die einzige - obskure und umständliche - Möglichkeit, dies zu erreichen, besteht darin, _ilitäre Umgebungsvariablen_ zu definieren, in denen die interessierenden PowerShell-Werte gespeichert sind und auf die Sie dann verweisen können (Shell-entsprechend mit %...% oder $... ) die native Befehlszeile.
  • Es ist Ihnen unangenehm, eine --% -Anweisung nicht in eine größere PowerShell-Pipeline integrieren zu können.

    • Beachten Sie, dass es legitime Gründe für das Aufrufen der nativen Shell gibt, bei denen Sie diese Integration erwarten können. Insbesondere für das Weiterleiten von _raw-Byte-Daten_ durch eine (Zwischen-) Pipeline und das Senden von _strings ohne nachfolgende Zeilenumbruch erfordert die Verwendung der nativen Pipe - ein Beispiel aus der Praxis finden Sie in dieser SO-Antwort .
  • Es gibt auch die Unbeholfenheit, dass der --% _Parameter_ unter Unix praktisch nutzlos ist, was eine merkwürdige Asymmetrie einführt: Wenn die --% _Statement_ auch unter Unix funktioniert, warum nicht der --% _Parameter_ ? (z.B,
    /bin/echo --% 'hi "noon"' ergibt wörtlich 'hi noon' (anstelle der erwarteten hi "noon" ), da echo zwei Argumente erhält, wörtlich 'hi und noon' , wobei einfache Anführungszeichen als Daten beibehalten und doppelte Anführungszeichen entfernt werden).


ins , als vorgeschlagene Alternative zur --% -Anweisung, wäre idealerweise nur ein _convenience-Wrapper_ um cmd /c und sh -c , aber es ist tatsächlich _erforderlich_:

  • Zumindest vorübergehend unter Unix, solange # 1995 nicht behoben ist, da das direkte Aufrufen von sh -c '...' derzeit Argumente mit eingebetteten " ordnungsgemäß weiterleitet.

  • _Unvariabel_ unter Windows, da Sie die Befehlszeile _as-is_ an cmd über den Parameter lpCommandLine von CreateProcess (was der Parameter --% nicht kann aufgrund der Beschränkung auf einen Befehl); Alternativ funktioniert der Versuch, die Befehlszeile als eine einzelne Zeichenfolge zu übergeben, die in "..." insgesamt eingeschlossen ist, aufgrund von # 1995 nicht robust.


Die Probleme mit der Übergabe von Argumenten könnten vermieden werden, indem die Befehlszeile zum Ausführen von _via stdin_ bereitgestellt wird (die ins auch unterstützen könnte und sollte), dh um den Befehlszeilen- / Skripttext an die ausführbare Shell-Datei zu leiten:

  • Unter Unix funktioniert dies einwandfrei, da das Übergeben von Code über _stdin_ an sh im Wesentlichen dem Übergeben einer _script-Datei_ zur Ausführung entspricht (genau wie sh -c '<code>' is ):
PSonUnix> @'
echo '{ "foo": "bar" }' | cat -n
'@ | sh

     1  { "foo": "bar" }
  • Leider verarbeitet cmd Windows solche Eingaben nicht gut: Es druckt sein Logo und gibt jeden Befehl vor der Ausführung zusammen mit der Eingabeaufforderungszeichenfolge wieder:
PSonWin> 'date /t & ver' | cmd

Microsoft Windows [Version 10.0.18363.836]
(c) 2019 Microsoft Corporation. All rights reserved.

C:\Users\jdoe>date /t & ver
Sun 07/26/2020

Microsoft Windows [Version 10.0.18363.836]

C:\Users\jdoe>

Leider zeigt PowerShell selbst ein ähnlich wenig hilfreiches Verhalten: siehe # 3223


Genau wie sh und alle wichtigen POSIX-ähnlichen Shells ( dash , bash , ksh , zsh ) funktionieren die meisten Skriptsprachen ordnungsgemäß Unterstützung von stdin-as-script-Eingaben wie python , node , ruby und perl .

PS> 'print("hi")' | python

hi

Zum Einbeziehen von _PowerShell_-Variablen- und Ausdruckswerten haben Sie erneut die Möglichkeit, die Interpolation / Verkettung von Zeichenfolgen zu verwenden:

PS> $foo = 'bar'; "print(`"$foo`")" | python

bar

oder Verwenden der Argumentübergabefunktion des Interpreters:

PS> @'
import sys
print(sys.argv[1])
'@ | python - bar

bar

Ich hoffe, dass das oben Gesagte deutlich macht, dass

Was die Konfigurierbarkeit der zu verwendenden nativen Shell betrifft: Aus Gründen der Vorhersagbarkeit denke ich nicht, dass wir eine anbieten sollten; Benutzer, die auf eine andere Shell abzielen möchten, können die Befehlszeile einfach an die spezifische ausführbare Datei dieser Shell weiterleiten oder, sobald # 1995 behoben ist, als Argument übergeben.

  • Eine umstrittene Variante besteht darin, auf /bin/bash (mit einem (unwahrscheinlichen) Fallback auf /bin/sh ) abzuzielen - während man hoffen würde, dass veröffentlichte Befehlszeilen nur auf POSIX-mandatierten Funktionen beruhen, Befehlszeilen mit Bash-Ismen (Nicht-POSIX-Funktionen, die voraussichtlich nicht von allen Implementierungen von /bin/sh unterstützt werden) sind angesichts der Verbreitung von bash wahrscheinlich

@joeyaiello

Ich bin der Ansicht, dass die Vorteile dieser besonderen Funktion viel bedeutender sind als die Auswirkungen # 1995.

Ich finde es umwerfend, dass wir uns genauso viel Mühe geben, um diese Funktion zum Ausschneiden und Einfügen - hauptsächlich aus Gründen der interaktiven Bequemlichkeit - zu einem (umständlichen, funktionsbeschränkten) erstklassigen Bürger zu machen, während PowerShell grundsätzlich nicht in der Lage ist, leer zu bleiben Argumente und Argumente mit eingebetteten " Zeichen.

aber ich sehe keine Leute, die # 1995 in freier Wildbahn getroffen haben

Ich tue es und aus den Gründen, die ich zuvor besprochen habe, wirst du es immer mehr sehen.

Auch hier würde ich argumentieren, dass eine Shell, die wie PowerShell Folgendes ausführt, ein ernstes Problem hat :

# On Unix
PS> /bin/echo '{ "foo": "bar" }'

{ foo: bar }

@rjmholt CommandAst hat die Extent-Eigenschaft mit Eingabezeichenfolge. Das erneute Parsen ist so einfach wie das Teilen durch das erste Leerzeichen unter Windows oder das Teilen durch Leerzeichen unter Unix. Ich gehe davon aus, dass wir nichts in der Saite verloren haben und eine gute Leistung erzielen.

Ja, ich verstehe die Auslosung dort und vielleicht sind die Zuweisungen des AST in diesem Szenario nicht schrecklich (obwohl ich argumentieren würde, dass die Sprache keine unnötigen Zuweisungen für einfache Syntax generieren sollte). Sie müssen sich jedoch nur eine Eingabe vorstellen, die bash / cmd als Zeichenfolge betrachtet, und PowerShell kann keine Probleme verursachen:

Parst nicht mit der aktuellen PowerShell:

--% my_command "\" "

Analysiert, aber das Teilen durch Leerzeichen ergibt das falsche Ergebnis:

--% my_command "\"    "\"

(Wir müssen 4 Leerzeichen in der Zeichenfolge senden, aber PowerShell sieht zwei Zeichenfolgen in einem Array getrennt.)

@rjmholt , alle obigen Kommentars , die mir erneut nahe legen, dass die richtige Antwort lautet: Keine spezielle Syntax erforderlich.

Ich habe die Frage beantwortet, welche native Shell-Programmdatei in meinem vorherigen Kommentar verwendet werden soll , aber wie folgt:

Würden andere Shells oder andere Sprachen dies implementieren oder haben sie es bereits? Auf Syntaxebene oder auf einer anderen Ebene? Wenn das so ist, wie?

Mir ist keine andere Sprache bekannt, die dies auf Syntaxebene getan hat, ohne explizite Trennzeichen zu verwenden und Interpolation zuzulassen.
Zum Beispiel hat perl `...` zum Ausführen von Shell-Befehlen, sodass Sie den folgenden Befehl beispielsweise in einem Skript unter Unix ausführen können:
my $foo='/'; print `ls $foo | cat -n`

Die wichtigsten Aspekte sind die Verwendung expliziter Trennzeichen und die Möglichkeit, Werte des Aufrufers einzubeziehen. Beide fehlen im aktuellen Vorschlag für die Anweisung --% .

Wie bereits erwähnt, könnten wir diesen Ansatz für einen neuen Operator (oder eine Art Trennzeichen-basierte, optional interpolierende Syntax) verwenden, aber ich denke nicht, dass sich das lohnt
ins "ls $foo | cat -n" (oder mit einer Here-String-Variante) reicht aus, ohne die Sprache mit zusätzlicher Komplexität zu belasten.

perl bietet eine (richtige) Lösung auf Syntaxebene, da es sich nicht um eine Shell handelt, sondern Shell-Aufrufe so bequem wie möglich gestalten möchte.

Im Gegensatz dazu sind wir (auch) eine Shell und bieten Aufrufe im Shell-Stil direkt ohne zusätzliche Syntax an (wenn auch derzeit fehlerhaft - siehe # 1995).
Es erscheint unnötig, eine spezielle Syntax bereitzustellen, um die Aufrufe einer anderen Shell so einfach wie möglich zu gestalten.

@iSazonov

_Wenn wir (und PowerShell) wissen, dass wir eine native ausführbare Datei ausführen, warum brauchen wir dann überhaupt "nativen Operator aufrufen"? _

Wenn PowerShell eine Eingabezeichenfolge analysiert und CommandAst abruft, ermittelt PowerShell den Befehl.
Wenn der Befehl ein nativer Befehl ist, konvertiert PowerShell den Ast in NativeCommandProcessor.
Wir können einen neuen (experimentellen NativeCommandProcessor) für den Fall implementieren, der die Ast-Ausdehnung (dh die Eingabezeichenfolge) neu analysiert, um den Namen / Pfad der nativen ausführbaren Datei und den Rest der Zeichenfolge als Argument oder Argumente nach Betriebssystemregeln zu erhalten.

Wenn ein Benutzer das Kopieren und Einfügen aus der Shell möchte, sollte er "cmd" oder "bash" eingeben - dies funktioniert ohne Bearbeitung.
Wenn ein Benutzer kopieren und einfügen möchte, funktioniert eine native Befehlszeile ebenfalls wie g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Aber warten Sie, könnten Sie niemals eine PowerShell-Syntax mit nativen Befehlen verwenden, wenn wir das alles tun würden? Also würde sc.exe query $serviceName $serviceName als wörtliche Zeichenfolge übergeben?

Aber warten Sie, könnten Sie niemals eine PowerShell-Syntax mit nativen Befehlen verwenden, wenn wir das alles tun würden? Wie würde sc.exe query $serviceName $serviceName als wörtliche Zeichenfolge übergeben?

Die Option --% soll diesen Bedarf nicht decken.

sc.exe query $serviceName wird von &"<cmd>"

Die Option --% soll diesen Bedarf nicht decken.

sc.exe query $serviceName wird von &"<cmd>"

Ja sicher, ich glaube nicht, dass @iSazonov sich darauf bezieht. Ich lese das, als ob sie eine allgemeine Änderung der Funktionsweise der Parameterbindung für native Befehle vorschlagen.

Ja sicher, ich glaube nicht, dass @iSazonov sich darauf bezieht. Ich lese das, als ob sie eine allgemeine Änderung der Funktionsweise der Parameterbindung für native Befehle vorschlagen.

Herr, ich hoffe nicht.

Herr, ich hoffe nicht.

Eh manchmal, wenn Sie die Zusammenfassung Brainstorming, ist es leicht, das Offensichtliche zu vergessen. Ich meine, um fair zu sein, wir alle haben es gelesen und sofort angefangen, über die Umsetzung nachzudenken, ohne zu wissen, wie viel es brechen würde.

Oder ich vermisse etwas Offensichtliches und das wird überhaupt nicht vorgeschlagen 😁

@essentialexch , um klar zu sein, re:

sc.exe query $serviceName wird von &"<cmd>"

& "<cmd>" funktioniert nur, wenn <cmd> zu einem bloßen Befehl _name oder path_ ausgewertet wird, _ ohne Argumente_ - Argumente müssen separat und einzeln übergeben werden - und sie benötigen per se nicht "...." Anführungszeichen für zu erweiternde Variablenreferenzen (z.
$exe = 'findstr'; & "where.exe $exe" schlägt & "where.exe" $exe (doppelte Anführungszeichen hier optional; wenn weggelassen, ist das & optional))

Ja sicher, ich glaube nicht, dass @iSazonov sich darauf bezieht. Ich lese das, als ob sie eine allgemeine Änderung der Funktionsweise der Parameterbindung für native Befehle vorschlagen.

Mein Vorschlag ist, # 1995 (mit bahnbrechendem Wechselgeld) so einfach wie möglich zu reparieren. Natürlich funktioniert die Interpolation nicht. Natürlich ist es kein endgültiger Vorschlag - es ist eine Demo, wie wir einen Fix in Minuten implementieren können. Und es zeigt hauptsächlich, dass wir überhaupt keinen nativen nativen Operator benötigen, wie auch @ mklement0 gezeigt hat .

Analysiert, aber das Teilen durch Leerzeichen ergibt das falsche Ergebnis:

@rjmholt Ja, aber ich denke du
Das erste untergeordnete Element von CommandAst ist StringConstantExpressionAst mit dem nativen Befehl BareWord. Wir können das bloße Wort aus CommandAst Extent herausschneiden und eine Parameterliste abrufen, ohne dass sich etwas ändert. Wir könnten aber auch neue Ast hinzufügen, um die Parameterliste beizubehalten.
Ich verbringe eine Zeit mit der Erklärung, da ich im Folgenden die Diskussion zusammenfassen möchte und ich denke, dass wir einige Verbesserungen implementieren müssen, die komplex aussehen, aber ich denke, dass sie einfach und __ mit einfachem Opt-out für Abwärtskompatibilität__ implementiert werden könnten.


Ich glaube, die Absicht von @ SteveL-MSFT mit dem ursprünglichen Vorschlag war es, eine bahnbrechende Änderung zu vermeiden.
Ich stimme @TSlivede und @ mklement0 zu, dass diese Diskussion gezeigt hat, dass die neue Syntax nicht benötigt wird, da sie nicht alle fundamentalen Probleme wie # 1995 und # 12975 löst. Auf der anderen Seite erhöht es die Komplexität der Sprache, während wir im Gegenteil die Arbeit mit nativen Anwendungen vereinfachen möchten.

__Die einzige Möglichkeit, vorwärts zu kommen, besteht darin, eine wichtige Änderung oder mehr vorzunehmen .__ (Danach kann in einigen Szenarien auch ein neuer Operator oder ein neues Cmdlet hilfreich sein.)


Wir sollten mit einer Zusammenfassung der Anwendungsfälle und Benutzererwartungen beginnen.
Siehe @rkeithhills großartigen Beitrag oben

  1. Benutzer möchten die Interpolation aktivieren / deaktivieren.
    PowerShell verfügt bereits über Strings / Here-Strings mit einfachen und doppelten Anführungszeichen.
    Unser Ziel ist es zu verstehen, wie man es anwendet / verfeinert / verbessert.
  2. Benutzer möchten eine ausführbare Datei oder eine Shell ausführen

    • Jeder ausführbare Befehl beginnt mit dem Namen der App und folgt den Argumenten

      • mit / ohne Interpolation

      • in einer Zeile / in mehreren Zeilen

    • Shell ist wahrscheinlich ein Sonderfall, da Argument (e) ein _script_ ist

      • Im Copy-Paste-Szenario möchten Benutzer keine Interpolation und möchten ein- und mehrzeilig

      • Im Skriptszenario möchten Benutzer die Interpolation aktivieren / deaktivieren und möchten ein- und mehrzeilig sein

    Gedanken:
    Dies lässt uns denken, dass das Verhalten des Copy-Paste-Szenarios die Standardeinstellung sein sollte.
    Zeilenabschlusszeichen hängen von der ausführbaren Datei ab (in Bash, in PowerShell). Dies lässt uns denken, dass:
    (1) Eine Shell sollte ausdrücklich als ausführbare Datei angegeben werden.
    (2) Vielleicht sollte jede ausführbare Datei mit mehreren Zeilen mithilfe einer Shell aufgerufen werden, oder wir müssen eine spezielle Analyse durchführen (um den Terminator zu entfernen und eine Argumentliste zu erstellen, die auf das Problem mit dem Escape zurückgreift).
    (3) Wir könnten das Copy-Paste-Szenario in PSReadline ansprechen. Es könnte einen Puffer in den richtigen PowerShell-Befehl konvertieren.

  3. Benutzer möchten einzeilig und mehrzeilig.

    • Vielleicht müssen wir hier die Zeichenfolgen für eine einzelne Zeile verbessern, um einige Szenarien zu behandeln, wie zuvor erläutert.
    • siehe 2 - (2) - Verwenden Sie immer eine Shell für mehrzeilige oder implementieren Sie eine spezielle Analyse

Ich möchte nicht mehr schreiben, da @TSlivede und @ mklement0 ihre Gedanken und Schlussfolgerungen von früher aufzulisten (beginnend mit @ Problem zu beheben.

Ich möchte nur hinzufügen, dass wir neue Cmdlets in Betracht ziehen könnten, um Benutzerübernahmen wie Invoke-Shell (Invoke-NativeShell), Testargumente (wie echoargs.exe, um zu zeigen, dass die native App Hilfe für Benutzer erhält und anzeigt), Convert-Arguments ( Benutzereingaben in rechts maskierte Argumente konvertieren).

PS: Wie ich oben gezeigt habe, kann das Verhalten einzelner Zeilen zur Abwärtskompatibilität zur Laufzeit leicht deaktiviert werden.

Zeilenabschlusszeichen hängen von der ausführbaren Datei ab (in Bash, in PowerShell).

\ ist kein Zeilenabschluss in bash .

Natürlich funktioniert die Interpolation nicht. Natürlich ist es kein endgültiger Vorschlag - es ist eine Demo, wie wir einen Fix in Minuten implementieren können. Und es zeigt hauptsächlich, dass wir überhaupt keinen nativen nativen Operator benötigen, wie auch @ mklement0 gezeigt hat .

Genauso wie wir alle Fehler in wenigen Minuten beheben könnten, wenn wir nur das Repo löschen würden. Das ist eine ziemlich enorme Pause, die Sie vorschlagen. Auch wenn es nur eine Hypothese ist, sehe ich nicht, wie hilfreich es ist.

Auch wenn es nur eine Hypothese ist, sehe ich nicht, wie hilfreich es ist.

@SeeminglyScience Es scheint, dass wir uns in verschiedenen Kontexten befinden. Ich sage, dass wir einen Fix für einzeiligen nativen Aufruf als brechende Änderung implementieren können, ohne Parser zu brechen. Mit anderen Worten, wir können das Verhalten sogar im laufenden Betrieb ändern, unabhängig davon, welches neue Verhalten wir implementieren würden.

Mit anderen Worten, wir können das Verhalten sogar im laufenden Betrieb ändern, unabhängig davon, welches neue Verhalten wir implementieren würden.

Ich denke, das, was mir fehlt, ist, wie Sie dieses Verhalten im laufenden Betrieb ändern können, ohne explizit danach zu fragen (wie bei einem nativen Anrufbetreiber).

@iSazonov

_Wenn wir (und PowerShell) wissen, dass wir eine native ausführbare Datei ausführen, warum brauchen wir dann überhaupt "nativen Operator aufrufen"? _

Wenn PowerShell eine Eingabezeichenfolge analysiert und CommandAst abruft, ermittelt PowerShell den Befehl.
Wenn der Befehl ein nativer Befehl ist, konvertiert PowerShell den Ast in NativeCommandProcessor.
Wir können einen neuen (experimentellen NativeCommandProcessor) für den Fall implementieren, der die Ast-Ausdehnung (dh die Eingabezeichenfolge) neu analysiert, um den Namen / Pfad der nativen ausführbaren Datei und den Rest der Zeichenfolge als Argument oder Argumente nach Betriebssystemregeln zu erhalten.

Wenn ein Benutzer das Kopieren und Einfügen aus der Shell möchte, sollte er "cmd" oder "bash" eingeben - dies funktioniert ohne Bearbeitung.
Wenn ein Benutzer kopieren und einfügen möchte, funktioniert eine native Befehlszeile ebenfalls wie g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Ich möchte bestätigen, was Sie damit meinen: Schlagen Sie vor, beim Aufrufen externer ausführbarer Dateien im Wesentlichen die gesamte Powershell-Syntax zu ignorieren?

Konkret: Schlagen Sie vor, dass zum Beispiel ausgeführt wird

/bin/echo (1..5)

in Powershell wird nicht mehr 1 2 3 4 5 ausgeben, sondern sollte stattdessen Literal (1..5) ausgeben?

Wenn Sie das tatsächlich vorschlagen, dann muss ich sagen: Ich denke, das ist eine schreckliche Idee.

Das vereinfacht die Dinge nicht (externe ausführbare Dateien verhalten sich jetzt syntaktisch anders als interne Cmdlets) und es muss auch nichts mit # 1995 IMHO zu tun haben ...

@rjmholt
Von oben :

  • Würden andere Shells oder andere Sprachen dies implementieren oder haben sie es bereits? Auf Syntaxebene oder auf einer anderen Ebene? Wenn das so ist, wie?

Das einzige, was mir in den Sinn kommt, wäre der Python-Operator ! :

Ausführen von !ps -'fax' || true in ipython-Ausgaben:

  261 pts/0    Sl     0:00          \_ /usr/bin/python /usr/bin/ipython
  311 pts/0    S      0:00              \_ /bin/bash -c ps -'fax' || true
  312 pts/0    R      0:00                  \_ ps -fax

( ps zeigt keine Anführungszeichen um Argumente an , aber ps -'fax' || true wird wirklich als ein einziges Argument für bash angegeben.)

Diese Funktion ermöglicht jedoch weiterhin die Interpolation von Python-Variablen in diesen Befehl ( {pyvar} ).

Und ähnlich wie @ mklement0 ‚s Beispiel , ipython nur implementiert diese

weil es selbst keine Muschel ist

Wenn ipython nur die Python-Syntax zulassen würde, wäre dies als Shell nicht sehr nützlich. Daher ermöglicht diese Syntax, Befehle an bash weiterzuleiten, damit sie als Shell verwendet werden können. Powershell hingegen behauptet, eine Shell zu sein (bis # 1995 gelöst ist, werde ich nicht sagen, dass es sich um eine Shell handelt), daher wäre es ziemlich seltsam, eine spezielle Syntax zum Aufrufen anderer Shells hinzuzufügen.

Wenn so etwas aus irgendeinem Grund tatsächlich implementiert wird (hoffentlich nicht), würde ich vorschlagen, ! anstelle von --% als "Call-Shell-Operator" zu verwenden.

! ist etwas, was die Leute vielleicht erraten (weil sie ! für diesen Zweck von ipython kennen (oder vom vim-Befehlsmodus oder von less oder von ftp ). )

--% ist dagegen schwieriger zu tippen, wahrscheinlich nicht zu erraten und vor allem: Die derzeitige Verwendung von --% hat meiner Meinung nach sehr wenig mit dem "Weitergeben von Sachen an eine andere Shell" zu tun. Verhalten. Das aktuelle --% emuliert genau ein Element der cmd-Syntax: die Variablensubstitution (und selbst das nicht vollständig). Es unterstützt das Escape-Zeichen ^ , erlaubt keine Umleitung zu Dateien und so weiter. Das einzige, was an den aktuellen --% etwas nützlich ist, ist die Möglichkeit, wörtliche Inhalte an lpCommandLine - aber das ist etwas, was der vorgeschlagene "Call-Shell-Operator" nicht kann.

Ein weiterer Unterschied (der bereits erwähnt wurde) zwischen dem vorgeschlagenen "Call Shell Operator" und dem aktuellen --% : Einer endet an Pipes, der andere nicht - sehr verwirrend für neue Benutzer. ! leitet andererseits | in allen Anwendungen, die ich bisher getestet habe (ipython, vim, less, ed usw.), an die Shell weiter.

HELP about_Logical_Operators -S

Ja, ! an sich ist problematisch, weil es ein logischer Negationsoperator in der Sprache ist.

Übrigens war einer der alternativen Vorschläge &! - eine Art "Call Native / Shell" -Operator.

Ich wünschte, @TSlivede hätte den Teil "hoffentlich nicht" mehr betont, als er sagte "Wenn so etwas aus irgendeinem Grund tatsächlich umgesetzt wird (hoffentlich nicht)".

  • Kein Operator / keine Anweisung mit einzelnen Argumenten kann das Problem der PowerShell-Werte in der nativen Befehlszeile sauber lösen, da $ als Variablensiegel in _both_ verwendet wird PowerShell- und POSIX-ähnliche Shells.

  • Kein Operator / keine Anweisung ohne explizite Trennzeichen in der gesamten nativen Befehlszeile kann das Problem lösen, bei dem die native Befehlszeile endet (das Sie möglicherweise interessiert oder nicht).

@ PowerShell / Powershell-Komitee hat dies heute diskutiert. Zu diesem Zeitpunkt scheint es keine Einigung über die anfängliche Problemstellung oder ein Design zur Lösung zu geben, sodass wir nicht glauben, dass wir dies in 7.1 mit einem stabilen Design umsetzen können.

@ PowerShell / Powershell-Komitee hat dies heute diskutiert. Zu diesem Zeitpunkt scheint es keine Einigung über die anfängliche Problemstellung oder ein Design zur Lösung zu geben, sodass wir nicht glauben, dass wir dies in 7.1 mit einem stabilen Design umsetzen können.

182 Kommentare!

Es gab einige großartige Diskussionen darüber! Ich werde weiterhin alle neuen Kommentare zu diesem Thema überwachen. Wir empfehlen der Community weiterhin, das in PSGallery veröffentlichte Cmdlet vom Typ Invoke-NativeCommand unabhängig von diesem Problem zu implementieren.

Ich finde es bedauerlich, wenn Sie unter https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -662732627 einen Kompromiss, aber eine sehr nützliche Lösung hatten, diesen fallen zu lassen. Ich weiß, dass Sie einen Konsens wollen, aber manchmal ist ein Kompromiss das Beste, was passieren kann. Nein, es ist nicht perfekt, aber eine einzeilige SO-Lösung wäre äußerst nützlich. Zumindest nach Meinung dieser einen Person.

@essentialexch Um klar zu sein, wir haben nicht einmal einen Konsens innerhalb des PowerShell-Teams

Da dies verschoben wird, was ist mit der Implementierung von Helfern

Wir könnten neue Cmdlets in Betracht ziehen, um Benutzeradoptionen wie Invoke-Shell (Invoke-NativeShell), Testargumente (wie echoargs.exe, um zu zeigen, dass native Apps Hilfe für Benutzer erhalten und anzeigen), Convert-Arguments (um Benutzereingaben in zu konvertieren) zu vereinfachen rechts entkommene Argumente).

Ich denke, Test-Argumente könnten sehr nützlich sein.

Ich habe gerade ein Modul veröffentlicht, Native ( Install-Module Native -Scope CurrentUser ), das ich Sie alle zum Ausprobieren einlade und dessen Befehle ich unten verwenden werde . die numerischen Anwendungsfälle verwiesen wird, sind von @rkeithhill ‚s Kommentar oben .

Wenn wir jemals wollen, dass sich der konzeptionelle Nebel auflöst, müssen wir die Anwendungsfälle meiner Meinung nach anders gestalten.
Keiner von ihnen erfordert Änderungen beim Parsen.

Anwendungsfall [Native-Shell-Aufruf]:

Sie möchten einen einzelnen Befehl (Anwendungsfall 1) oder eine gesamte Befehlszeile mit mehreren Befehlen (Anwendungsfall 2) ausführen, die für die plattformeigene Shell geschrieben wurde (dieses Problem):

  • Da Sie die Syntax einer anderen Shell verwenden, übergeben Sie den Befehl (Zeile) als einzelne Zeichenfolge an die Ziel-Shell, damit _it_ die Analyse durchführen kann. Die String-Interpolation von PowerShell bietet die Möglichkeit, PowerShell-Werte in den übergebenen String einzubetten (Anwendungsfall 2a).
  • ins ( Invoke-NativeShell ) deckt diese Anwendungsfälle ab.
# Use case 1: Single executable call with line continuation, on Unix.
@'
tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found 
'@ | ins

# Use case 2: Entire Bash command line (also with line continuation)
@'
ps -o pid,args | awk \
  '/pwsh/ { print $1 }'
'@ | ins

# Use case 2a: Entire Bash command line (also with line continuation) with string interpolation.
# Note the double-quoted here-string and the need to escape the $ that is for Bash as `$
$fields = 'pid,args'
@"
ps -o $fields | awk \
  '/pwsh/ { print `$1 }'
"@ | ins

# Alternative to use case 2a: pass the PowerShell value *as a pass-through argument*,
# which allows passing the script verbatim.
# Bash sees the pass-through arguments as $1, ... (note that the `awk` $1 is unrelated).
@'
ps -o $1 | awk \
  '/pwsh/ { print $1 }'
'@ | ins - 'pid,args'

Die Here-String-Syntax ist nicht die bequemste (daher der Vorschlag, eine Inline-Variante zu implementieren - siehe # 13204), aber Sie müssen sie nicht verwenden. Für wörtliche Befehle können Sie '...' , für die nur _doubling_ embedded ' erforderlich ist, falls vorhanden.

Hier ist auch ein vernünftiger Ersatz für den "StackOverflow-Operator" :

Wenn Sie den folgenden PSReadLine Key-Handler in Ihre $PROFILE -Datei einfügen, können Sie Alt-v verwenden, um einen Aufruf von ins mit einer wörtlichen Hier-Zeichenfolge zu erstellen in die der aktuelle Text der Zwischenablage eingefügt wird.Enter sendet den Anruf.

# Scaffolds an ins (Invoke-NativeShell) call with a verbatim here-string
# and pastes the text on the clipboard into the here-string.
Set-PSReadLineKeyHandler 'alt+v' -ScriptBlock {
  [Microsoft.PowerShell.PSConsoleReadLine]::Insert("@'`n`n'@ | ins ")
  foreach ($i in 1..10) { [Microsoft.PowerShell.PSConsoleReadLine]::BackwardChar() }
  # Comment the following statement out if you don't want to paste from the clipboard.
  [Microsoft.PowerShell.PSConsoleReadLine]::Insert((Get-Clipboard))
}

Anwendungsfall [direkter ausführbarer Aufruf]:

Sie möchten eine einzelne externe ausführbare Datei mit der Syntax von PowerShell mit einzelnen Argumenten aufrufen (Anwendungsfälle 1a und 3).

  • Dies ist ein Kernmandat jeder Shell, und es ist von entscheidender Bedeutung, dass sie robust funktioniert.

    • Sie müssen sich darauf verlassen können, dass PowerShell alle Argumente, die sich aus der Analyse von _as-is_ ergeben, an die ausführbare Zieldatei weiterleiten kann. Dies ist derzeit nicht der Fall - siehe # 1995 .
  • Sie müssen die Syntax von PowerShell und die Metazeichen im Argumentmodus verstehen.

    • Sie müssen sich bewusst sein, dass ` als Escape-Zeichen und für die Fortsetzung der Zeile verwendet wird.
    • 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 ).

Während wir darauf warten, dass # 1995 behoben wird, füllt die Funktion ie (für i nvoke (external) e xecutable) die Lücke, indem einfach ein direkter Anruf vorangestellt wird. zB anstelle des folgenden Aufrufs:

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

Das Modul Native etwas, das echoArgs.exe ähnelt, den Befehl dbea ( Debug-ExecutableArguments ). Beispielausgabe unter Windows:

# Note the missing first argument, the missing " chars., and the erroneous argument boundaries.
PS> dbea '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'
7 argument(s) received (enclosed in <...> for delineation):

  <a&b>
  <3 of snow Nat>
  <King>
  <Cole c:\temp>
  <1\ a>
  <">
  <b>

Command line (helper executable omitted):

  a&b 3" of snow "Nat "King" Cole" "c:\temp 1\\" "a \" b"

Mit dem Schalter -UseIe ( -ie ) können Sie dbea anweisen, die Argumente über ie . Dies zeigt, dass die folgenden Probleme behoben werden:

# OK, thanks to -UseIe
PS> dbea -UseIe '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'
6 argument(s) received (enclosed in <...> for delineation):

  <>
  <a&b>
  <3" of snow>
  <Nat "King" Cole>
  <c:\temp 1\>
  <a \" b>

Command line (helper executable omitted):

  "" a&b "3\" of snow" "Nat \"King\" Cole" "c:\temp 1\\" "a \\\" b"

Hinweis: Sobald # 1995 behoben ist, ist ie nicht mehr erforderlich. Im Interesse der Vorwärtskompatibilität wurde ie entwickelt, um einen Fix zu erkennen und automatisch zu verschieben. Das heißt, sobald das Update installiert ist, werden ie Problemumgehungen nicht mehr anwenden und sich effektiv wie ein direkter / & -Anruf verhalten.

Anwendungsfall [direkter, nicht ausführbarer Windows-Aufruf]:

Es gibt zwei Hauptprobleme, die nur bei Windows auftreten:

  • Sie rufen eine ausführbare "Rogue" -Datei auf, deren Angebotsanforderungen von der am häufigsten verwendeten Konvention abweichen . Zum Beispiel erfordern msiexec.exe und msdeploy.exe dass prop="<value with spaces>" genau so zitiert wird - das Zitat direkt um den _value_-Teil - obwohl "prop=<value with spaces>" - das gesamte zitiert Argument - sollte gleich sein (letzteres ist das, was PowerShell - zu Recht - hinter den Kulissen tut).

  • Sie rufen eine Batchdatei mit einem Argument auf, das keine Leerzeichen enthält, aber cmd.exe Metazeichen_ enthält, z. B. & , ^ oder | ; z.B:

    • .\someBatchFile.cmd 'http://example.org/a&b'
    • PowerShell übergibt - zu Recht - http://example.org/a&b _unquoted_ (da kein eingebettetes Leerzeichen vorhanden ist), dies unterbricht jedoch den Aufruf der Batchdatei, da cmd.exe - unangemessen - die an eine Batchdatei übergebenen Argumente dem unterwirft Dieselbe Parsing-Regelung, die innerhalb von cmd.exe gelten würde, anstatt sie als Literale zu akzeptieren.

    • Hinweis: Während die Verwendung von Batchdateien zur direkten Implementierung von Funktionen wahrscheinlich nachlässt, ist die Verwendung als _CLI-Einstiegspunkte_ immer noch weit verbreitet, z. B. die az CLI von Azure, die als Batchdatei az.cmd implementiert ist.

Hinweis: ie behandelt diese Szenarien automatisch für Batch-Dateien sowie für msiexec.exe und msdeploy.exe , sodass für _most_ Aufrufe kein zusätzlicher Aufwand erforderlich sein sollte . Es ist jedoch unmöglich, alle ausführbaren "Rogue" -Dateien zu antizipieren.

Es gibt zwei Möglichkeiten, dies zu beheben:

  • Angesichts der Tatsache, dass cmd.exe nach dem Anwenden einer eigenen Interpretation auf Argumente in einer Befehlszeile _does_ das angegebene Anführungszeichen beibehält, können Sie unter Windows an einen Aufruf von ins delegieren:

    • ins '.\someBatchFile.cmd "http://example.org/a&b"'
    • Wenn Sie eine _expandable_-Zeichenfolge verwenden, können Sie auf diese Weise erneut PowerShell-Werte in die Befehlszeichenfolge einbetten.

      • $url = 'http://example.org/a&b'; ins ".\someBatchFile.cmd `"$url`""

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

    • .\someBatchFile.cmd --% "http://example.org/a&b"'
    • Angesichts der Implementierung von --% besteht die einzige Möglichkeit, PowerShell-Werte einzubetten, darin, - umständlich - ein _aux zu definieren. Umgebungsvariable_ und referenzieren Sie sie mit der Syntax %...% :

      • $env:url = 'http://example.org/a&b'; .\someBatchFile.cmd --% "%url%"


Fehlerbehandlung

Die ausstehende https://github.com/PowerShell/PowerShell-RFC/pull/88 ermöglicht eine bessere Integration der Fehlerbehandlung in externe (native) ausführbare Dateien.

In der Zwischenzeit wird das Modul Native mit zwei Funktionen für die Ad-hoc-Anmeldung geliefert, mit der ein Exit-Code ungleich Null als Fehler beim Beenden eines Skripts behandelt wird :

  • ins unterstützt den Schalter -e / -ErrorOnFailure , der einen Fehler auslöst, wenn $LASTEXITCODE nach dem Aufruf der nativen Shell ungleich Null ist.

  • iee ist eine Wrapper-Funktion für ie , die analog einen Fehler auslöst, wenn $LASTEXITCODE nach dem Aufruf der externen ausführbaren Datei ungleich Null ist.

Die mit dem Modul gelieferten Befehle enthalten viele Feinheiten.
Die ziemlich umfangreiche kommentarbasierte Hilfe deckt sie hoffentlich ausreichend ab.

Wenn wir jemals wollen, dass sich der konzeptionelle Nebel auflöst, müssen wir die Anwendungsfälle meiner Meinung nach anders gestalten.

Großartig! Was ich zuvor gepostet habe, war nur, um die Leute dazu zu bringen, darüber nachzudenken, mehr und bessere Anwendungsfälle zu erstellen.

Ich sollte einige wichtige Aspekte von ins ( Invoke-NativeShell ) klarstellen, die sich von den zuvor vorgeschlagenen unterscheiden. Ziel war es, die Allgegenwart von bash zu berücksichtigen und eine konsistente CLI über alle Plattformen hinweg bereitzustellen.

  • Unter Unix habe ich mich entschieden, /bin/bash statt /bin/sh , da Bash allgegenwärtig ist, aber Sie können sich dafür entscheiden, /bin/sh mit -UseSh ( -sh ) ( /bin/sh dient auch als Fallback für den unwahrscheinlichen Fall, dass /bin/bash nicht vorhanden ist).

    • Im Interesse einer konsistenten Erfahrung über Aufrufformulare und Plattformen hinweg habe ich die Möglichkeit verborgen, $0 , den Aufrufnamen, festzulegen. Das heißt, alle Pass-Through-Argumente beginnen mit $1 . Dies stimmt damit überein, wie Argumente übergeben werden, wenn Sie das Skript aus der Pipeline leiten, und was Benutzer wahrscheinlich erwarten:
# Script as argument
PSonUnix> ins 'echo $# arguments; echo "[$1] [$2]"' one two
2 arguments
[one] [two]

# Script via pipeline; note the "-" to signal that the script is piped.
PSonUnix> 'echo $# arguments; echo "[$1] [$2]"' | ins - one two
2 arguments
[one] [two]

# As an aside: script as argument, with pipeline input *as data*:
PSonUnix> 'one', 'two' | ins 'cat -n'
     1  one
     2  two
  • Unter Windows musste ich aus technischen Gründen eine temporäre Batchdatei hinter den Kulissen verwenden, aber ich denke, dass die Verwendung der Batchdateisyntax letztendlich sowieso die bessere Wahl ist ( %%i statt %i in for Schleifenvariablen, Fähigkeit, % als %% zu entkommen).

    • Die Verwendung einer Batchdatei ermöglicht auch die Unterstützung von Pass-Through-Argumenten wie unter Unix:
# Script as argument
PSonWin> ins 'echo [%1] [%2]' one two
[one] [two]

# Script via pipeline; note the "-" to signal that the script is piped.
PSonWin> 'echo [%1] [%2]' | ins - one two
[one] [two]
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen