Powershell: Appeler un opérateur natif

Créé le 30 juin 2020  ·  189Commentaires  ·  Source: PowerShell/PowerShell

Énoncé du problème

Actuellement, il existe des cas où couper et coller une ligne de commande native ne s'exécute pas comme prévu dans PowerShell. Cela peut être dû à une analyse incorrecte des guillemets destinés à être transmis à la commande native ou à l'utilisation de la syntaxe PowerShell qui n'est pas censée être interprétée comme PowerShell. PowerShell a un argument spécial --% lorsqu'il est utilisé avec des commandes natives, traite le reste des arguments comme des littéraux passés à la commande native, mais présente plusieurs problèmes:

  1. Il n'est pas détectable, les utilisateurs doivent connaître ce paramètre spécial à l'avance
  2. | , && et || ont priorité, donc: wsl --% ls | less exécuterait wsl ls et dirigerait les résultats vers less s'exécutant dans PowerShell plutôt que less s'exécutant dans wsl
  3. Si vous coupez et collez une ligne de commande, vous devrez modifier la ligne pour insérer --% vers le début
  4. Sur les systèmes Unix, les arguments après -% sont passés textuellement sans globalisation là où les commandes natives sous Unix s'attendent à ce que le shell effectue une globalisation

Détails de mise en œuvre technique proposés

La proposition est d'introduire un nouvel opérateur --% (Call Native).

Tout contenu de texte après cet opérateur appellera dans le "shell par défaut" du système d'exploitation pour s'exécuter. Sous Windows, ce serait cmd.exe et sur les systèmes Unix, ce serait / bin / sh. Cela résout le problème de globbing sur les systèmes Unix, et permet également une expansion %variable% sous Windows. Contrairement au commutateur --% , cela signifie également que | , && et || sont traités comme faisant partie de la ligne de commande native.

Cela signifie que ces deux sont fonctionnellement identiques:

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

$foo et $PWD est évalué par le shell dans WSL. Notez que dans le premier exemple, vous devez savoir échapper à && pour l'exécuter dans WSL plutôt que dans PowerShell.

Pour rediriger la sortie d'une telle exécution dans PowerShell, l'utilisateur doit d'abord stocker les résultats dans une variable:

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

Notez que contrairement à l'opérateur d'appel actuel & , vous ne pouvez utiliser aucune syntaxe PowerShell, donc:

--% $commandline

ne résoudrait pas $commandline tant que variable d'abord par PowerShell, mais passerait à la place $commandline au shell par défaut pour traiter les non résolus.

Le problème du copier-coller est résolu en collant simplement après la saisie de --% .

L'exemple ci-dessus pour wsl ressemblerait à ceci:

--% wsl ls | less

où l'intention est d'exécuter toute la ligne dans l'instance WSL Linux.

Découvrabilité

Les utilisateurs déjà familiers avec --% tant que commutateur peuvent facilement passer à l'utilisation de ce nouvel opérateur là où cela a du sens. Pour les nouveaux utilisateurs, --% est unique afin que les moteurs de recherche le trouvent facilement lié à PowerShell.

Autres considérations

&! et &n ont été proposés comme sigil, mais il y a eu un retour parce que &! est un opérateur valide dans certaines langues, ce qui rend la recherche de documentation sur le Web plus difficile. Il y avait également un souci de savoir si un visuel similaire à l'opérateur d'appel & serait déroutant pour les utilisateurs.

Il est question de prendre en charge la continuation de ligne lors de l'utilisation de ce nouvel opérateur. Je dirais que nous ne l'appuyons pas au départ.

Une solution de cmdlet au lieu d'une solution d'opérateur a été proposée. Nous pensons que cela ne résout pas le problème du «copier-coller» car vous devez maintenant savoir comment mettre le pipeline entre guillemets simples et / ou échapper des caractères spéciaux. Nous pensons qu'une applet de commande comme dans Invoke-NativeCommand (nom à déterminer) serait utile comme option supplémentaire au lieu de remplacer le besoin d'un opérateur.

Problèmes liés

Cela devrait également résoudre ces problèmes:

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

Committee-Reviewed Issue-Discussion Issue-Enhancement

Commentaire le plus utile

Je viens de publier un module, Native , ( Install-Module Native -Scope CurrentUser ) que je vous invite tous à essayer et dont je vais utiliser les commandes ci-dessous ; les cas d'utilisation numériques référencés proviennent du commentaire de ci-dessus .

Si jamais nous voulons que le brouillard conceptuel se dissipe, je pense que nous devons encadrer les cas d'utilisation différemment.
Aucun d'entre eux ne nécessite de modifications dans l'analyse_.

Cas d'utilisation [appel shell natif]:

Vous voulez exécuter une _ commande individuelle_ (cas d'utilisation 1) ou une _ ligne de commande multi-commandes_ entière_ (cas d'utilisation 2) _ écrite pour le shell natif de la plateforme_ (ce problème):

  • Puisque vous utilisez la syntaxe d'un shell différent, vous passez la commande (ligne) _ comme une seule chaîne_ au shell cible, pour que _it_ fasse l'analyse; L'interpolation de chaîne de PowerShell offre la possibilité d'incorporer des valeurs PowerShell dans la chaîne transmise (cas d'utilisation 2a).
  • ins ( Invoke-NativeShell ) couvre ces cas d'utilisation.
# 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'

La syntaxe here-string n'est pas la plus pratique (d'où la suggestion d'implémenter une variante en ligne - voir # 13204), mais vous n'avez pas à l'utiliser; pour les commandes textuelles, vous pouvez utiliser '...' , qui ne nécessite que _doubling_ intégré ' , s'il est présent.

En outre, voici un substitut raisonnable à «l'opérateur StackOverflow» :

Si vous placez le gestionnaire de clés PSReadLine dans votre fichier $PROFILE , vous pourrez utiliser Alt-v pour échafauder un appel à ins avec une chaîne verbatim here-string dans lequel le texte actuel du presse-papiers est collé.Enter soumet l'appel.

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

Cas d'utilisation [appel exécutable direct]:

Vous voulez appeler un _exécutable externe unique_ en utilisant la syntaxe de _PowerShell_, avec des _ arguments individuels_ (cas d'utilisation 1a et 3).

  • Il s’agit d’un mandat fondamental de toute coquille et il est d’une importance vitale qu’elle fonctionne de manière robuste.

    • Vous devez pouvoir compter sur PowerShell pour pouvoir transmettre tous les arguments qui résultent de _its_ parsing _as-is_ à l'exécutable cible. Ce n'est actuellement pas le cas - voir # 1995 .
  • Cela nécessite que vous compreniez la syntaxe de _PowerShell_ et les métacaractères en mode argument de _its_.

    • Vous devez être conscient que ` est utilisé comme caractère d'échappement et pour la continuation de la ligne.
    • Vous devez être conscient que PowerShell a des métacaractères supplémentaires qui nécessitent des guillemets / échappements pour une utilisation verbatim; ce sont (notez que @ n'est problématique que comme premier caractère d'un argument.):

      • pour les shells de type POSIX (par exemple, Bash): @ { } ` (et $ , si vous voulez empêcher l'expansion initiale par PowerShell)
      • pour cmd.exe : ( ) @ { } # `
      • Individuellement ` échapper à ces caractères. est suffisant (par exemple, printf %s `@list.txt ).

Pendant que nous attendons que # 1995 soit corrigé, la fonction ie (for i nvoke (external) e xecutable) comble le vide, simplement en précédant un appel direct; par exemple, au lieu de l'appel suivant:

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

vous utiliseriez ce qui suit:

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

Le module Native est livré avec quelque chose qui ressemble à echoArgs.exe , la commande dbea ( Debug-ExecutableArguments ); exemple de sortie sous 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"

En utilisant le commutateur -UseIe ( -ie ), vous pouvez dire à dbea de passer les arguments via ie , ce qui démontre qu'il résout les problèmes:

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

Remarque: une fois que # 1995 est corrigé, ie n'est plus nécessaire; dans l'intérêt de la compatibilité ascendante, ie est conçu pour détecter et se reporter automatiquement à un correctif; c'est-à-dire qu'une fois le correctif en place, ie cessera d'appliquer ses solutions de contournement et agira effectivement comme un appel direct / & .

Cas d'utilisation [appel exécutable Windows non autorisé direct]:

Il existe deux problèmes principaux, qui se posent sous _Windows uniquement_:

  • Vous invoquez un exécutable «non autorisé» dont les exigences de cotation diffèrent de la convention la plus utilisée ; par exemple, msiexec.exe et msdeploy.exe nécessitent que prop="<value with spaces>" soient cités précisément de cette façon - la citation juste autour de la partie _value_ - même si "prop=<value with spaces>" - citation de l'ensemble argument - _devrait_ être équivalent (ce dernier est ce que PowerShell - à juste titre - fait dans les coulisses).

  • Vous appelez _un fichier batch_ avec _un argument qui ne contient pas d'espaces, mais contient cmd.exe métacaractères_ tels que & , ^ , ou | ; par exemple:

    • .\someBatchFile.cmd 'http://example.org/a&b'
    • PowerShell - à juste titre - transmet http://example.org/a&b _unquoted_ (car il n'y a pas d'espace blanc incorporé), mais cela interrompt l'appel du fichier batch, car cmd.exe - sans raison - soumet les arguments passés à un fichier batch au même règle d'analyse qui s'appliquerait à l' intérieur de cmd.exe plutôt que de les accepter comme littéraux.

    • Remarque: Bien que l'utilisation de fichiers de commandes pour implémenter _directement_ les fonctionnalités diminue probablement, leur utilisation comme des _ points d'entrée CLI_ est toujours très courante, comme la CLI az Azure, qui est implémentée en tant que fichier de commandes az.cmd .

Remarque: ie gère automatiquement ces scénarios pour les fichiers batch et pour msiexec.exe et msdeploy.exe , donc pour les appels _most_ aucun effort supplémentaire ne devrait être nécessaire ; cependant, il est impossible d'anticiper tous les exécutables "escrocs".

Il existe deux façons de résoudre ce problème:

  • Étant donné que cmd.exe , après avoir appliqué sa propre interprétation aux arguments sur une ligne de commande, _does_ conserve les guillemets comme spécifié, vous pouvez déléguer à un appel ins sous Windows:

    • ins '.\someBatchFile.cmd "http://example.org/a&b"'
    • Si vous utilisez une chaîne _expandable_, cela vous permet à nouveau d'incorporer des valeurs PowerShell dans la chaîne de commande.

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

  • Vous pouvez également utiliser l'implémentation actuelle de --% , mais attention à ses limitations:

    • .\someBatchFile.cmd --% "http://example.org/a&b"'
    • Étant donné la façon dont --% est implémenté, la seule façon d'incorporer des valeurs PowerShell est de - maladroitement - définir un _aux. variable d'environnement_ et référencez-la avec la syntaxe %...% :

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


La gestion des erreurs

Le https://github.com/PowerShell/PowerShell-RFC/pull/88 en attente apportera une meilleure intégration de la gestion des erreurs avec les exécutables externes (natifs).

En attendant, le module Native est livré avec deux fonctionnalités permettant d' accepter ad-hoc de traiter un code de sortie différent de zéro comme une erreur de fin de script :

  • ins prend en charge le commutateur -e / -ErrorOnFailure , qui renvoie une erreur si $LASTEXITCODE est différent de zéro après l'appel au shell natif.

  • iee est une fonction wrapper pour ie qui renvoie de manière analogue une erreur si $LASTEXITCODE est différent de zéro après l'appel à l'exécutable externe.

Il existe de nombreuses subtilités dans les commandes fournies avec le module.
Nous espérons que l’aide assez étendue basée sur des commentaires les couvre suffisamment.

Tous les 189 commentaires

J'aime l'idée et la trouve utile, mais je pense que ce serait beaucoup plus utile (je pense spécifiquement dans le cas d'un DSL) si je peux obtenir une extension d'une seule chaîne. Donc, si j'ai construit une chaîne compatible cmd.exe dans mon PS, j'ai un moyen de chaîne textuellement à cmd.exe. Peut-être

&n -command $string

Cela me permettrait également de préciser mon interprète:

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

en termes de nom / opérateur, est "&!" utilisé? Cela ressemble à shbang ("#!").

$! est une bonne suggestion!

Le shell peut être spécifié en le spécifiant simplement comme commande:

&! /bin/sh -c blah

L'expansion $var crée un problème en ce sens que $ peut avoir besoin d'être littéral pour être passé à la commande native / shell. L'un des problèmes que cela tente d'éviter est lorsque les gens doivent trouver comment s'échapper correctement. Si vous avez besoin d'une expansion variable, vous pouvez toujours faire:

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

Cependant, dans les cas où vous souhaitez mélanger PowerShell avec la commande native, il est probablement préférable d'utiliser la syntaxe actuelle et d'être simplement conscient de ce qui doit être correctement échappé, car je considérerais cette utilisation avancée.

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

Je ne peux pas croire que vous venez de suggérer iex. :-)

@essentialexch il y a quelques fois où c'est approprié :)

Notez que dans l'exemple, on s'attend à ce que l'utilisateur ait correctement validé le contenu de $mycommand avant de l'exécuter!

Une bonne rédaction et une solution à des problèmes comme celui que vous avez décrit sont absolument nécessaires.
Je ne connaissais pas moi-même --% et je ne me souviens pas l'avoir vu malheureusement. Par conséquent, l'opérateur &n proposé pourrait souffrir de problèmes de découvrabilité similaires et il ne me semble pas naturel de combiner un symbole avec un caractère (et cela me rappelle% f dans les instructions printf C). Puisqu'il s'agit d'un changement de rupture de toute façon Y a-t-il une raison pour laquelle cela ne pourrait pas être par exemple && ?
Peut-être qu'il serait plus intuitif d'utiliser quelque chose comme des crochets (ou des doubles accolades?) Pour marquer la zone que vous souhaitez exécuter. Exemple: & [ wsl ls ] | less

Je suis d'accord que &! est le moyen le plus intuitif pour ceux provenant d'autres shells, mais dans super-duper-linter, j'exécute généralement des commandes natives en construisant un tableau de paramètres, puis en le splattant vers la commande.

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

Donc c'est le meilleur moyen en cas de conditionnel et tout le reste pour moi

@bergmeister J'ai d'abord pensé à && comme visuellement il est similaire à & que beaucoup de gens connaissent aujourd'hui. Cependant, && étant un opérateur de chaîne de pipeline peut prêter à confusion sur ce qu'il est censé faire. J'aime la suggestion &! actuellement.

@JustinGrote , il n'y a aucune raison de ne pas continuer à l'utiliser. la syntaxe &! est vraiment pour les cas où vous voulez juste couper et coller ou avoir des arguments qui entrent en conflit avec la syntaxe PowerShell et ne veulent pas ou ne savent pas comment s'échapper correctement

Oh mec, je me souviens avoir discuté de cela avec Bruce et Jason il y a dix ans. Mon instinct est qu'inventer un autre opérateur semble inutile ici. Je sais que certaines personnes sur ce fil n'ont pas entendu parler de --% mais je parierai qu'il n'y en a plus que vous ne le pensez. Quel est le problème avec:

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

Pourquoi personne n'a suggéré cela? Il est logiquement cohérent avec l'utilisation de --% ailleurs pour dire "tout après cela doit être passé sans traitement PowerShell spécial."

@oising bien pour un, cette commande ne fonctionne pas car vous devez la faire précéder de la commande que vous souhaitez exécuter, suggérez-vous d'ajouter la fonctionnalité?
image

Cela fait un peu ce que l'on attend:
function Invoke-LiteralCommand ($command) {Invoke-Expression "& $command --% $args"}

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

@JustinGrote Oui, je sais que cela ne fonctionne pas maintenant :) Je suggère que _instead_ de &n ou &! ou quoi que ce soit d'autre dont on parle. Cela a plus de sens pour moi: & est un appel et --% supprime l'analyse PowerShell; ensemble, ils sont cohérents et plus découvrables que l'ajout de quelque chose de nouveau. Je n'aime pas l'idée d'avoir un opérateur "appel" et "appel natif".

J'ai développé mon exemple pour montrer les différences.

@oising Je serais cool avec ça sur un nouvel opérateur, bien que ce soit beaucoup de frappe obtuse pour "nouvel utilisateur PowerShell qui sait bash", ce à quoi je suppose que cela serait destiné.

@oising Je serais cool avec ça sur un nouvel opérateur, bien que ce soit beaucoup de frappe obtuse pour "nouvel utilisateur PowerShell qui sait bash", ce à quoi je suppose que cela serait destiné.

Pas du tout. C'est pour l'utilisateur PowerShell qui ne comprend pas les règles de citation complexes entre PowerShell / cmd.exe / bash. Comme le titre du problème l'indique "appel natif", pour appeler des exécutables natifs.

@oising Je serais cool avec ça sur un nouvel opérateur, bien que ce soit beaucoup de frappe obtuse pour "nouvel utilisateur PowerShell qui sait bash", ce à quoi je suppose que cela serait destiné.

Pas du tout. C'est pour l'utilisateur PowerShell qui ne comprend pas les règles de citation complexes entre PowerShell / cmd.exe / bash. Comme le titre du problème l'indique "appel natif", pour appeler des exécutables natifs.

C'est vrai, ou pour ceux d'entre nous qui préfèrent ne pas avoir à y penser du tout.

Ouais, je suis avec @oising. Ce concept existe, il est tout simplement insuffisant. Si nous voulons implémenter quelque chose de complètement nouveau, il vaut mieux désapprouver / supprimer l'ancienne syntaxe.

Je sens souvent que ces idées sont exprimées, que l'on n'accorde pas assez de poids à leur cible démographique réelle. Si les utilisateurs ont déjà du mal à trouver les méthodes existantes et à trouver les méthodes existantes manquantes lorsqu'elles sont trouvées, c'est un appel à a) améliorer la documentation et b) améliorer la fonctionnalité réelle des opérateurs existants.

Au lieu de cela, nous obtenons une option étrange c) qui est censée résoudre en quelque sorte les problèmes du passé tout en introduisant un autre opérateur qui s'appuie sur une documentation qui n'est déjà pas suffisamment accessible pour que les utilisateurs la trouvent naturellement.

J'accepte que les messages d'erreur et / ou le système de suggestions soient utilisés pour aider à introduire ces concepts. Je ne suis pas d'accord pour dire que nous avons besoin d'un troisième? Quatrième? cinquième? (J'ai littéralement perdu le compte, quelqu'un m'aide ici) façon d'appeler des commandes. Les méthodes existantes sont insuffisantes - cela signifie que nous devons les _améliorer_, ne pas les laisser comme des toiles d'araignée pour semer la confusion chez les utilisateurs lorsqu'ils commencent à les explorer.

La première question que j'ai tendance à se poser lorsque les gens réalisent qu'il y a 5 façons de faire quelque chose est "pourquoi y a-t-il 2-3 façons qui font littéralement la même chose mais qui ne fonctionnent pas aussi bien", et ma réponse est comme cela a toujours été - l'équipe PS est trop concentrée sur le fait de s'assurer que tout de la dernière décennie ++ fonctionne toujours, lorsqu'elle essaie d'ajouter / d'améliorer des fonctionnalités. Ici, nous le voyons à nouveau, nous avons des implémentations existantes mais insuffisantes qui nécessitent une révision et une amélioration, et la solution proposée est de brouiller davantage le pool en ajoutant une autre implémentation potentiellement incomplète.

Nous devrions terminer ce que nous avons commencé, ne pas recommencer une autre mise en œuvre, OMI.

Je crois que si nous allons utiliser & pour plus de quelques cas d'utilisation uniques, nous devrions commencer à penser à en faire un verbe ou à regarder la normalisation dans la langue.

La dernière chose que je voudrais, c'est la confusion sur ce que & est censé faire. Dans son intégralité.

Je considérerais 3 scénarios:

  1. Extension complète - comportement actuel de PowerShell.
  2. Littéral complet - qui est proposé dans l'OP.
  3. Expansion partielle - wsl ls $path fonctionne toujours

Je me demande où @ mklement0 commente? :-)

Au début de la lecture de ce fil, j'ai pensé; bonne idée! Mais après avoir lu les commentaires, cela m'a fait réfléchir. J'ai toujours trouvé que la commande & "n'était pas digne des standards PS" et quelque chose me rappelant "PERL days" (ugh). Introduire une autre commande PS non standard (n'étant pas nom-verbe) n'aidera pas. Certainement pas ceux qui sont moins avertis à PS.

Je ne connaissais pas non plus le paramètre -%. J'utilise le même principe que @JustinGrote comme solution.

Couper / coller des commandes dans le shell PS n'a jamais été mon «truc» principal.
Ne ferions-nous pas mieux de remplacer ces commandes natives par celles de PowerShell?
Créez le chemin pour remplacer entièrement cmd.exe ...

Je vote pour explorer l'amélioration des commandes existantes. Et - en effet - l'amélioration de la documentation sur -% et & utilisation

@iSazonov le cas d'utilisation proposé par @ SteveL-MSFT est le scénario du «copier-coller». Une expansion partielle rendrait les choses beaucoup plus difficiles je pense (du côté AST des choses)

Immédiatement si de "#!" et alors "!#". Aimer le «&!».

Nous pouvons probablement envisager d'ajouter des accélérateurs de jetons aux appels de fonction dans le tokenizer. Cela permettrait d'utiliser les éléments suivants.

Iex -l
Ou Invoke-Expression -literal qui arrête l'analyse avant de passer.

Je pense que l'ajout de jetons uniques au niveau de l'analyse augmente la barrière à l'entrée et la découvrabilité de cette fonctionnalité diminue.

Get-Help & par exemple ne s'affiche avec rien. Et nous devons le rechercher dans about_Operators.

Cependant, il existe des cas uniques pour le call_operator qui ne sont pas documentés.
& nom de module {commande}
vous permet d'étendre la portée d'un module. Et je suis certain qu'il y en a plus. Mais la découvrabilité est si faible qu'elle devient difficile à trouver dans la documentation native. Et la communauté ne le sait que grâce à JSnover et à une conférence qu'il a donnée nous montrant des choses cool.

Je pense que, quelle que soit la décision prise, elle doit pleinement prendre en compte à quel point cette "fonctionnalité" est découvrable, du point de vue d'un nouvel utilisateur et garder à l'esprit que les nouveaux utilisateurs essaieront de l'utiliser sur Powershell 5 non sans connaître son nouveau noyau pwsh si la découvrabilité est trop faible.

J'aimerais que nous changions le comportement existant, mais changer une API aussi fondamentale dans PowerShell casserait tellement de choses. Nous pourrions éventuellement envisager de migrer avec une configuration.

Casser les choses plus directement aurait peut-être déjà été possible avant que PS 6 ne devienne GA, mais honnêtement, même dans ce cas, cela aurait été une menace sérieuse pour la compatibilité descendante (un peu comme la fonction print() Python).

En termes de transmission d'arguments aux sous-processus, je pense que l'invocation synchrone et l'invocation Start-Process ont déjà de nombreux problèmes lors de la discussion d'une refonte, et j'ai le sentiment que les deux doivent investir en même temps pour la cohérence. Start-Process en particulier doit mettre à jour sa prise en charge du passage d'un tableau d'arguments.

Pour l'invocation synchrone avec le passage d'argument cmdline, je vois quatre types de passage d'argument possibles:

  • Comportement actuel, qui deviendrait hérité, soumis à CommandLineToArgvW sous Windows et ayant des résultats inattendus
  • Comportement par défaut, qui devrait passer les arguments par leur valeur d'expression mais appliquer les règles habituelles de jeton de mots nus, de sorte que des commandes comme > et | séparent. Dans ce mode, la valeur qu'un sous-processus prend de l'expression doit être ce qui est affiché par Write-Host $val
  • Comportement verbatim, où tous les jetons sont interprétés comme des chaînes de mots nus jusqu'à ce que le jeton d'échappement soit vu. C'est ce que --% est censé faire aujourd'hui, mais idéalement n'aurait qu'un seul jeton d'extrémité pouvant être intégré sur la même ligne comme --% ... %--
  • Comportement Bash readline, où une logique d'échappement alternative orientée sh est appliquée, permettant une compatibilité plus simple

Je pense que le premier comportement devrait être progressivement éliminé en introduisant le second en tant que fonctionnalité expérimentale, puis en les échangeant.

Les deuxième et troisième comportements pourraient avoir leurs propres sigils comme &! et &# ou peut-être +% ... -% ou quelque chose comme ça. Mais pour le mode verbatim, je pense qu'une facette importante serait de simplifier le jeton d'échappement afin que plus de jetons soient pris textuellement. Similaire à un heredoc.

Si la demande est uniquement destinée à adresser un scénario de copier-coller tel que "Tout contenu de texte après cet opérateur appellera dans le" shell par défaut "du système d'exploitation à exécuter." pourquoi ne pas le dire explicitement par shell ?
`` powerhell
PS> shell wsl ls | Moins
PS> (shell wsl ls * .txt) | Select-String Bonjour

Il est plus découvrable et lisible que les opérateurs cryptiques de style Forth / APL.

Je ne vois absolument pas comment cela résoudrait # 1995: Voir https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -653174261

De plus, le problème avec WSL est un problème de comportement de wsl.exe (voir https://github.com/PowerShell/PowerShell/issues/12975#issuecomment-650353021) et non de powershell.
(Ok, il y a un problème avec PowerShell, mais c'est # 1995)

Oh, et n'est-ce pas presque un double de https://github.com/PowerShell/PowerShell/issues/12975 ? Parce que je peux essentiellement répéter ce que j'ai mentionné ici:

@bitcrazed
Le problème est que le manque de capacité à délimiter une partie d'une ligne de commande à passer varbatim à la commande / script de réception est quelque chose qui dérange les utilisateurs tout le temps.
Qu'entendez-vous par "partie d'une ligne de commande à passer varbatim"? Voulez-vous dire 1. passer une séquence de caractères textuellement à l'exécutable appelé, de sorte que l'exécutable appelé ait cette séquence de caractères dans un élément de son tableau d'arguments (par exemple, ces caractères sont alors disponibles dans `argv [1]` dans [main ] (https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs-2019#command-line-arguments)) Ou voulez-vous dire 2. insérer une séquence de caractères verbatim dans le paramètre [`lpCommandLine` de` CreateProcess`] (https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#parameters) - - Si vous voulez dire (1.), cela fonctionne déjà essentiellement:
PS /home/User> /bin/echo 'cd / && ls . | cowsay'
cd / && ls . | cowsay
PS /home/User>
(Sauf pour le problème des citations incorporées comme discuté dans https://github.com/PowerShell/PowerShell/issues/1995) On pourrait soutenir que l'ajout d'une chaîne d'une ligne ici améliorerait certains cas d'utilisation, mais je pense, ce n'est pas vraiment le but de ce problème. Comme cela fonctionne déjà plus ou moins, je suppose que vous vouliez dire (2.) --- Si vous voulez dire (2.), alors laissez-moi exprimer mon opinion à ce sujet d'une manière un peu dramatique: S'il vous plaît, veuillez ne pas ajouter syntaxe spéciale pour cela. C'est essentiellement ce que «-%» a essayé de faire, ce qui n'aurait jamais dû être implémenté. Pourquoi suis-je si fermement contre cela? 1. C'est un problème uniquement Windows, donc l'ajout de syntaxe signifierait que PowerShell sous Windows a une syntaxe différente de celle sous Linux. (Ou la syntaxe serait prise en charge mais n'a aucun sens, comme c'est actuellement le cas pour `-%`) 2. Si le shell de ligne de commande principal sur Windows publié par Microsoft ajoute une fonctionnalité de première classe (via une syntaxe spéciale opposée à via une applet de commande) pour appeler des exécutables qui ne suivent pas les [règles d'analyse de la ligne de commande] typiques (https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs -2019 # parsing-c-command-line-arguments) (si l'outil suit les règles typiques, vous n'avez pas besoin de (2.), vous pouvez généralement mieux utiliser (1.)), alors cela encourage les auteurs de ligne de commande outil, de ne pas suivre ces règles, ce qui ne fait qu'aggraver ["l'anarchie de la ligne de commande Windows"] (https://stackoverflow.com/a/4094897/2770331). Moins les gens suivent les règles typiques, plus il est difficile d'appeler par programme des exécutables externes ou d'écrire généralement du code multiplateforme, donc je pense vraiment que les auteurs de programmes devraient être encouragés à suivre ces règles typiques. 3. Je doute fortement que ce soit un cas d'utilisation courant> Et ce n'est pas seulement un problème qui affecte WSL: il affecte également les appels de ligne de commande wt de Windows Terminal et de nombreux autres outils. Pourriez-vous ajouter quelques exemples où de tels problèmes surviennent? Parce que dans le cas de WSL, je dirais que l'analyse par WSL de la ligne de commande est simplement [cassée] (https://github.com/Microsoft/WSL/issues/1746) (le problème concernait `bash.exe` mais la situation est par défaut pas mieux avec `wsl.exe`) dans le cas par défaut - je considérerais chaque outil, qui ne suit pas les [règles d'analyse de la ligne de commande] typiques (https://docs.microsoft.com/en-us/ cpp / cpp / main-function-command-line-args? view = vs-2019 # parsing-c-command-line-arguments) cassé, mais le comportement par défaut de WSL est à mon humble avis même pas correctement documenté ... J'ai dit le "défaut "Le comportement de` wsl.exe` est cassé - en écrivant cette réponse, j'ai remarqué que `wsl.exe` semble en fait se comporter comme prévu, lors de l'utilisation de` -e`:
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:\>
Donc, la seule chose qui manque pour ce cas d'utilisation explicite est IMO un paramètre à `wsl.exe` pour appeler le shell par défaut avec les arguments de ligne de commande analysés comme dans tout autre exe normal.

La seule chose concernant "la partie d'une ligne de commande à passer varbatim à la commande / script de réception", qui serait améliorée avec cet opérateur serait la signification (2.) ci-dessus, et comme mentionné, je pense que développer dans cette direction est mal.

Si tout ce que vous voulez faire est un raccourci, copiez et collez les lignes de commande existantes, pourquoi n'ajoutez-vous pas une applet de commande (par exemple Invoke-Shell , qui appelle bash -c sur linux et cmd /c sur les fenêtres?
Alors tu pourrais juste faire

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

S'il doit s'agir d'une seule ligne, ajoutez une syntaxe de chaîne d'une ligne ici, mais veuillez ne pas implémenter un opérateur spécial, qui ne peut pas être trouvé correctement et complique tout.

Et n'abandonnez pas # 1995 pour ça!

+10 points pour l'utilisation du mot "sigil".

Ce qui est bien avec &! par rapport à shell est que "shell" est un mot anglais courant, donc les gens peuvent avoir une commande existante nommée shell. (En fait, j'ai à la fois une fonction nommée "shell" et une fonction nommée "n" (donc je n'aime pas &n ).)

@oising : & --% est bien, sauf que l'un des objectifs express de la fonctionnalité proposée est de signifier que | devient quelque chose qui est passé au shell natif; pas quelque chose qui vous ramène à l'analyse PowerShell. Et si nous rendions la priorité du pipeline différente pour & --% que pour foo $blah --% more args , je trouverais cela déroutant ... et si vous faisiez & $foo --% more args | blah ? Ainsi, à mon avis, le &! proposé est suffisamment différent pour justifier d'avoir un opérateur différent. (En d'autres termes: la fonctionnalité proposée est liée mais différente de la combinaison de & et --% .)

Cette fonctionnalité est plus que juste pour «les nouveaux utilisateurs PowerShell qui connaissent bash» et «les utilisateurs qui ne connaissent pas toutes les règles complexes de guillemet / échappement». C'est aussi pour les utilisateurs les plus experts, qui veulent juste copier / coller une commande de StackOverflow sans avoir à entrer et à jouer avec tout. Cela m'arrive un peu - un produit sur lequel je travaille a beaucoup de documentation sur la façon de faire les choses, et la lingua franca est des commandes cmd.exe - donc je dois résoudre le problème% VARIABLE_REFERENCES%, etc. .; ce serait tellement plus agréable de taper &! puis de coller. En fait, si cela est implémenté, je l'appellerai probablement "l'opérateur StackOverflow". :RÉ

@ vexx32 : Ce n'est pas le "one ring to rule them all" call operator; juste un autre outil dans la boîte à outils pour résoudre un problème courant. Je ne pense pas que nous devrions nous débarrasser des outils existants pour faire de la place à celui-ci; ils font des choses différentes. Je comprends que les nouveaux utilisateurs peuvent se sentir dépassés lorsque la boîte à outils a un marteau, un tournevis, une perceuse, une perceuse à percussion et un pistolet à clous ... mais malgré le fait qu'ils sont tous pour "pousser un truc pokey à travers un ou plusieurs autres les choses », elles sont inestimables pour un professionnel; tous utilisés dans des situations différentes (bien que liées).

@TSlivede : Je conviens que # 1995 est séparé. Mais je ne suis pas d'accord avec "C'est un problème uniquement Windows": copier et coller des commandes spécifiques à la plate-forme à partir de StackOverflow s'applique également à n'importe quel système d'exploitation. (En fait, cela serait très utile pour beaucoup de gens comme moi qui sont plus forts sur une plate-forme que sur une autre - je suis plus compétent avec les technologies Windows, donc quand sous Linux j'ai tendance à faire BEAUCOUP de copier-coller à partir de SO.)

Que ce soit via un nouvel opérateur &! ou une commande Invoke-Shell , je n'ai pas de sentiments aussi forts que les autres ... Je sympathise avec les gens qui se plaignent que les opérateurs sont difficiles à rechercher, etc. mais J'aime vraiment le caractère laconique de &! .

@jazzdelightsme ce n'est pas "un autre outil dans la boîte à outils" autant que c'est "un autre accessoire de tournevis incroyablement spécifique pour un tournevis qui en a déjà environ 3-5 qui sont actuellement déjà _okay_ et pourraient être améliorés" à mon avis.

@jazzdelightsme La partie "Windows uniquement" était peut-être plus liée au problème où j'ai initialement publié cette déclaration - oui OK, ce n'est pas une copie exacte de ce problème.

Si # 1995 n'est pas abandonné et que la fonctionnalité demandée ici n'est pas résolue via une syntaxe spéciale mais via une commande (ou peut-être une nouvelle syntaxe pour une chaîne d'une ligne ici qui fonctionne partout pas seulement pour ce cas particulier), alors je suis en fait en faveur de l'ajout de cette fonctionnalité. (Ou peut-être pas "pour" mais "c'est tout à fait acceptable pour moi")

Une autre idée: si le problème concerne en fait le copier-coller de commandes complètes destinées aux shells natifs, PsReadLine pourrait peut-être ajouter une fonctionnalité pour traduire grossièrement une commande du style bash ou cmd en powershell. La traduction peut être déclenchée par une combinaison de touches spéciale ou automatiquement si une erreur d'analyse se produit lors de la tentative d'exécution d'une commande.

Cela aurait l'avantage supplémentaire d'enseigner aux utilisateurs la syntaxe de PowerShell.

Concernant la découvrabilité: PsReadLine pourrait toujours afficher un message pendant une courte période indiquant que la combinaison de touches spécifique peut être utilisée, chaque fois qu'un contenu syntaxiquement incorrect (qui serait valide après la traduction) est collé.

Mais je pourrais comprendre si c'était juste un peu trop complexe à réaliser.

@jazzdelightsme avez-vous parlé à @joeyaiello? Je pensais seulement qu'il l'appelait le StackOverflow operator qui m'a fait grincer des dents même si c'est très vrai

@ SteveL-MSFT Non, je n'en ai parlé à personne; Je suis arrivé à "l'opérateur StackOverflow" indépendamment. xD

@jazzdelightsme

... sauf que l'un des objectifs exprès de la fonction proposée est de signifier que | devient quelque chose qui est passé au shell natif; pas quelque chose qui vous ramène à l'analyse PowerShell.

Je ne suis pas sûr que vous ayez compris ma suggestion; ce que vous dites est précisément ce que je dis. Permettez-moi d'être plus verbeux avec quelques conseils stratégiquement capitalisés: 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)
@'

Qu'est-ce que j'oublie ici? Si le tube apparaît après --% c'est juste un autre caractère stupide: un argument. Il n'y a pas de modification de la priorité des opérateurs. S'il est contenu dans un pipeline imbriqué ou une sous-expression, recommencez l'analyse pour PowerShell.

et si vous faisiez & $ foo -% plus d'arguments | blabla?

Eh bien, cela tenterait d'exécuter l'argument dans $foo et passerait "more" , "args" , "|" et "blah" à être traité comme quoi que ce soit en $foo veut. Les règles me semblent assez simples, mais il est vrai que j'utilise PowerShell / Monad depuis longtemps. Ce n'est pas un changement majeur de & car & $foo --% ... traite --% comme n'importe quel autre argument. Donc, à moins que nous n'utilisions ce sigil ailleurs de manière néfaste, nous devrions être en sécurité.

Pensées?

J'ai apporté plusieurs modifications à ce qui précède, pour couvrir l'évaluation de ligne partielle, l'évaluation multiligne et le positionnement variable de --% . Le tout sans introduire de nouveaux sigils, opérateurs ou syntaxe étrange. Je pense que cela couvre tout. La seule chose est: l'analyseur peut-il le gérer? Que pensez-vous de @lzybkr @JamesWTruher @BrucePay ?

Écoutez, écoutez, @TSlivede.

Pardonnez-moi d'être franc, mais nous parlons de ces problèmes depuis un looooong temps:

Ce que propose ce numéro relève de la pensée magique:
Il est lié au "symbole d'arrêt de l'analyse" malheureux et intrinsèquement problématique, --% , qui dans sa forme actuelle est d'une utilisation limitée _ sur Windows_ (voir https://github.com/MicrosoftDocs/PowerShell- Docs / issues / 6149) et pratiquement inutile sur les plates-formes de type Unix (voir https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4963).
--% n'aurait jamais dû être mis en œuvre, et cette proposition ne devrait pas non plus être mise en œuvre telle que présentée.

La pensée magique est une fusion conceptuellement confuse de deux idées:

  • (a) "Je veux passer des jetons séparés par des espaces à un programme externe, autorisant tous les barewords, sans avoir à me soucier des métacaractères shell tels que & ou | ." - voir https://github.com/PowerShell/PowerShell/issues/12975#issuecomment -646276628

    • Bien sûr, une telle fonctionnalité est en contradiction avec l'utilisation de telles commandes _ dans le cadre d'un pipeline_ ou avec des opérateurs de chaînage de pipelines_ ( && et || ), ainsi que le placement de commandes supplémentaires sur la même ligne, après ; - et, non des moindres, être capable d'utiliser toutes les variables et expressions PowerShell dans la ligne de commande.
  • (b) "Je veux que ma ligne de commande soit traitée comme je suis habitué à _ du shell que j'utilise normalement_ (ou du shell pour lequel la ligne de commande a été écrite à l'origine)", ce qui, bien sûr, _est_ soumis à l'utilisation de caractères sans guillemets tels que & ou | étant interprétés comme des métacaractères et ayant donc une signification _syntactique_.

    • Exemple: ni cmd ni /bin/sh (ni aucun shell de type POSIX tel que bash ) n'accepterait wsl ls | less d'une manière qui passe ls , | et less _verbatim_ jusqu'à wsl - ils interpréteraient tous _sans guillemets_ | comme _leur_ symbole de tube.

Ces idées sont en contradiction les unes avec les autres et nécessitent des solutions distinctes - dont aucune ne nécessite une syntaxe particulière en soi:

  • La solution à (a) consiste à _abandonner_ l'idée et à utiliser à la place la propre syntaxe existante de _PowerShell_ pour vous assurer que les métacaractères sont utilisés _verbatim_ - ce qui nécessite invariablement _quoting_ ou _escaping_ ,:

    • Soit: citez des mots nus (jetons non entre guillemets) qui contiennent des métacaractères _ dans leur ensemble_: wsl ls '|' less
    • Ou: ` -escape metacharacters _individually_ in barewords: wsl ls `| less
    • Ou: utilisez un _array [variable] _ pour passer les arguments _verbatim_: $wslArgs = 'ls', '|', 'less'; wsl $wslArgs
    • Bien sûr, # 1995 démontre que, malheureusement, cette approche est et a toujours été cassée quand il s'agit d'arguments qui ont _embedded_ " _ lors de l'appel de programmes externes_ - _ cela doit être abordé_: voir ci-dessous.
  • La solution de (b) consiste en fait à _appeler le shell que j'utilise normalement_, en passant la ligne de commande _ comme une seule chaîne_.

    • Comme le suggère @TSlivede , une cmdlet Invoke-Shell à laquelle vous passez la ligne de commande _ comme une seule chaîne_ et qui appelle le _platform-native shell_ est la solution - et nous avons déjà une syntaxe pour les chaînes verbatim ( '...' ou sa variante de chaîne ici, @'<newline>...<newline>'@
    • Ma recommandation est de l'appeler Invoke-NativeShell , et d'appeler /bin/sh - et non bash - sur les plates-formes de type Unix.

      • De plus, je suggère de définir ins comme un alias succinct.

    • Cette approche résout tous les problèmes conceptuels avec --% et avec la proposition à portée de main:

      • Cela rend très évident la fin de la commande passée au shell natif (du fait d'être _une seule chaîne_), et permet d'utiliser un appel Invoke-NativeShell / ins dans un pipeline PowerShell normal / avec Redirections PowerShell sans ambiguïté.

      • Vous pouvez éventuellement utiliser une chaîne _expandable_ ( "...." ou sa variante de chaîne ici) afin d'incorporer des valeurs de variable _PowerShell_ dans la ligne de commande (ce que --% ne peut pas faire).


Puisque les pouvoirs en cause ont conclu que # 1995 ne peut pas être corrigé de peur que la rétrocompatibilité ne soit rompue - après tout, toutes les solutions de contournement existantes seraient rompues, voir https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606 la meilleure chose que nous puissions faire est de soutenir l'opt-in dans le comportement approprié avec une syntaxe aussi succincte que possible, afin de minimiser la douleur d'avoir à faire tout votre possible pour obtenir le comportement qui aurait toujours dû être le défaut.

Par conséquent: une syntaxe spéciale telle que &! ne doit être fournie qu'en tant qu'opt-in pour obtenir le passage correct des arguments aux programmes externes afin d'adresser # 1995 , en fonction du comportement proposé dans la RFC de @TSlivede - voir https://github.com/PowerShell/PowerShell-RFC/pull/90. Si une future version de PowerShell est un jour autorisée à rompre la compatibilité descendante, c'est l'invocation / l'invocation directe avec & qui devrait présenter ce comportement.

Du point de vue de l'utilisateur, cela signifie: tout ce que j'ai à faire est de satisfaire les exigences de syntaxe de _PowerShell_ - voir https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -640711192 - et je peux compter sur PowerShell pour transmettre de manière robuste les jetons textuels résultants au programme cible - _c'est un mandat de base de tout shell, que PowerShell n'a malheureusement jamais satisfait en ce qui concerne les programmes externes_.

Par exemple, la commande suivante - qui ne fonctionne actuellement _pas_ comme prévu (lorsqu'elle est appelée directement ou avec & - voir ce problème de documentation ) - devrait alors fonctionner:

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`

Ceux qui recherchent une solution de contournement dans les versions actuelles / antérieures de PowerShell peuvent trouver une fonction simple qui couvrira la plupart des cas d'utilisation à https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -303345059 (limitations discutées sur https: // github.com/PowerShell/PowerShell/issues/1995#issuecomment-649560892), et une version plus élaborée sur https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606

PS: Le problème de la découverte des opérateurs (pas seulement basés sur des symboles) est un problème distinct qui vaut la peine d'être résolu en lui-même: la solution est de _enhance Get-Help pour permettre la recherche d'opérateurs_ - voir # 11339, qui aussi liens vers une solution provisoire.

Puisque les pouvoirs en cause ont conclu que # 1995 ne peut pas être corrigé de peur que la rétrocompatibilité ne soit rompue - après tout, toutes les solutions de contournement existantes seraient rompues, voir # 1995 (commentaire) - la meilleure chose que nous puissions faire est de soutenir l'opt-in dans le comportement approprié avec une syntaxe aussi succincte que possible, afin de minimiser la douleur d'avoir à faire tout son possible pour obtenir le comportement qui aurait toujours dû être la valeur par défaut.

@ mklement0 Je pense que l'opt-in faisait partie de https://github.com/PowerShell/PowerShell/issues/1995 et cela a été rejeté. Donc, pour moi, cela ressemble à un vœu pieux. Ai-je tort?

@AndrewSav , nous avons initialement discuté d'un opt-in via une variable de configuration ou de préférence.

Cependant, @joeyaiello a déclaré ce qui suit dans https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -653164987 lorsqu'il a clos le problème (italiques ajoutés):

Tout ce que nous touchons ici est tout simplement un changement de rupture, et nous devrions résoudre le problème avec un nouvel opérateur

Un nouvel opérateur _est_ un opt-in en quelque sorte, mais sans problème, j'espère (même si la nécessité est malheureuse, mais c'est le prix de la compatibilité descendante).

Bien que cette proposition résout prétendument aussi # 1995, ce n'est pas le cas, étant donné qu'elle vise (principalement) à s'adapter à la syntaxe des _autres_ shells. Étant donné que les deux problèmes méritent d'être résolus, ma proposition était la suivante:

  • Introduisons un nouvel opérateur, mais utilisons-le uniquement pour corriger # 1995 (via https://github.com/PowerShell/PowerShell-RFC/pull/90), c'est-à-dire pour réparer le _own_ de PowerShell (émulation non-autre-shell) cassé traitement des arguments passant à des programmes externes.

    • Le nouveau code doit alors toujours utiliser &! (ou la forme que nous choisissons) au lieu de & lors de l'appel de programmes externes (vous pouvez le considérer comme obsolète & pour les programmes externes) .
    • Lors de l'appel des commandes _PowerShell_, &! devrait agir comme & fait maintenant (il n'y a jamais eu de problème), permettant ainsi l'utilisation cohérente de &! .
  • Pour résoudre le problème de reuse-command-lines-from-other-shells, implémentons une cmdlet Invoke-NativeShell / ins qui appelle le shell natif approprié à la plate-forme ( cmd /c sous Windows , /bin/sh -c sur les plates-formes de type Unix) avec la ligne de commande passée sous forme de _single string_.

    • Dans le cas le plus simple, sans ' présent dans la ligne de commande, une chaîne régulière entre guillemets fonctionnera; par exemple:

      ins 'ls | chat -n '

    • Avec ' présents, ils peuvent être soit _doublés_; par exemple:

      ins 'echo' 'salut' '| chat -n '

    • ou, si le désir est de ne pas avoir à toucher du tout à la ligne de commande, une chaîne ici peut être utilisée (comme déjà montré par @TSlivede) - alors que cette syntaxe est un peu plus lourde, elle devrait toujours fonctionner:

      ins @ '
      echo 'salut' | chat -n
      '@

    • de plus, en utilisant une chaîne _expandable_ (interpolation) ( "..." ou @"<newline>...<newline>"@ ), vous avez la possibilité d'incorporer des variables _PowerShell_ dans la chaîne passée au shell natif (quelque chose que --% ne prend pas en charge):

      $ foo = 'salut'
      ins @ "
      echo '$ foo' | chat -n
      "@

    • Remarque:

      • Invoke-NativeShell cmdlet serait un wrapper assez mince autour d'appels cmd /c / /bin/sh -c , que vous pourriez évidemment faire directement vous-même, mais cela a l'avantage que vous n'avez pas besoin de connaître le nom de l'exécutable spécifique et la syntaxe de passage de la ligne de commande.
      • En tant qu'effet secondaire potentiellement bénéfique, vous serez en mesure d'utiliser des paramètres communs tels que -OutVariable , et de réfléchir à l'opportunité de laisser les paramètres -Error* agir sur _stderr_ (cependant, pour la symétrie, Invoke-Command devrait alors aussi, ce qui serait un changement radical).

        • Puisque les shells de type POSIX supportent des arguments _multiple_ avec -c - le premier constituant le code à exécuter, et les suivants les arguments à passer à ce code, commençant par $0 (!) - par exemple bash -c 'echo $@' _ 1 2 3 - les arguments supplémentaires facultatifs doivent également être pris en charge.

        • Puisque le shell natif de la plate-forme est appelé, ces appels ne sont par définition pas portables, en raison des différences fondamentales entre cmd et sh .C'est pourquoi il est si important de corriger le passage d'arguments _own_ de PowerShell (# 1995), car il fournit alors une méthode fiable et véritablement multiplateforme pour appeler des programmes externes.


La dernière pièce du puzzle est la _documentation_.

https://github.com/MicrosoftDocs/PowerShell-Docs/issues/5152 est une proposition de longue date pour écrire une rubrique d'aide conceptuelle sur l'appel de programmes externes (par exemple, about_Native_Calls ), qui devrait, en un seul endroit, couvrir _tous_ les aspects pertinents pour l'appel de programmes externes; Outre ce qui y est déjà couvert (écueils de syntaxe dus à des métacaractères supplémentaires, absence de pipeline d'octets bruts, non-intégration avec la gestion des erreurs PowerShell, ...), les problèmes à résoudre doivent également être abordés:

  • Comment le passage d'arguments avec des arguments " (et _empty_) est brisé dans les versions antérieures (espérons-le bientôt) (actuellement discutées dans une proposition distincte, https://github.com/MicrosoftDocs/PowerShell-Docs / issues / 2361), et comment contourner cela (manuel \ -escaping ou utilisation de l'une des fonctions d'assistance de # 1995, dont l'une est suffisamment succincte pour être directement incluse dans le sujet).

  • Comment seulement &! devrait être utilisé à l'avenir pour appeler des exécutables natifs (externes), afin de garantir un passage d'arguments correct.

  • Comment Invoke-NativeShell / ins peut être utilisé pour exécuter des lignes de commande _écrites pour le shell_ natif de la plateforme en l'état.

Intéressant, les utilisateurs peuvent aujourd'hui:

  1. invocation directe
  2. & opérateur
  3. Processus de démarrage

Il n'est pas du tout clair que les utilisateurs devraient utiliser.

Après avoir corrigé un comportement cassé, les utilisateurs auront les fonctionnalités suivantes:

  1. invocation directe
  2. & opérateur
  3. Processus de démarrage
  4. &! opérateur
  5. Démarrer-ProcessV2

N'est-ce pas trop pour le shell d'exécuter ping 127.0.0.1 ?

Oui, c'est ma principale préoccupation avec l'ajout d'un autre opérateur également @iSazonov. C'est déjà déroutant, et cela n'aidera pas. La documentation n'est qu'une partie du puzzle. Même si nous avons une documentation décente, le simple fait qu'il existe cinq façons différentes de le faire, chacune se comportant un peu différemment, causera toujours de la confusion.

Si nous nous soucions du tout de l'UX, nous devrions choisir d'améliorer les cas existants plutôt que d'en ajouter encore plus.

Si nous nous soucions du tout de l'UX, nous devrions choisir d'améliorer les cas existants plutôt que d'en ajouter encore plus.

Ce que vous ne pouvez pas car vous devez être rétrocompatible. Je sens que nous tournons en rond. https://github.com/PowerShell/PowerShell/issues/1995 était cela et il a été fermé pour cette même raison.

@iSazonov :

Le manque d'orientation existant doit être corrigé même si nous n'introduisons pas de nouvel opérateur:

Pour résumer brièvement:

  • N'utilisez pas Start-Process pour appeler les programmes _console_ (terminal) (sauf si - qui est disponible uniquement sous Windows - vous voulez les exécuter _dans une nouvelle fenêtre_).

  • L'appel direct et l'appel via & sont _equivalents_; (en laissant de côté les blocs de script), & n'est nécessaire que pour des raisons _syntactiques_, chaque fois que le nom ou le chemin de la commande est _ guillemet_ et / ou contient des _ références variables_.


Après avoir corrigé un comportement cassé, les utilisateurs auront les fonctionnalités suivantes:
invocation directe

  • & opérateur
  • Processus de démarrage
  • &! opérateur
  • Démarrer-ProcessV2
  • Non, il n'y aura pas de Start-ProcessV2 , juste un _nouveau paramètre_, -IndividualArguments - voir https://github.com/PowerShell/PowerShell/issues/5576#issuecomment -552124719

  • Oui, il y aura &! (ou quel que soit le symbole choisi), mais il remplace effectivement & et l'invocation directe_ (au moins pour les _ programmes externes_, mais vous serez en mesure de l'utiliser avec les formulaires de commande _all_) - qui devraient être faciles à communiquer.

N'est-ce pas trop pour le shell d'exécuter ping 127.0.0.1 ?

ping 127.0.0.1 continuera à fonctionner, mais si vous voulez que bash -c 'echo "one two"' fonctionne, vous devrez utiliser
&! bash -c 'echo "one two"'

C'est malheureux, mais - étant donné que l'invocation directe / & comportement _ne peut pas être corrigée_ - cela me semble être la meilleure solution.

Heureusement, c'est une règle simple à mémoriser: _Si vous voulez que des arguments prévisibles passent à des programmes externes, utilisez &! _

Les formes alternatives de l'opt-in - configuration (via powershell.config.json ) ou une variable de préférence - sont beaucoup plus problématiques:

  • La portée dynamique des variables de préférence rend trop facile l'application par inadvertance de l'opt-in au code hérité qui n'a pas été conçu pour cela - voir https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -650262164

  • La configuration s'appliquerait aux sessions dans leur ensemble, ce qui rendrait la désinscription totalement impossible.

  • Dans les deux cas, une action est nécessaire qui est _séparée_ d'un appel spécifique, et regarder un appel donné ne vous dira pas comment il se comportera - en revanche, &! ne laisse aucun doute sur le comportement qui en découlera.

Pour l'utilisateur final, cela semble tout aussi horrible que si nous avions lancé une calculatrice et y trouvions 5 boutons pour l'opération d'ajout. 😄

Si nous voulons tellement l'améliorer que nous sommes prêts pour de telles complications, alors peut-être que cela suffit pour accepter un changement de rupture (pour un appel direct et & ) afin de simplifier le comportement et de publier la V8.0.

Si notre désir n'est pas si fort, il est possible d'améliorer suffisamment les diagnostics ( prog.exe param1 param2 -Whatif et pareil pour Start-Process -WhatIf) afin qu'il affiche ce que le programme testargs affiche et des recommandations sur la façon dont pour effectuer correctement l'échappement.

Le développeur de script peut se permettre de passer du temps à tester et à utiliser le débogueur. Mais dans une session interactive, tout utilisateur veut la simplicité, la brièveté et la clarté, et cela l'emporte sur tout.

@iSazonov :

alors peut-être que cela suffit pour accepter un changement de rupture (pour un appel direct et &) afin de simplifier le comportement et de publier la V8.0

Vous avez certainement mon vote - mais les pouvoirs en place soutiennent-ils ce plan ? [_update_ - voir # 13129]
Si nous prenons un changement de cette ampleur, nous pouvons enfin nous attaquer à tous les bagages historiques - voir # 6745

Si nous voulons tellement l'améliorer

Il y a deux bonnes raisons d'en vouloir autant:

  • Pour mettre fin à des années de souffrance en raison de la rupture des disputes - comme en témoigne # 1995, de nombreux problèmes connexes et d'innombrables questions sur Stack Overflow.

  • Parce que - comme cela a été dit plusieurs fois auparavant - appeler des programmes externes avec des arguments est un mandat principal de tout shell_.

Historiquement, à l'époque de Windows uniquement, la pénurie de CLI externes capables rendait l'incapacité de les appeler correctement moins problématique, et les utilisateurs qui restaient dans le jardin clos de PowerShell (appelant uniquement les commandes natives de PowerShell) ne ressentaient pas la douleur. Mais les temps ont certainement changé:

Nous vivons à l'ère d'une multitude de CLI multiplateformes qui sont essentielles pour les tâches de développement, d'IC ​​et de déploiement.
Si PowerShell veut être pris au sérieux en tant que shell, ce problème doit être résolu.


Même si une version 8.0 de compatibilité descendante autorisée se produit (doigts croisés), sa sortie pourrait être très éloignée, et nous avons également besoin d'une solution provisoire.

Autant les améliorations proposées à la documentation sont nécessaires, je ne pense pas qu'elles soulagent à elles seules suffisamment la douleur, faute d'une solution d'exécution facilement disponible.

J'obtiens que l'opérateur est moche, mais une solution insécable _nécessite_ une syntaxe supplémentaire (plus à taper), quelle que soit sa forme.

Une alternative à &! qui est moins moche, plus facile à taper et ne «pollue» pas le langage est de fournir une _fonction_ intégrée avec un nom concis, comme la fonction iep ( I nvoke- E xternal P rogram) qui masque tous les problèmes, montrés ici .
(Notez que nous livrons déjà des fonctions qui encapsulent d'autres fonctionnalités (par exemple, pause ) ou accueillent des utilisateurs cmd.exe (par exemple, cd.. [sic]).)

Ainsi, pour que bash -c 'echo "one two"' fonctionne correctement, vous appelleriez iep bash -c 'echo "one two"'
Ou, pour utiliser peut-être un scénario plus réaliste:

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

Encore une fois, nous parlons en fin de compte d'un _compromise_ dans le service pour préserver la compatibilité descendante - c'est inévitablement une nuisance pour les utilisateurs finaux.


Améliorez les diagnostics (prog.exe param1 param2 -Whatif et pareil pour Start-Process -WhatIf) afin qu'il affiche ce que le programme testargs affiche et des recommandations sur la manière d'exécuter correctement l'échappement.

Étant donné que l'invocation directe / & n'est pas un appel d'applet de commande et que tous les arguments sont supposés être des arguments directs, traiter -WhatIf comme un paramètre commun équivaut à un changement radical (même si est en pratique).

Si vous êtes prêt à accepter un tel changement, nous pourrions également (également) prendre en charge un commutateur -DoItRight (ou un commutateur basé sur des symboles, semblable à --% ) en tant qu'opt-in pour le comportement fixe - et cela me semble pire que l'approche iep -prefix / &! .

(Une option de diagnostic insécable, je suppose, serait d'améliorer Invoke-Command , qui ne prend actuellement pas en charge -WhatIf - cependant, si nous fournissons un opt-in bien documenté pour le bon comportement, je ne pense pas que cela en vaille la peine.)

Je suis d'accord avec @iSazonov que la situation n'est pas idéale pour introduire encore une autre chose pour faire quelque chose qui est censé fonctionner, mais comme mentionné par @ mklement0, attendre une version de rupture qui corrige correctement cela pourrait prendre très très très longtemps, et beaucoup d'entre nous ont désespérément besoin d'une solution dès que possible. Pour tout ce que je peux voir, la meilleure solution actuellement est d'utiliser simplement
https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606 n'est-ce pas?

@ mklement0 Je ne sais pas comment un programme iep / invoke-external pourrait fonctionner, car une applet de commande est soumise au même paramètre que nous essayons de corriger. C'est un problème de parseur, non? Si nous essayions de tout cacher dans une applet de commande / une fonction, j'imagine que ce serait affreusement complexe étant donné qu'il faudrait annuler l'intention de munging / reconstruct. Ou est-ce que je manque quelque chose?

@oising :

Pour être clair: la fonction liée iep corrige # 1995, et non pas l'objet principal de cette proposition (ce qui ne vaut pas la peine d'être poursuivi, comme indiqué ci-dessus ), mais notez que l'intention déclarée dans l'OP est de _aussi_ résolvez # 1995.

1995 est de pouvoir s'appuyer sur la syntaxe de _PowerShell_ (et non sur celle d'un autre shell) pour passer des arguments à des programmes externes _fidérément, comme le voit verbatim PowerShell après son propre analyse_.

L'analyse propre de PowerShell est correcte, et il n'y a aucun problème tant que vous passez des arguments aux commandes _PowerShell_.

En revanche, passer des arguments à des _ programmes externes_ est gravement cassé, et c'est ce que corrige iep .

Il est cassé en raison d'un _re-guillemet et d'un échappement_ défectueux des arguments _en coulisses_, lorsque la ligne de commande finalement utilisée est construite.
https://github.com/PowerShell/PowerShell-RFC/pull/90 propose un correctif approprié (sans accord sur la façon de gérer la compatibilité descendante, et avec quelques questions mineures encore à répondre), qui repose essentiellement sur le a récemment introduit System.Diagnostics.ProcessStartInfo.ArgumentList pour effectuer les re-citations et échappements appropriés pour nous (nous transmettons simplement la collection d'arguments résultant de l'analyse de PowerShell) - ce qui, d'ailleurs, n'est jamais requis _ sous Windows_; sous Unix, sensiblement, _il n'y a pas de ligne de commande_ (en dehors d'un shell) lorsque vous passez des arguments au démarrage du processus: vous passez simplement les arguments textuellement, sous forme de tableau.

En bref:

  • Fournir iep tant que fonction intégrée serait un opt-in simple pour offrir un argument approprié passant à des programmes externes, jusqu'à ce qu'une solution appropriée - dans le contexte d'un changement radical - puisse être implémentée

  • Une applet de commande Invoke-NativeShell ( ins ) est la solution appropriée - et non un palliatif - pour fournir un moyen d'appeler des lignes de commande _un shell différent_ (le shell natif), en utilisant la syntaxe _its_ - voir ci -

Actuellement, il existe des cas où couper et coller une commande PERL ne s'exécute pas comme prévu dans Python. Allez comprendre. Cela doit être un gros désavantage pour tous ces découpeurs-pasteurs. Je pense que Python devrait avoir un opérateur spécial pour cela.

Je ne suis pas sûr que vous ayez compris ma suggestion; ce que vous dites est précisément ce que je dis. Permettez-moi d'être plus verbeux avec quelques conseils stratégiquement capitalisés: 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 $_ }

Qu'est-ce que j'oublie ici? Si le tube apparaît après --% c'est juste un autre caractère stupide: un argument. Il n'y a pas de modification de la priorité des opérateurs. S'il est contenu dans un pipeline imbriqué ou une sous-expression, recommencez l'analyse pour PowerShell.

Pensées?

Ce qui vous manque, c'est la nécessité de passer une parenthèse fermante comme argument à un programme externe. Il s'agit d'une approche DWIM typique, mais PowerShell n'est pas HAL.

Merci pour tous les commentaires détaillés, tout le monde. Parlant comme moi ici. Quelques uber-points avant d'entrer plus dans le vif du sujet:

  • Je ne suis toujours pas à bord avec un vNext / 8.0 qui décide de casser une toute nouvelle litanie de choses. La rétrocompatibilité est importante. Je sais que c'est douloureux et je préfère vivre dans un monde où nous pourrions revenir en arrière et corriger toutes les erreurs que PowerShell a jamais commises, souvent involontaires. Mais la grande majorité des utilisateurs de PS ne traînent pas ici dans les problèmes, jusqu'aux genoux dans les cas de pointe SO. Ils ont copié / collé la même solution de contournement sur quelque chose pendant 4 à 10 ans, ils ne savent pas nécessairement pourquoi cela fonctionne, et ils ne se soucieront pas que nous "corrigions les erreurs" lorsque leur script est interrompu lors de la mise à niveau. Ils savent juste que "la mise à jour de PS m'a cassé, et je ne peux pas faire confiance aux mises à jour", et puis nous nous retrouvons avec encore plus d' utilisateurs ralentissant leurs mises à jour et leur adoption.
  • Je pense que certaines personnes dans ce numéro peuvent projeter de nombreux espoirs et désirs différents dans ce problème, ou le voir comme un ou / ou avec d'autres correctifs. Je me rends compte que j'ai exacerbé cela en fermant # 1995, et j'en suis désolé (même si je dirai, je ne suis toujours pas sûr que l'un d'eux puisse réellement être corrigé, mais nous y regarderons plus attentivement), mais ceci est vraiment une solution de «trappe» qui permet aux utilisateurs nouveaux et expérimentés de se sortir rapidement d'une impasse sans laisser monter la frustration. Nous pouvons encore avoir des conversations à plus long terme sur les correctifs du côté de l'analyse PS de la maison afin de pouvoir offrir une solution véritablement multiplateforme pour tout.

Maintenant, commente comme je l'ai lu:

  • Je me méfie du désir proposé d'aligner avec #! (avec &! ou $! étant donné que, dans le cas par défaut, les utilisateurs ne spécifieront probablement pas l'interpréteur . Si je veux faire quelque chose comme $! net <stuff> , net.exe n'est pas mon interprète. Et sur les non-Windows, la chose "native" à faire est de passer à sh / bash / peu importe avant passer au binaire en cours d'exécution. Par exemple, je ne veux pas que Python analyse *.py dans la chaîne $! /usr/bin/python *.py ou même $! python *.py . Je veux que Bash fasse le globbing et ensuite la liste des correspondances à python .
  • Comme @oising l'a suggéré, je suis en faveur de --% comme opérateur, surchargé pour être utilisé à l'avant. La découvrabilité est un problème, et étant donné que nous travaillons déjà depuis 10 ans sur la découvrabilité de celui-ci ( il est désormais compatible avec & ici? Pourquoi ne pas simplement commencer la ligne avec --% native.exe do /some $stuff ?
  • @rjmholt : Je n'ai pas totalement compris votre commentaire ici , mais je pense que nous ne devrions probablement rien changer avec le comportement existant de --% où il vient après l'exécutable, mais je pense que nous pouvons faire votre "nouveau comportement "avec où il vient avant l'exécutable.
  • Lisez ceci sur @jazzdelightsme et je suis tout à fait d'accord:

    C'est aussi pour les utilisateurs les plus experts, qui veulent juste copier / coller une commande de StackOverflow sans avoir à entrer et à jouer avec tout.

    Dans mon esprit, c'est un aspect énorme du scénario, peut-être le plus important. Ce n'est pas seulement SO, ce sont des documents du Web qui supposent que vous êtes dans un shell de type Bash.

  • Indépendamment du fait que des choses comme # 1995 soient corrigées, le fait est que a) il y a une longue queue de cas de bord étranges comme ça, et b) ils sont vraiment difficiles à réparer sans briser les gens. Ce n'est pas nécessairement vrai pour tous, mais nous l'avons suffisamment frappé au cours des deux dernières années pour qu'il me semble que nous avons besoin d'une «trappe» comme celle-ci pour sortir les gens de situations poilues où ils ne veulent pas plonger le trou de lapin de notre imbriqué échapper (pour découvrir qu'il ya un bug comme # 1995 qui les empêche de travailler même une fois qu'ils ne savent ce qu'ils savoir).

En examinant les commentaires, je pense qu'il y a deux questions ouvertes auxquelles nous devons encore répondre:

  1. Continuons-nous à analyser les séparateurs de commandes et tout ce qui suit dans PowerShell, ou le séparateur de commandes est-il également passé textuellement au shell natif?
    Je dis que nous devrions tout transmettre textuellement au shell natif car c'est actuellement quelque chose que vous ne pouvez pas faire, et que la solution de @oising d'une parenthèse principale peut être la solution dans la mesure où nous pouvons toujours prendre en charge les tuyaux, les chaînes de pipelines, etc. .si vous voulez mélanger / faire correspondre l'analyse native et PS (bien que je veuille obtenir des opinions de ceux qui sont plus proches de l'analyseur).
  2. Quel est l'opérateur?
    Comme évoqué ci-dessus, j'aime bien --% parce qu'il a déjà la réputation d'être l'opérateur "faites simplement ce qu'il faisait", et nous allons devoir réinitialiser la découvrabilité avec quelque chose de nouveau. Je comprends les arguments comme @ mklement0 selon lesquels, si nous faisons cela, nous --% (et @ SteveL-MSFT m'a également fait valoir cet argument), mais je ne pense pas la plupart des gens dans la nature pensent à --% en termes de "il fait ce qu'il faisait dans cmd". Je ne pense pas qu'il soit exagéré de codifier cette hypothèse dans la définition de l'opérateur comme "faites ce que le shell natif fait ici".

Orthogonalement, je viens de penser à un autre scénario que nous pouvons ou non vouloir soutenir.

Sous Windows, cmd inclut (effectivement) le répertoire courant dans sa recherche PATH . Cependant, PowerShell exige que l'utilisateur pré-pend un ./ afin d'exécuter des EXE (ou quoi que ce soit dans PATHEXT ) si le répertoire actuel n'est pas dans PATH .

Ma question ouverte est donc la suivante: voulons-nous réellement transmettre la chaîne de commande entière à cmd? Je proposerais que si nous voulons vraiment éclairer le scénario copier / coller avec cet opérateur, la réponse est oui, car certains documents / tutoriels tiers ne détaillent pas nécessairement comment mettre les outils qu'ils attendent de vous. dans le PATH . Cela n'a probablement pas beaucoup d'importance de toute façon, mais je voulais l'énumérer ici.

  • Je ne suis toujours pas à bord avec un vNext / 8.0 qui décide de casser une toute nouvelle litanie de choses.

Ouais, je ne sais pas comment vous résolvez ces problèmes sans vous ruiner de manière incroyablement difficile à résoudre. De plus, je ne sais pas pour vous, mais même je ne veux pas avoir à jongler avec toutes ces différences compliquées de liant / analyseur lors de l'écriture pour plusieurs versions.

Habituellement, si je m'oppose à la rupture des changements, c'est pour d'autres personnes. Cela semble cependant que ce serait probablement un PITA à traiter, même pour les personnes présentes dans ITT.

  • Comme @oising l'a suggéré, je suis en faveur de --% comme opérateur, surchargé pour être utilisé à l'avant. (...) Pourquoi ne pas commencer la ligne avec --% native.exe do /some $stuff ?

J'aime vraiment ça. Idéalement aussi proche que possible d'appeler CreateProcess / exec (plus la gestion de nix env var et la redirection d'entrée / sortie ou l'attachement de console). Ou peut-être revenir complètement à cmd.exe / bash .

Je ne suis pas obligé de garder & devant --% non plus. Je peux justifier l'idée de traiter au moins --% comme une directive / opérateur dans ma tête. Et la multiligne? Autoriser --% avec @" et "@ pour les substitutions et sous-expressions var, et les guillemets simples @' et '@ pour passer littéral? Sans les chaînes ici, c'est une seule ligne?

Comme @oising l'a suggéré, je suis pour -% en tant qu'opérateur

Travaille pour moi.

Question, cependant: avons-nous même besoin de l'opérateur & call ici?

Tant que je peux encore faire --% & $computedPathToNativeExe foo;@workspace alors je me sens bien. Nous devons pouvoir utiliser l'opérateur d'appel avec cela.

Je dis que nous devrions tout transmettre textuellement au shell natif car c'est actuellement quelque chose que vous ne pouvez pas faire

D'accord.

Je ne pense pas qu'il soit exagéré de codifier cette hypothèse dans la définition de l'opérateur (-%) comme "faire ce que le shell natif fait ici".

Aussi d'accord!

Aussi, c'est peut-être un moyen d'accéder à la fonctionnalité pour définir les variables d'environnement avant l'exécutable qui sont définies uniquement pour la session de l'exécutable (# 3316):

--% JEKYLL_ENV=production jekyll build

Juste une pensée.

@rkeithhill , l'idée avec le call native operator est que la ligne n'est pas traitée par PowerShell et envoyée textuellement au "shell par défaut" du système d'exploitation. Donc, dans ce cas, votre premier exemple --% & $computerPathToNativeExe foo;@workspace ne fonctionnerait pas. Vous devrez utiliser la méthode actuelle et échapper le cas échéant. Le deuxième cas --% JEKYLL_ENV=productoin jekyll build devrait juste fonctionner.

la ligne n'est pas traitée par PowerShell et envoyée textuellement au "shell par défaut" du système d'exploitation

OK, cela semble raisonnable. Cependant, ce --% JEKYLL_ENV=productoin jekyll build fonctionnerait simplement sur Linux / macOS mais pas sur Windows - du moins pas sans l'aide de PowerShell, n'est-ce pas?

@rkeithhill tel que proposé actuellement qui ne fonctionnera jamais sous Windows car il repose sur le shell "par défaut" du système d'exploitation. Pour PowerShell déclarant env var pour un processus, nous avons un autre problème ouvert pour en discuter.

@ PowerShell / powershell-Committee en a discuté aujourd'hui, j'ai mis à jour la proposition originale pour refléter le résultat de cette discussion. Nous allons aller de l'avant avec une implémentation de fonctionnalité expérimentale et obtenir plus de commentaires des utilisateurs avec un code fonctionnel. Nous sommes toujours ouverts sur le sigil pour l'opérateur, mais en utilisant --% pour le moment.

$foo est un nom de fichier littéral et $PWD est le répertoire courant dans WSL. Notez que dans le premier exemple, vous devez savoir échapper à && pour qu'il s'exécute dans WSL plutôt que dans PowerShell.

$foo et $PWD sont des références de variables à évaluer par sh .

Pour rediriger la sortie d'une telle exécution dans PowerShell, l'utilisateur doit d'abord stocker les résultats dans une variable:

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

Notez que contrairement à l'opérateur d'appel actuel & , vous ne pouvez utiliser aucune syntaxe PowerShell, donc:

--% $commandline

essaiera d'exécuter $commandline littéralement (sous cmd ) ou après l'expansion du shell (sous sh ). PowerShell n'essaiera pas de résoudre cette variable.

Le problème du copier-coller est résolu en collant simplement après la saisie de &n .

en collant simplement après --%

L'exemple ci-dessus pour wsl ressemblerait à ceci:

Laquelle?

Étant donné que tous les arguments après --% sont passés au shell "par défaut", pour prendre en charge le pipelining dans PowerShell, vous devez utiliser une variable:

Ceci est une répétition.

Si nous allons ajouter quelque chose d'utile, je propose un Pivot.

Pourquoi ne pouvons-nous pas avoir quelque chose comme un littéral héréditaire

@! ""! @

Nous pouvons donc avoir un moyen réel de le faire au lieu d'un -% qui a également sa propre série de problèmes.

Au moins de cette façon, nous pouvons enterrer le problème avec de «meilleures» tactiques de conception.
Et la découvrabilité pour ces cas extrêmes de se trouver dans About_Strings où il devrait être résolu.

Juste une pensée.

Je ne vois pas comment @!"…"!@ est meilleur que Invoke-NativeShell @"…"@ . Essayons-nous de rendre PowerShell plus semblable à Brainfuck?

@ yecril71pl

Je ne vois pas comment @!"…"!@ est meilleur que Invoke-NativeShell @"…"@ . Essayons-nous de rendre PowerShell plus semblable à Brainfuck?

Je crois que @ romero126 voulait suggérer un littéral herestring en ligne, de sorte que l'on puisse écrire

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

au lieu de

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

Si un tel héritage en ligne était envisagé, je suggérerais d'utiliser une syntaxe telle que @"""…"""@ et @'''…'''@ , ce serait IMO plus facile à taper que @!"…"!@ .


@ romero126 Pourriez-vous clarifier ce que vous vouliez dire?

@ yecril71pl merci d'avoir attrapé ces erreurs, mis à jour le message principal avec des correctifs.

Il n'est pas spécifié si --% s'exécutera en ligne de commande ou en tant que fichier batch.

Je crois que @ romero126 voulait suggérer un littéral herestring en ligne, de sorte que l'on puisse écrire

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

au lieu de

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

Si un tel herestring en ligne était envisagé, je suggérerais d'utiliser une syntaxe comme @"""…"""@ et @'''…'''@ , ce serait IMO plus facile à taper que @!"…"!@ .

@ romero126 Pourriez-vous clarifier ce que vous vouliez dire?

Je crois que si nous ajoutons de la flexibilité aux chaînes pour gérer des données non analysées "presque brutes", nous n'aurions pas de problèmes avec cette fenêtre contextuelle.
@TSlivede Je suis presque entièrement d'accord avec ce que vous avez dit. Seulement avec une légère variation. Il devrait être capable de gérer à la fois une ligne simple et une ligne multiple sans interpoler quoi que ce soit à l'intérieur des guillemets. Quelle que soit la syntaxe sur laquelle nous atterrissons pour y parvenir, je suis ouvert. Je suis plus concentré sur le concept.

Avec ces exemples, cela ajoute beaucoup de flexibilité là où je n'ai pas besoin de "Invoke-NativeShell" car il ne ferait que renvoyer une chaîne. Ce qui signifie que nous sommes libres de le manipuler comme le serait une chaîne. Pour que cela puisse devenir un élément de base de ce à quoi ressemble beaucoup.

Un pivot comme celui-ci signifierait également que nous n'aurions pas à gérer les problèmes ci-dessus où nous faisons tourner nos roues sur une presque solution lorsque nous pouvons gérer un cas de pointe, plutôt que de résoudre le problème dans son ensemble.

Exemples rapides:

# 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

Cela ne résout pas le problème @ mklement0 cover dans la deuxième partie de ce commentaire . Le fait est que nous ne pouvons pas simplement passer une seule chaîne à chaque commande; dans de nombreux cas, ils doivent être séparés.

Je crois que si nous ajoutons de la flexibilité aux chaînes pour gérer des données non analysées "presque brutes", nous n'aurions pas de problèmes avec cette fenêtre contextuelle.

C'est à cela que servent ' et @' .

Avec ces exemples, cela ajoute beaucoup de flexibilité là où je n'ai pas besoin de "Invoke-NativeShell" car il ne ferait que renvoyer une chaîne. Ce qui signifie que nous sommes libres de le manipuler comme le serait une chaîne. Pour que cela puisse devenir un élément de base de ce à quoi ressemble beaucoup.

Les commandes natives renvoient une séquence de chaînes. @!" a le potentiel de devenir un élément essentiel de ce à quoi ressemble inintelligible.

Je crois que si nous ajoutons de la flexibilité aux chaînes pour gérer des données non analysées "presque brutes", nous n'aurions pas de problèmes avec cette fenêtre contextuelle.

C'est à cela que servent ' et @' .

Avec ces exemples, cela ajoute beaucoup de flexibilité là où je n'ai pas besoin de "Invoke-NativeShell" car il ne ferait que renvoyer une chaîne. Ce qui signifie que nous sommes libres de le manipuler comme le serait une chaîne. Pour que cela puisse devenir un élément de base de ce à quoi ressemble beaucoup.

Les commandes natives renvoient une séquence de chaînes. @!" a le potentiel de devenir un élément essentiel de ce à quoi ressemble inintelligible.

Je pense que tu es confus. Avec le point que je faisais.
Les guillemets simples et doubles ne développent que $ variable ou non. Il n'échappe pas automatiquement les caractères. Cela me limite aussi donc je ne peux plus utiliser son caractère définissant. Donc, au lieu de cela, pour qu'il fonctionne correctement comme il est maintenant. Je devrais quand même échapper aux personnages, ce qui explique pourquoi nous devions inclure un &! Commander.

Si vous avez une meilleure solution, je suis toute oreille. Je pense que cela touche plus de cas extrêmes que d'ajouter un &!

Les guillemets simples et doubles ne développent que $ variable ou non. Il n'échappe pas automatiquement les caractères. Cela me limite aussi donc je ne peux plus utiliser son caractère définissant. Donc, au lieu de cela, pour qu'il fonctionne correctement comme il est maintenant. Je devrais quand même échapper aux personnages, ce qui explique pourquoi nous devions inclure un &! Commander.

Quels personnages devriez-vous échapper et pourquoi?

@ romero126 Maintenant, je ne a une chaîne ici ?

Je pensais auparavant que vous pourriez considérer la syntaxe des PowerShells ici comme des chaînes ennuyeuses (au moins je suis parfois ennuyé, qu'il doit y avoir une nouvelle ligne au début et à la fin ...), mais maintenant je ne sais pas trop quoi vous dites, pourriez-vous clarifier?

@TSlivede , a convenu que les chaînes ici étaient syntaxiquement quelque peu encombrantes.

Il existe une proposition existante, # 2337, qui propose des améliorations, et bien qu'elle se concentre sur le retrait du délimiteur de fermeture, @lzybkr propose également une variation _single-line _ (- également) de type Rust dans https: // github. com / PowerShell / PowerShell / issues / 2337 # issuecomment -391152107, ce qui nous permettrait de faire ce qui suit, par exemple:

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

Le besoin d'échapper serait évité en combinant _multiple_ ' avec @ , si nécessaire (par exemple, @'' can use @' here ''@ .

En d'autres termes: il s'agirait d'une amélioration générale des chaînes littérales PowerShell, utilisables partout, dont Invoke-NativeShell bénéficierait également.

Étant donné que # 2337 est principalement axé sur autre chose, j'ai créé une proposition ciblée dans # 13204 , et je suggère que nous poursuivions la conversation là-bas.

Le besoin de s'échapper serait évité

J'ai vu cela plusieurs fois dans ce fil.

Tout indicateur du tokeniser pour commencer un nouveau mode aurait besoin d'un mécanisme d'échappement, car vous devez différencier l'indicateur du tokeniser que vous quittez ce mode de la forme littérale de ce caractère.

La forme la plus naturelle de cela aujourd'hui est les chaînes entre guillemets simples:

  • ' : lancer la tokenisation à guillemet simple
  • ' : fin de la tokenisation par guillemet simple
  • '' : écrire un guillemet simple littéral dans une chaîne entre guillemets simples

Je pense que la raison pour laquelle nous parlons de s'échapper malgré cela est:

  • L'opérateur --% définit trop de façons de quitter son mode ( | , && , || , ; , nouvelle ligne) et autorise les chaînes à l'intérieur (en supprimant leurs caractères ' )
  • Herestrings et co résolvent en un seul argument de chaîne plutôt qu'un tableau d'entre eux

Vouloir passer des arguments à un appel exécutable signifie que nous parlons en fait d'une syntaxe de tableau:

  • Comment démarrer le tableau (démarrer le mode verbatim)
  • Comment séparer le tableau (passer un seul argument au nouvel exécutable)
  • Comment la fin du tableau (fin du mode verbatim)

(Ceci est un concept natif sur * nix, puisque exec attend un tableau. Sous Windows, je pense que nous sommes obligés de prendre un tableau, de le sérialiser selon une syntaxe de chaîne particulière et de le passer par CommandLineToArgvW . Cependant, .NET fournit une abstraction basée sur des tableaux pour les deux - les tableaux ont donc encore plus de sens)

Cela signifie que nous avons besoin de deux échappements:

  • Le littéral du séparateur de tableau
  • Le littéral de fin de tableau

Par exemple, disons:

  • --% avant que la commande ne devienne la nouvelle syntaxe verbatim
  • Comment terminer la syntaxe? nouvelle ligne et ; peut-être?
  • Comment séparer les arguments? peut-être?

Les questions sont alors:

  • Comment passer un littéral ; ou nouvelle ligne?
  • Comment passer un littéral ?

Une ligne de commande native n'est pas un tableau. C'est soit une chaîne, soit un AST. Étant donné que PowerShell ne sait rien des AST dans les shells natifs, il ne reste qu'une chaîne.

Une ligne de commande native n'est pas un tableau. C'est soit une chaîne, soit un AST

Vous n'avez fourni aucun motif pour cette affirmation.

Voici où PowerShell construit l'AST pour une commande (de tout type, car il ne sait pas avant l'exécution lorsque la commande résout de quel type de commande il s'agit):

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

Notez que les éléments de commande (y compris les paramètres et les arguments) sont ajoutés dans un tableau.

Vous pouvez voir cela en action dans PowerShell comme ceci:

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

Ce tableau est compilé en expressions ici:

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

Et puis cela est passé à un appel de pipeline ici (notez que Expression.NewArrayInit(typeof(CommandParameterInternal[]), pipelineExprs) est l'endroit où les arguments sont passés):

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

Au moment de l'exécution, lorsque les expressions compilées sont exécutées, cela devient cet appel de méthode , qui passe par cet appel qui décide du processeur de commande à utiliser:

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

Finalement, lorsque le pipeline est appelé, ce tableau d'arguments est lié à la commande native à l'aide de NativeCommandParameterBinder :

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

Cela prend le tableau d'arguments, qui inclut les métadonnées AST comme le type de chaîne utilisé pour chaque argument, et construit une nouvelle chaîne pour l'invocation d'argument dans le NativeCommandProcessor ici:

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

Cela est dû au fait que l'API .NET que nous utilisons actuellement pour l'appel de processus est l'ancienne version «tous les arguments dans une seule chaîne», plutôt que la nouvelle version de .NET Core qui vous permet de transmettre un tableau et permet à .NET de reconstruire l'appel selon les besoins d'une manière spécifique à la plate-forme.

Mais l'utilisation de cette API est un détail de mise en œuvre. Par exemple, nous pourrions appeler fork / exec directement sur * nix, comme nous le faisons ici:

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

Notez que exec ici prend un tableau - nous l'avons fait parce que c'est le moyen le plus simple et le plus direct de passer des arguments à l'appel système, ce qui est finalement le but d'une syntaxe d'argument textuel.

@rjmholt :

_Fonctionnellement_, tous les besoins sont déjà couverts par les formes littérales de chaîne existantes, _si vous transmettez la ligne de commande native en une seule chaîne_, ce que je recommande vivement, via une nouvelle cmdlet Invoke-NativeShell / ins .

Le point de départ ici est une _single string_, pas un tableau: c'est une _ligne de commande entière_ - à analyser _par le shell cible_ - et il est plus logique de refléter cela dans PowerShell en tant que tel - plutôt que d'essayer de faire un chausse-pied maladroit syntaxe de shell différente dans celle de PowerShell avec quelque chose comme --%

Tout ce que Invoke-NativeShell a à faire est de transmettre la chaîne unique qui contient toute la ligne de commande du shell natif au shell natif via sa CLI:

  • sous Unix , cela signifie: passer la chaîne telle quelle, _as whole_, comme deuxième élément de l'argument _array_ passé à exec (avec -c étant le premier élément du tableau d'arguments, et /bin/sh l'exécutable); exprimé en termes .NET avec un exemple simple, où printf "'%s'" foo | cat -n est le contenu textuel de la chaîne unique contenant la ligne de commande à traverser (notez l'utilisation de .ArgumentList , et non de .Arguments ; le doublement de ' est un artefact de cet exemple uniquement):

    • $psi = [Diagnostics.ProcessStartInfo]::new('/bin/sh'); $psi.ArgumentList.Add('-c'); $psi.ArgumentList.Add('printf "''%s''" foo | cat -n'); [Diagnostics.Process]::start($psi).WaitForExit()
  • sous Windows , cela signifie: pour accommoder les bizarreries de cmd , remplissez l'argument lpCommandLine de CreateProcess comme suit (avec lpApplicationName supposé être la valeur de variable d'environnement ComSpec , qui pointe vers cmd.exe ): "/c " + cmdLine , où cmdLine est la chaîne de ligne de commande entière telle quelle; exprimé en termes .NET avec un exemple simple, où echo a^&b & ver est le contenu textuel de la chaîne unique contenant la ligne de commande à traverser:

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

      • @ yecril71pl , cela implique la réponse à votre question de savoir si la sémantique de l'invite de commande ou du fichier batch doit être utilisée: c'est la première, également utilisée par d'autres langages de script, tels que Python avec os.system() ; l'implication (peut-être malheureuse) est que vous ne pourrez pas échapper aux caractères % , et que les variables de boucle for doivent utiliser un seul % (par exemple, %i au lieu de %%i ); et que vous devez préfixer les commandes loop-body avec @ pour éviter qu'elles ne soient renvoyées (par exemple, for %i in (*.txt) do <strong i="56">@echo</strong> %i ).

Ce processus sera _transparent pour l'utilisateur final_, de sorte que tout ce sur quoi ils doivent se concentrer est de passer la ligne de commande du shell natif _verbatim_, n'ayant besoin que de satisfaire les exigences de syntaxe littérale de chaîne de _PowerShell_:

Avec le formulaire ins @'<newline>...<newline>'@ , vous _pouvez_ déjà utiliser vos lignes de commande du shell natif telles quelles: voyez ci - @"<newline>...<newline>"@ ) également - inclut la possibilité d'utiliser des variables et des expressions _PowerShell_ dans la ligne de commande (l'incapacité de le faire est l'une des principales lacunes de --% ).

Ce que mon commentaire précédent propose est une _nouvelle forme générale de littéral de chaîne brute_, qui est _séparée de cette proposition_ (et sa logique d'analyse est (espérons-le) bien définie); cependant, comme indiqué, les appels Invoke-NativeShell seraient alors plus pratiques.

Au lieu de ce que vous pouvez déjà faire (en supposant une commande Invoke-NativeShell / ins ):

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

avec le nouveau littéral brut sur une seule ligne proposé, vous pouvez simplifier:

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

Si les spécificités de cette nouvelle syntaxe brute de chaîne littérale doivent être débattues, laissez-nous le faire dans # 13204, pas ici.

Donc, en relisant la proposition pour comprendre l'intention d'origine, je vois qu'il s'agit simplement de demander une meilleure syntaxe pour /bin/sh -c '<command>' + la version cmd, ainsi qu'une logique d'échappement de chaîne spéciale pour chaque shell cible. J'avais basé ma conception initiale sur une syntaxe spéciale et le besoin de travailler avec l'analyseur et le processeur de commande natif, et sur la propriété ProcessStartInfo.Arguments .NET étant très difficile à utiliser correctement en termes de passage de devis .

Mais en fait, la chaîne Arguments simplement transmise textuellement . Donc, tout ce que nous avons à faire est de passer directement une telle chaîne.

Dans ce cas, je pense que toute nouvelle syntaxe est totalement inutile (cela compliquera simplement la logique de tokenisation et je soupçonne de confondre les gens) et à la place c'est juste une nouvelle commande, ins comme vous le dites. .NET a une logique pour cela ici que nous serons obligés de réimplémenter, mais ce n'est pas grave car nous devons être un peu plus intelligents de toute façon.

Quelques commentaires sur le commentaire de @rjmholt https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -660231641

Tout indicateur du tokeniser pour commencer un nouveau mode aurait besoin d'un mécanisme d'échappement, car vous devez différencier l'indicateur du tokeniser que vous quittez ce mode de la forme littérale de ce caractère.

Je suis d'accord et je ne pense pas qu'une syntaxe de chaîne en ligne ici puisse éviter complètement le besoin de s'échapper.

L'opérateur -% définit trop de façons de quitter son mode (|, &&, ||,;, nouvelle ligne) et autorise les chaînes à l'intérieur (en supprimant leurs 'caractères)

En parlant de --% , faites-vous référence au commutateur --% existant? Si tel est le cas, il est préférable de le préciser dans votre commentaire, car maintenant --% est proposé comme opérateur d'appel.

Vouloir passer des arguments à un appel exécutable signifie que nous parlons en fait d'une syntaxe de tableau:

La proposition est d'appeler le shell natif (sh ou cmd) et de tout passer littéralement après l'opérateur d'appel --% , comme sh -c ... . Ce n'est donc pas PowerShell qui transmet les arguments à l'exécutable cible. Dans ce cas, je pense que nous n'avons pas besoin de _une syntaxe de tableau_, non?

Comment terminer la syntaxe? nouvelle ligne et; peut être?
Comment séparer les arguments? «espace» peut-être?

Je pense que l'analyse verbatim se termine à la nouvelle ligne. Pour passer une nouvelle ligne dans le cadre de l'argument, utilisez \n Je suppose, tout comme ce que vous faites directement dans bash.

Une ligne de commande native n'est pas un tableau. C'est soit une chaîne, soit un AST

Vous n'avez fourni aucun motif pour cette affirmation.

Voici où PowerShell construit l'AST pour une commande (de tout type, car il ne sait pas avant l'exécution lorsque la commande résout de quel type de commande il s'agit):

Si cette proposition est mise en œuvre, elle introduira une construction spéciale dans laquelle PowerShell ne construira pas du tout l'AST.

Heureux de l'entendre, @rjmholt.

.NET a une certaine logique pour cela ici que nous serons obligés de réimplémenter

Je ne pense pas que nous ayons besoin de réimplémenter quoi que ce soit, comme le montrent les commandes de mon commentaire précédent: sous Windows, utilisez .Arguments pour le passage de chaîne de ligne de commande entière à WinAPI; sous Unix, utilisez .ArgumentList pour construire l'argument _array_ qui est utilisé directement avec exec .

Si cette proposition est mise en œuvre, elle introduira une construction spéciale dans laquelle PowerShell ne construira pas du tout l'AST.

Eh bien, il construirait un type AST différent, conçu à cet effet. Si cela ne fait pas partie de l'AST, c'est essentiellement la même chose qu'un commentaire, rien ne se passe.

_Fonctionnellement_, tous les besoins sont déjà couverts par les formes littérales de chaîne existantes, _si vous transmettez la ligne de commande native en une seule chaîne_, ce que je recommande vivement, via une nouvelle cmdlet Invoke-NativeShell / ins .

Personnellement, j'aime l'idée d'applet de commande Invoke-NativeShell , mieux que l'opérateur d'appel --% . La principale préoccupation que j'ai entendue à propos de cette idée est qu'il faut taper plus que --% + Ctrl + v et différencier la chaîne ici littérale de l'interpolation ici-chaîne est un autre fardeau pour l'utilisateur (_Je pense personnellement que ins est bien plus facile à taper que --% _).

La principale préoccupation que j'ai entendue à propos de cette idée est qu'il faut taper plus que --% + Ctrl + v et différencier la chaîne ici littérale de l'interpolation ici-chaîne est un autre fardeau pour l'utilisateur (_Je pense personnellement que ins est bien plus facile à taper que --% _).

Peut-être que PSRL peut vous aider. Semblable au problème d'un gestionnaire de clés pour entourer les chemins collés, PSRL pouvait lire le CommandAst , voir que c'est ins et échapper correctement la chaîne collée.

Eh bien, il construirait un type AST différent, conçu à cet effet. Si cela ne fait pas partie de l'AST, c'est essentiellement la même chose qu'un commentaire, rien ne se passe.

Bien sûr, mais l'arbre sera trivial. Il ne reflètera pas la signification des commandes à exécuter.

Démarrez le processus cmd.exe rediriger stdin / stdio et nous pouvons littéralement utiliser une chaîne pour invoquer-NativeCommand même chose avec bash

Heureux de l'entendre, @ daxian-dbw.
J'adore l'idée, @SeeminglyScience .

différencier ici-chaîne littérale de l'interpolation ici-chaîne est un autre fardeau pour l'utilisateur

Je pense que c'est un _plus_, car - contrairement à --% - l'utilisation de @"<newline>...<newline>"@ vous permet d'incorporer directement les valeurs des variables et expressions _PowerShell_ dans la ligne de commande native.

Bien sûr, cela suppose que les utilisateurs connaissent la différence entre les chaînes entre guillemets simples (verbatim) et double guillemets (interpolation) et à propos ` échappement sélectif de $ , mais je pense que c'est un (avancé) option à avoir.

Pour aider les utilisateurs de copier-coller, la fonctionnalité PSReadLine proposée doit par défaut être @'<newline>...<newline>'@ (verbatim).


En fin de compte, nous ne voulons pas promouvoir un état d'esprit où les utilisateurs n'ont pas besoin de connaître et de comprendre les exigences de syntaxe uniques de PowerShell - au lieu de cela, nous voulons promouvoir leur compréhension, à la lumière du pouvoir supplémentaire qu'ils apportent.

Mais pour encourager les utilisateurs à utiliser la syntaxe _own_ de PowerShell, qui est par définition _cross-platform_, elle doit fonctionner de manière prévisible .
À cette fin, # 1995 doit être corrigé , idéalement directement, permettant un changement de rupture, ou, si ce n'est pas une option, via une cérémonie minimale _opt-in_.

Résumer:

  • Quelque chose comme --% (la nouvelle forme d'opérateur proposée ici), par définition une fonctionnalité _platform-specific_, ne remplace en aucun cas la correction # 1995 .

  • Invoke-NativeShell ( ins ) évite toutes les limitations de l'opérateur --% proposé et fournit la solution de "trappe" mentionnée par @joeyaiello : un moyen rapide d'exécuter une ligne de commande écrite pour le shell natif, sans avoir à l'adapter à la syntaxe de PowerShell.

  • --% ne doit rester que dans sa forme _argument_ d'origine, et doit rester une fonctionnalité _Windows-only_ (ce qu'elle _effectivement_, mais pas _techniquement_) - en d'autres termes: aucun changement n'est nécessaire.

    • Une fois # 1995 corrigé, le seul but de --% sera de prendre en charge les cas _edge sur Windows_: vous permettant de contrôler les citations explicites de la ligne de commande passée à des programmes malveillants tels que msiexec qui nécessitent des formes très spécifiques de citations intra-argumentaires.

Une des autres fonctionnalités que je souhaite vraiment dans PS v7.x est la possibilité d'avoir une commande native sortant avec un code d'erreur pour arrêter mon script, comme set -e comme indiqué dans cette RFC . Si la solution à cela est quelque chose comme un Invoke-NativeCommand , est-ce que cela prêterait à confusion avec Invoke-NativeShell ?

J'essaie juste de faire avancer un peu le film car autant que je veux la fonction native d'appel, je veux vraiment que mes scripts de construction et de test fassent une erreur lors de l'appel de msbuild, cl, cmake, Conan, npm, etc. et ces commandes natives se terminent avec un code de sortie non nul sans avoir à se souvenir de vérifier tout le temps $ LASTEXITCODE. Si l'implémentation se fait via juste une autre variable de préférence, par exemple $PSNativeCommandInErrorActionPreference alors je suppose que cela aurait un impact sur l'invocation du shell natif - il s'agit d'une "commande" native et tout?

La principale préoccupation que j'ai entendue à propos de cette idée est qu'il faut taper plus que --% + Ctrl + v et différencier la chaîne ici littérale de l'interpolation ici-chaîne est un autre fardeau pour l'utilisateur (_Je pense personnellement que ins est bien plus facile à taper que --% _).

Peut-être que PSRL peut vous aider. Semblable au problème d'un gestionnaire de clés pour entourer les chemins collés, PSRL pouvait lire le CommandAst , voir que c'est ins et échapper correctement la chaîne collée.

Mais qu'en est-il:

$c = gcm ins
& $c ...

PSRL va avoir du mal à reconnaître ce qui se passe là-bas. Il devrait être avec état, résoudre les alias ins pour invoke-nativeshell, puis comprendre que vous voulez appeler le commandinfo, puis supprimer les arguments? Je n'aime vraiment, vraiment pas l'idée d'enveloppe spéciale une fausse commande.

différencier ici-chaîne littérale de l'interpolation ici-chaîne est un autre fardeau pour l'utilisateur

Je pense que c'est un _plus_, car - contrairement à --% - l'utilisation de @"<newline>...<newline>"@ vous permet d'incorporer directement les valeurs des variables et expressions _PowerShell_ dans la ligne de commande native.

Je ne te suis pas @ mklement0. Pourquoi ne peut-on pas apprendre à --% à faire cela? J'ai déjà donné ceci comme cas d'utilisation.

Je n'aime vraiment, vraiment pas l'idée d'enveloppe spéciale une fausse commande.

Il n'y a pas de fausse commande ici, seulement une applet de commande de bonne foi, Invoke-NativeShell , alias ins .

gcm ins ( Get-Command ins ) fournira des informations sur l'alias ins sous la forme d'une instance [System.Management.Automation.AliasInfo] , que vous pourrez ensuite passer à & pour l'invocation, comme pour toute autre commande ou alias.

Par conséquent, votre exemple fonctionnera exactement comme prévu - c'est juste que si vous utilisez le détour gcm ( Get-Command ), vous devrez épeler la syntaxe littérale de chaîne appropriée _vous-même_, sans le _échafaudage de commande de commodité optionnel_ que PSRL _pourrait_ fournir, si la suggestion de @SeeminglyScience est implémentée:

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

Ou, si la nouvelle syntaxe brute de chaîne littérale proposée dans # 13204 est implémentée:

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

Bien sûr, si vous connaissez la syntaxe des chaînes littérales de PowerShell, une chaîne entre guillemets simples fera également l'affaire: il suffit de doubler la chaîne ' intégrée dans la chaîne globale '...' :

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

Pourquoi ne peut-on pas apprendre à --% à faire cela?

  • --% n'a pas de délimiteur de fermeture, ce qui l'empêche de l'utiliser dans les pipelines Powershell, ce qui est une restriction inacceptable (notez que (...) enclos n'est pas une option, car cela implique la collecte initiale lignes en mémoire plutôt que le traitement _streaming_).

  • Sous Unix, --% ne peut pas prendre en charge _ les deux_ (sans échappement) $ tant que métacaractère PowerShell _et_ en tant que métacaractère POSIX-shell.

Encore une fois: il n'y a aucune justification et aucun avantage à chausser une _ syntaxe de shell différente_ dans PowerShell - passez la ligne de commande du shell natif _ en tant que chaîne_, à ins , et tous les problèmes conceptuels disparaissent.

Invoke-NativeShell implique que PowerShell n'est pas le shell natif. Cette hypothèse est-elle toujours correcte et le sera-t-elle toujours?

@oising le cas d'utilisation est quelqu'un qui copie et colle une commande native à partir d'un README ou quelque chose. S'ils font quelque chose de plus compliqué que de taper ins ESPACE CTRL + v, ils construiront et échapperont simplement à leur propre chaîne comme d'habitude.

@ mklement0

Mais pour encourager les utilisateurs à utiliser la syntaxe _own_ de PowerShell, qui est par définition _cross-platform_, elle doit fonctionner de manière prévisible .
À cette fin, # 1995 doit être corrigé , idéalement directement, permettant un changement de rupture, ou, si ce n'est pas une option, via une cérémonie minimale _opt-in_.

Ceci est distinct de la discussion sur ins / --% n'est-ce pas? Si vous n'êtes pas d'accord, pouvez-vous expliquer pourquoi ce problème affecterait la nouvelle commande / syntaxe?

@SeeminglyScience , oui, c'est séparé, mais cela n'a pas été clair dans ce fil.
La seule raison pour laquelle # 1995 est venu ici est qu'il a été initialement fermé en faveur de ce problème (bien que heureusement rouvert depuis), et le PO indique toujours que ce problème le résoudra ("Cela devrait également résoudre ces problèmes:") .
Par conséquent, je voulais insister sur le fait que ins / --% n'adresserait _pas_ l'adresse # 1995, ce qui nécessite son propre correctif, de toute urgence.
(Ce n'est que si le passage d'argument de PowerShell fonctionne correctement que nous pouvons en toute conscience recommander aux utilisateurs d'apprendre et d'adopter pleinement la propre syntaxe de PowerShell - ce que nous devrions.)

@ SP3269 , on peut traverser ce pont quand on y arrive 😁.

Mais sérieusement:

Si vous pensez que «natif» est «écrit spécifiquement pour la plate-forme hôte [famille]», le nom conviendrait toujours, même si PowerShell devait un jour devenir le shell système par défaut sur une plate-forme donnée (j'espère, mais je ne pense pas c'est réaliste, du moins pas dans un avenir prévisible).

Quant aux alternatives:

  • Invoke-[System]DefaultShell serait le nom le plus précis (même s'il ne s'appliquerait plus sans ambiguïté si PowerShell devenait le shell par défaut d'un système), mais «natif» semble être le terme le plus couramment utilisé dans le monde PowerShell.

  • Invoke-LegacyShell a une justification sous Windows, mais je ne pense pas que les utilisateurs d'Unix accepteraient trop ce terme.

Je pense que je commence à démêler la confusion totale que je ressens en essayant de garder une trace des motivations, des idées et des problèmes de chacun ici. Il semble que nous ( @ mklement0 et moi, au moins) --% comme un indice à _parser_ pour changer la façon dont les choses sont analysées en conjonction avec l'opérateur d'appel & (pas nécessairement en utilisant --% autonome.) D'un autre côté, Michael semble regarder ins comme une applet de commande pour remplacer le propre courtier de commandes natif interne de PowerShell et l'analyseur d'arguments, et ne cherche pas à changer l'analyseur de powershell, à la place, en utilisant des chaînes / here -strings pour capturer les paramètres prévus (ce que je dis pourrait être utilisé avec --% également.)

-% n'a pas de délimiteur de fermeture, ce qui l'empêche de l'utiliser dans les pipelines Powershell

Ce point je ne comprends pas du tout. Il ne manque pas plus de délimiteur de fermeture que ins / invoke-nativecommand. Si vous avez besoin de délimiter ou d'alimenter le LHS, utilisez des chaînes ici, sinon il est considéré comme une seule instruction.

Quelle que soit la manière choisie, il semble qu'il sera nécessaire de choisir un shell natif pour envoyer la ligne de commande. Je suggère que nous utilisions les variables d'environnement COMSPEC sous Windows (par défaut %systemroot%\system32\cmd.exe ) et SHELL sous Linux (par défaut /bin/bash ) - si SHELL ne le fait pas ' t existent, nous devrions cibler /bin/sh (qui dans la plupart des cas est un lien symbolique vers bash de toute façon.)

-% n'a pas de délimiteur de fermeture, ce qui l'empêche de l'utiliser dans les pipelines Powershell

Ce point je ne comprends pas du tout. Il ne manque pas plus de délimiteur de fermeture que ins / invoke-nativecommand. Si vous avez besoin de délimiter ou d'alimenter le LHS, utilisez des chaînes ici, sinon il est considéré comme une seule instruction.

En gros, vous ne pouvez pas appeler des choses comme cmd --% /c someapp | someOtherApp et laisser cmd gérer le pipeline; PowerShell interprète toujours un pipeline (et quelques autres choses comme && et || qui seraient autrement utiles pour les gens Unix) comme étant un jeton PowerShell au lieu de le transmettre à la commande native dans le cadre de la chaîne d'argument.

et SHELL sous Linux (par défaut à /bin/bash )

Non: le shell _system_ sur les plates-formes de type Unix est _invariably_ /bin/sh .
Ceci est distinct du shell interactif d'un _utilisateur_ donné (reflété dans $env:SHELL - mais, malheureusement, actuellement pas si _PowerShell_ est le shell de l'utilisateur, voir # 12150), qui est (a) configurable et (b) souvent _par défaut à_ /bin/bash , mais même cela n'est pas une donnée, comme en témoigne le passage récent de macOS à /bin/zsh comme shell interactif par défaut pour les nouveaux utilisateurs.

Je suggère d'utiliser les variables d'environnement COMSPEC sous Windows

Bon point, c'est la meilleure façon de faire référence au shell système (interpréteur de commandes) sous Windows - j'ai mis à jour l'exemple de commande ci-dessus en conséquence.

Une des autres fonctionnalités que je souhaite vraiment dans PS v7.x est la possibilité d'avoir une commande native sortant avec un code d'erreur pour arrêter mon script, comme set -e comme indiqué dans cette RFC . Si la solution à cela est quelque chose comme un Invoke-NativeCommand , est-ce que cela prêterait à confusion avec Invoke-NativeShell ?

J'essaie juste de faire avancer un peu le film car autant que je veux la fonction native d'appel, je veux vraiment que mes scripts de construction et de test fassent une erreur lors de l'appel de msbuild, cl, cmake, Conan, npm, etc. et ces commandes natives se terminent avec un code de sortie non nul sans avoir à se souvenir de vérifier tout le temps $ LASTEXITCODE. Si l'implémentation se fait via juste une autre variable de préférence, par exemple $PSNativeCommandInErrorActionPreference alors je suppose que cela aurait un impact sur l'invocation du shell natif - il s'agit d'une "commande" native et tout?

Nous avons Start-NativeExecution dans notre module build à cet effet.

J'ai vu ça. Comment combineriez-vous exactement Start-NativeExecution avec Invoke-NativeShell si vous souhaitez que ce dernier lance efficacement un code de sortie différent de zéro? J'espère vraiment que quelque chose comme PR # 3523 arrivera avec cette fonction native d'appel car je veux que les deux fonctionnent ensemble. Je suppose que si l'appel natif est implémenté en tant qu'applet de commande (Invoke-NativeShell vs -%), alors -ErrorAction Stop pourrait être implémenté pour le lancer (erreur de fin) sur un code de sortie différent de zéro.

-% n'a pas de délimiteur de fermeture, ce qui l'empêche de l'utiliser dans les pipelines Powershell

Ce point je ne comprends pas du tout. Il ne manque pas plus de délimiteur de fermeture que ins / invoke-nativecommand. Si vous avez besoin de délimiter ou d'alimenter le LHS, utilisez des chaînes ici, sinon il est considéré comme une seule instruction.

En gros, vous ne pouvez pas appeler des choses comme cmd --% /c someapp | someOtherApp et laisser cmd gérer le pipeline; PowerShell interprète toujours un pipeline (et quelques autres choses comme && et || qui seraient autrement utiles pour les gens Unix) comme étant un jeton PowerShell au lieu de le transmettre à la commande native dans le cadre de la chaîne d'argument.

Oui, je comprends le comportement actuel @ vexx32 . Mais nous ne parlons pas (du moins je ne le suis pas) du comportement actuel de --% - nous parlons de l'améliorer, non? Pourquoi ai-je le sentiment que nous nous parlons tous ici? :)

Comme je l'ai dit plus haut, nous pourrions améliorer & pour travailler avec --% , ce qui est possible . Je pense également que nous devons être clairs sur les exécutions natives implicites par rapport à l'invocation explicite d'un shell avec des arguments. C'est une source constante de confusion - même pour les utilisateurs chevronnés de Windows - que d'une manière ou d'une autre cmd.exe (un shell) est nécessaire pour "exécuter" d'autres exécutables; il en va de même pour linux / osx.

Oui, je comprends le comportement _current_ @ vexx32 . Mais nous ne parlons pas (du moins je ne le suis pas) du comportement _current_ de --% - nous parlons de l'améliorer, non? Pourquoi ai-je le sentiment que nous nous parlons tous ici? :)

Donc, le pas actuel pour ajuster --% est que si c'est là que le nom de la commande se trouverait généralement, tout ce qui se trouve à sa droite est analysé tel quel (jusqu'à une nouvelle ligne) et envoyé directement à bash / cmd. Il n'y a pas de place pour la syntaxe PowerShell comme les chaînes here-strings et autres, car il a alors tous les mêmes problèmes que --% et le processeur de commande natif ont actuellement.

J'ai vu ça. Comment combineriez-vous exactement Start-NativeExecution avec Invoke-NativeShell si vous souhaitez que ce dernier lance efficacement un code de sortie différent de zéro? J'espère vraiment que quelque chose comme PR # 3523 arrivera avec cette fonction native d'appel car je veux que les deux fonctionnent ensemble. Je suppose que si l'appel natif est implémenté en tant qu'applet de commande (Invoke-NativeShell vs -%), alors -ErrorAction Stop pourrait être implémenté pour le lancer (erreur de fin) sur un code de sortie différent de zéro.

Actuellement, ce sera Start-NativeExecution { Invoke-NativeShell <strong i="10">@whatever</strong> } .

Ce point je ne comprends pas du tout. Il ne manque pas plus de délimiteur de fermeture que ins / invoke-nativecommand. Si vous avez besoin de délimiter ou d'alimenter le LHS, utilisez des chaînes ici, sinon il est considéré comme une seule instruction.

Invoke-NativeShell n'a pas besoin de délimiteur de fermeture car il s'agit d'une commande normale, alors que l'horreur de --% ne l'est pas.

et SHELL sous Linux (par défaut à /bin/bash )

Non: le shell _system_ sur les plates-formes de type Unix est _invariably_ /bin/sh .

Je pense que la chose devrait être équivalente à créer un fichier de script exécutable et à l'exécuter, ce qui délègue la tâche de choisir le bon shell au système d'exploitation et nous ne devrions pas être dérangés. Y compris, si cela commence par #! , qu'il en soit ainsi.

L'idée derrière Invoke-NativeShell est de fournir un wrapper pratique autour du _CLI_ du shell natif.

  • Sous Unix, cela est tout à fait suffisant, et la création d'un fichier de script auxiliaire introduit non seulement une surcharge supplémentaire, mais signale ensuite une valeur aléatoire dans $0 (le chemin du fichier de script), alors que l'invocation via l'interface de ligne de commande contient de manière prévisible /bin/sh et peut même être défini explicitement en suivant l'argument code avec un autre argument (par exemple, sh -c 'echo $0' foo )

    • (En aparté: créer un script shell avec la ligne shebang #!/bin/sh signifie cibler le shell système par le chemin complet tout aussi explicitement que d'appeler directement /bin/sh ).
  • Sous Windows, l'exécution via un fichier batch _would_ apporterait des avantages (mais pas pour déléguer la tâche de localisation du shell système, ce que $env:ComSpec manière prévisible), car cela éviterait les problèmes d'échappements de % et for comportement mentionné ci -

Pour aborder ce dernier par courtoisie (afin que les utilisateurs n'aient pas à écrire leurs propres fichiers aux. Batch), pour plus de clarté conceptuelle, nous pourrions offrir un
-AsBatchFile passer en tant que _opt-in_.

Cela dit, s'il y a consensus sur le fait que la majorité des lignes de commande publiques cmd utilisées pour le copier-coller sont écrites avec la sémantique _batch-file_ à l'esprit ( %%i plutôt que %i tant que variable de boucle, possibilité d'échapper textuellement à un % comme %% ), peut-être invariablement en utilisant un aux. le fichier batch _sur Windows_ (tout en restant fidèle à la CLI sous Unix) est la meilleure solution - je ne me sens pas fortement à ce sujet, mais cela devrait être clairement documenté, d'autant plus que cela signifie présenter un comportement différent des autres langages de script.

@rkeithhill

Pour moi, le RFC d'intégration des erreurs natives auquel vous vous connectez vaut la peine d'être abordé aussi urgemment que # 1995:

Les deux représentent des étapes nécessaires pour faire des programmes externes (utilitaires natifs) des citoyens de première classe dans PowerShell (autant que conceptuellement possible), ce qu'ils auraient toujours dû être.

Être un citoyen de première classe signifie: une invocation _direct_ (avec & nécessaire uniquement pour des raisons _syntactiques_, situationnellement), pas _ via une applet de commande_.
En d'autres termes: il ne devrait jamais y avoir Invoke-NativeCommand cmdlet Start-NativeExecution .

(En aparté: Start-NativeExecution est mal nommé, car de nombreuses fonctions du module build sont; Start signaux opération _asynchrone_, alors que la plupart de ces fonctions sont _synchrones_ - voir la discussion sur Start-Sleep sur https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4474).

Par conséquent, Invoke-NativeShell ne nécessite aucune considération particulière: PowerShell, comme il le fait déjà, définira $LASTEXITCODE fonction du code de sortie signalé par le processus exécutable du shell natif ( cmd / sh ) invoqué.

Ensuite, le mécanisme proposé dans la RFC agira sur lui, comme il agirait sur les exécutables directement invoqués (par exemple, si $PSNativeCommandErrorAction = 'Stop' est défini, une erreur de fin de script se produirait si $LASTEXITCODE est différent de zéro. )

(Un petit aparté - cette discussion n'a pas sa place ici: comme pour un mécanisme _par-appel_: quelque chose comme
/bin/ls nosuch || $(throw 'ls failed) fonctionne, tout comme /bin/ls nosuch || $(exit $LASTEXITCODE) (l'équivalent du /bin/ls nosuch || exit d'un shell POSIX); # 10967 explique pourquoi nous ne pouvons malheureusement pas éviter $(...) (sans changements majeurs de grammaire); de même, le besoin de se référer explicitement à $LASTEXITCODE ne peut probablement pas être évité.)

(En aparté: créer un script shell avec la ligne shebang #!/bin/sh signifie cibler le shell système par le chemin complet tout aussi explicitement que d'appeler directement /bin/sh ).

Ce n'est pas ce que je voulais dire. Je voulais dire que PowerShell n'a pas besoin de savoir quel est le shell par défaut, car les systèmes d'exploitation ont cette fonctionnalité intégrée. Linux sait comment exécuter un script exécutable au cas où il ne commencerait pas par #! ou il commencerait par quelque chose d'autre, comme #!/usr/bin/env python et Windows sait quoi faire pour appeler un script .CMD , donc nous ne devrions pas jeter un coup d'œil dans %COMSPEC% 'qui n'est pas non plus à mon humble avis. Windows peut également exécuter d'autres scripts, mais il est basé sur l'extension du fichier de script, donc cela ne s'applique évidemment pas à ce cas.

Encore une fois, le mandat de Invoke-NativeShell devrait être d'appeler _CLI_ du shell natif, et non de créer des fichiers de script auxiliaires (avec des effets secondaires) dans les coulisses (sauf pour des raisons différentes, comme expliqué ci -

En ce qui concerne la détermination robuste du shell système, il n'y a pas de problème à résoudre ici: le codage en dur /bin/sh sous Unix est parfaitement approprié, tout comme la consultation de $env:ComSpec sous Windows.

Non, les plates-formes Unix au niveau des appels système ne savent _pas_ comment exécuter un fichier exécutable en texte brut _sans une ligne Shebang_; que vous _pouvez_ toujours appeler de tels fichiers est une fonctionnalité pratique des shells de type POSIX: ils essaient exec , puis _ retombent_ pour interpréter ces fichiers comme écrit _pour eux_; c'est-à-dire que c'est n'importe quel shell de type POSIX en cours d'exécution qui finit par exécuter le fichier - alors que PowerShell _fails_ simplement, car il n'implémente pas cette solution de secours (vous obtiendrez Program 'foo' failed to run: Exec format error ).

Inutile de dire qu'il est donc déconseillé de créer de tels scripts.

Non, les plates-formes Unix au niveau des appels système ne savent _pas_ comment exécuter un fichier exécutable en texte brut _sans une ligne Shebang_; que vous _pouvez_ toujours appeler de tels fichiers est une fonctionnalité pratique des shells de type POSIX: ils essaient exec , puis _ retombent_ pour interpréter ces fichiers comme écrits _pour eux_; c'est-à-dire que c'est n'importe quel shell de type POSIX en cours d'exécution qui finit par exécuter le fichier - alors que PowerShell _fails_ simplement, car il n'implémente pas cette solution de secours (vous obtiendrez Program 'foo' failed to run: Exec format error ).

csh n'est pas comme POSIX et il n'échoue pas sur exec un script nu. Il invoque sh . Il en va de même pour perl .

J'espère qu'il est évident que cela fait mon point: c'est _à chaque shell_ de décider quoi faire, et différents shells / langages de script font des choses différentes; PowerShell échoue actuellement - s'il devait faire un choix et que vous vouliez que ce choix soit /bin/sh , nous sommes de retour à la case départ: /bin/sh doit être supposé.

J'espère qu'il est évident que cela fait mon point: c'est _à chaque shell_ de décider quoi faire, et différents shells / langages de script font des choses différentes; PowerShell échoue actuellement - s'il devait faire un choix et que vous vouliez que ce choix soit /bin/sh , nous sommes de retour à la case départ: /bin/sh doit être supposé.

exec in perl est un appel système direct, perl ne le modifie en aucune façon. En particulier, il ne _choisit_ pas l'interpréteur.

Bien sûr, cela suppose que les utilisateurs connaissent la différence entre les chaînes entre guillemets simples (verbatim) et double guillemets (interpolation) et à propos ` échappement sélectif de $ , mais je pense que c'est un important (avancé) option à avoir.

C'est peut-être juste moi, mais j'échangerais mille " contre un -f .

@ yecril71pl

exec en perl est un appel système direct, perl ne le modifie en aucun cas. En particulier, il ne choisit pas l'interprète.

Cela ne peut pas être vrai si cela fonctionne avec des scripts sans ligne #! . L'appel système exec sous Linux ne fonctionne pas sans #! , comme on peut facilement le tester:

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$ 

Edit: Je viens de remarquer que si vous utilisez la fonction de bibliothèque execlp alors selon la page de manuel:

Si l'en-tête d'un fichier n'est pas reconnu (la tentative d'execve (2) a échoué avec l'erreur ENOEXEC), ces fonctions exécuteront le shell (/ bin / sh) avec le chemin du fichier comme premier argument. (Si cette tentative échoue, aucune recherche supplémentaire n'est effectuée.)

Cependant ce n'est pas une fonctionnalité de «linux» mais de la bibliothèque gnu c - et encore une fois selon la page de manuel, il utilise explicitement / bin / sh et non un shell définissable par l'utilisateur. (Codé en dur dans la source de posix/execvpe.c ( new_argv[0] = (char *) _PATH_BSHELL; ) et sysdeps / unix / sysv / linux / paths.h (ou sysdeps / generic / paths.h - je ne sais pas quel en-tête est utilisé ...) ( #define _PATH_BSHELL "/bin/sh" ))

Cependant ce n'est pas une fonctionnalité de «linux» mais de la bibliothèque gnu c - et encore une fois selon la page de manuel, il utilise explicitement / bin / sh et non un shell définissable par l'utilisateur. (Codé en dur dans la source de posix/execvpe.c ( new_argv[0] = (char *) _PATH_BSHELL; ) et sysdeps / unix / sysv / linux / paths.h (ou sysdeps / generic / paths.h - je ne sais pas quel en-tête est utilisé ...) ( #define _PATH_BSHELL "/bin/sh" ))

Puisqu'il vient de sysdeps/**/linux , c'est une chose Linux. C'est peut-être aussi la chose générique, mais elle est remplaçable - évidemment pas localement mais par OS.

Oui, je comprends le comportement _current_ @ vexx32 . Mais nous ne parlons pas (du moins je ne le suis pas) du comportement _current_ de --% - nous parlons de l'améliorer, non? Pourquoi ai-je le sentiment que nous nous parlons tous ici? :)

Donc, le pas actuel pour ajuster --% est que si c'est là que le nom de la commande serait typiquement, tout ce qui se trouve à sa droite est analysé _as is_ (jusqu'à une nouvelle ligne) et envoyé directement à bash / cmd. Il n'y a pas de place pour la syntaxe PowerShell comme les chaînes here-strings et autres, car il a alors tous les mêmes problèmes que --% et le processeur de commande natif ont actuellement.

Oh seigneur ... Est-ce que tout le monde essaie juste de me troller? 😄 Non, Patrick - si vous voulez utiliser des expressions PowerShell, utilisez des chaînes ici avec --% . Si vous voulez multiligne, utilisez des chaînes ici. Si vous voulez des lignes multiples sans substitution de variables, vous pouvez utiliser des chaînes ici à guillemets simples.

De toute façon, je n'ai pas de skin dans ce jeu et je pense en avoir assez dit. Je pense toujours qu'il y a quelque chose de grave dans l'utilisation d'une applet de commande pour se reporter à un autre shell à partir d'un shell. C'est juste maladroit. C'est particulièrement maladroit lorsque vous utilisez une applet de commande pour exécuter un autre shell afin d'exécuter une commande native qui n'a pas besoin de ce shell secondaire en premier lieu. Le tout sent juste mauvais.

modifier: dernier mot

Le titre de cet élément est "appeler un opérateur natif". Je vois une opportunité gâchée lorsque nous avons déjà un opérateur d'appel & et que nous avons déjà un jeton qui a été conçu pour gérer le passage d'arguments aux commandes natives, --% . À l'heure actuelle, l'utilisation de & avec --% n'est PAS reconnue et, en tant que telle, n'introduira PAS de changement de rupture si nous décidons d'activer ce scénario et de lui donner un comportement spécifique pour résoudre les problèmes discutés ici.

si vous souhaitez utiliser des expressions PowerShell, utilisez des chaînes ici avec --% . Si vous voulez multiligne, utilisez des chaînes ici.

L'intérêt de l'opérateur est qu'il n'analyse littéralement aucune syntaxe PowerShell. Les chaînes ici n'existent pas. Il n'existe pas de constantes de chaîne entre guillemets simples. C'est "envoyer le texte à droite tel quel" pas "évaluer l'expression à droite, convertir en chaîne et envoyer sa valeur"

Par exemple, si vous aviez ceci:

--% @'
echo my native command
'@

cela se traduirait par:

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

Sauf que vous obtiendrez une erreur d'analyseur car '@ ne serait que le début d'une seule constante de chaîne entre guillemets contenant @ .

Je pense que vous imaginez peut-être l'implémentation différemment de sorte que c'est plus ou moins la même chose que le pitch pour ins , mais ce qui précède est le pitch actuel en cours de discussion pour --% .

Il convient également de noter que c'est ainsi que fonctionne la fonctionnalité existante. Cet exemple fonctionne essentiellement de la même manière:

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

Je répète:

À l'heure actuelle, l'utilisation de & with -% n'est PAS reconnue / indéfinie et en tant que telle, n'introduira PAS de changement de rupture si nous décidons d'activer ce scénario et de lui donner un comportement spécifique pour résoudre les problèmes discutés ici .

Permettez-moi de l'expliquer encore plus clairement: autorisez les chaînes ici à suivre --% lorsqu'elles sont utilisées avec &

Hah c'est un gros oof - ouais j'ai raté ça, désolé @oising.

À propos de cette idée, je pense qu'il serait un peu déroutant d'avoir --% signifie arrêter l'analyse dans certains contextes et "faire un meilleur travail à la liaison d'argument native" dans d'autres (de même que le faire fonctionner comme Invoke-NativeShell si c'est ce que vous voulez dire à la place).

Merci d'avoir creusé plus profondément sur exec , @TSlivede. Permettez-moi d'essayer de résumer et donc, espérons-le, de clôturer cette tangente; s'applique à la fois à Linux et macOS:

Il n'y a pas de fonction système (ou de bibliothèque) appelée exec sous Unix, il n'y a qu'une _family_ de fonctions liées avec exec dans leur nom (la plupart d'entre elles comme préfixe).

  • Les fonctions _system_ ( man section 2 ), execve en son cœur, _fail_ lorsque vous essayez d'exécuter un script sans shebang.

  • Parmi les fonctions _library_ qui _ se construisent sur les fonctions système_ ( man section 3 - man 3 exec ), seules celles qui ont p (pour "chemin", je présume) ont le secours de passage au shell du système (par exemple, execlp ).

    • Les implémentations de la bibliothèque GNU de ces fonctions en code dur /bin/sh , comme @TSlivede l' a montré, alors que celles basées sur BSD utilisent juste sh , et reposent sur sh pour être dans $env:PATH (cependant, curieusement, la page man indique /bin/sh ).

Comme indiqué, pour _l'exécution directe_, les principaux shells de type POSIX ( bash , dash , ksh et zsh ) reviennent à l'exécution de scripts sans shebang _themselves_, ce qui implique qu'ils n'utilisent _pas_ les variantes de secours parmi les fonctions de bibliothèque exec ; en revanche, le choix de perl pour sa propre fonction exec était de s'appuyer sur le repli; de même, les exec _builtins_ des principaux shells de type POSIX reposent sur le repli, sauf dans bash .

Puisque sous Unix l'utilisation d'un fichier de script dans les coulisses n'est pas nécessaire, nous pouvons faire les mêmes hypothèses sur le chemin du shell système que les fonctions de bibliothèque de secours, et je recommande /bin/sh sur sh , pour sécurité et prévisibilité:

Malgré la spécification POSIX _pas_ imposant l'emplacement de sh (c'est-à-dire qu'à proprement parler, les fonctions de la bibliothèque BSD sont les plus conformes):

  • /bin/sh est sûr à supposer, car c'est l'emplacement standard _de facto_, notamment parce que l'écriture d'un shell Unix portable _nécessite_ se référant à sh par son chemin complet, étant donné que les lignes shebang ne prennent en charge que _full, literal_ chemins ( #!/bin/sh ).

  • Inversement, se fier à la localisation de sh via $env:PATH peut être un risque pour la sécurité, étant donné que $env:PATH pourrait être manipulé pour invoquer un sh .

Au fait, le même risque s'applique à la localisation de cmd.exe via $env:ComSpec , alors peut-être que la meilleure façon est d'appeler la fonction GetSystemDirectory WinAPI et d'ajouter \cmd.exe à son résultat (nous avons besoin d'une solution de secours de toute façon, si $env:ComSpec n'est pas défini).

/bin/sh est sûr à supposer, car c'est l'emplacement standard _de facto_, notamment parce que l'écriture d'un shell Unix portable _nécessite_ faisant référence à sh par son chemin complet, étant donné que les lignes shebang ne prennent en charge que _full, literal_ chemins ( #!/bin/sh ).

Normalement, vous dites #!/usr/bin/env sh (sauf dans les scripts système).

  • Normalement, vous _non_: googler "#! / Bin / sh" donne environ 3 900 000 correspondances, "#! / Usr / bin / env sh" donne environ 34 100 correspondances.

  • Normalement, vous ne devriez pas: vous voulez cibler de manière prévisible _le_ shell système, /bin/sh , pas n'importe quel utilitaire sh qui arrive en premier dans $env:PATH .

La seule raison de cibler un exécutable nommé sh est de cibler de manière portative le shell _system_system_support-commun-dénominateur le plus bas, ie /bin/sh .

Normalement, vous _non_: googler "#! / Bin / sh" donne environ 3 900 000 correspondances, "#! / Usr / bin / env sh" donne environ 34 100 correspondances.

Il n'y a aucun moyen de limiter une recherche Web aux scripts utilisateur, d'autant plus que de nombreux scripts utilisateur ne sont pas du tout marqués comme exécutables, et même s'ils le sont, ils peuvent s'appuyer sur execlp ; mais même si la plupart des scripts utilisateur disaient cela, nous ne devrions pas prendre _customary_ pour _normal_. Les scripts à exécuter par PowerShell sont des scripts utilisateur; quand les utilisateurs veulent un shell, ils appellent sh , pas /bin/sh , sauf s'ils sont paranoïaques.

@oising

Passer la ligne de commande du shell natif _ comme une seule chaîne_ (quelle que soit la forme de chaîne (-literal) est la plus simple), et _seulement_ comme une seule chaîne (sauf pour les arguments supplémentaires de passage à la ligne de commande pris en charge sous Unix ( seulement) discuté ci - sonne comme un terrain d'entente.

Passer la ligne de commande sous forme de _single string_ est la condition préalable pour:

  • être capable d'incorporer un tel appel dans un pipeline _PowerShell_.
  • être capable d'incorporer des variables _PowerShell et une expression_ dans la ligne de commande du shell natif.

Passer une seule chaîne est une _ expression conceptuelle directe_ de ce que l'appel va faire; en revanche, essayer d'incorporer d'une manière ou d'une autre directement la syntaxe d'un shell différent directement dans PowerShell avec des arguments _individual_ (éventuellement des mots nus) apporte des problèmes insolubles qui entraînent un compromis déroutant avec des limitations fondamentales, que le --% existant incarne déjà.


Bien que vous puissiez affirmer qu'il n'est pas si important que nous choisissions un _opérateur_ ou un _cmdlet_ pour passer cette ligne de commande à une seule chaîne (sauf pour les questions de _découverabilité_), j'ai des préoccupations conceptuelles concernant l'utilisation des deux & et --% (précédé de & ):

  • Le souci re & : si nous devons passer une chaîne - entre guillemets - contenant la ligne de commande, nous fonctionnons effectivement en mode _expression_, alors que le & implique actuellement le mode _argument_ (ce qui signifie: Arguments _individuels_, ne citant que si nécessaire). Par conséquent, l'implication de & prête à confusion.

  • Le problème concernant --% concerne l'origine uniquement Windows et la sémantique confuse de --% comme symbole d'arrêt de l'analyse; ce serait particulièrement déroutant si --% tant que symbole d'arrêt de l'analyse fonctionnait en mode _argument_ (par exemple
    & /bin/ls --% dir1 dir2 ), alors que & --% fonctionnait en mode _expression_ (par exemple & --% '/bin/ls dir1 dir2 >out.txt' )


Permettez-moi de prendre du recul et d'examiner --% , le symbole d'arrêt de l'analyse, tel qu'il est actuellement implémenté:

C'était une tentative de résoudre deux problèmes finalement sans rapport (Windows uniquement à l'époque):

  • (a) Un cas d'utilisation qui s'applique également à d'autres plates-formes: permettez-moi de réutiliser les _ lignes de commande_ écrites pour cmd.exe / le shell natif tel quel, donc je n'ai pas à les adapter à la syntaxe de PowerShell.

  • (b) Un problème toujours Windows uniquement: permettez-moi d'appeler un _rogue CLI_ (c'est-à-dire un appel à une application de console externe _single_) qui nécessite un style très spécifique de citation que la re-citation générique en arrière-plan de PowerShell ne peut pas fournir , en me permettant de créer la ligne de commande finalement transmise à WinAPI pour ces CLI _verbatim_. (Pour limiter l'énoncé du problème à cela, on suppose que la nouvelle citation de PowerShell fonctionne généralement correctement, ce qui n'est jamais le cas - c'est le sujet de # 1995).

--% été implémenté comme une _conflation_ confuse de tentatives de solution à la fois pour (a) et (b), et a fini par résoudre aucun des problèmes correctement:

  • Il a été annoncé comme la solution à (a), mais en réalité a de sérieuses limitations, car _la seule fonction cmd exe qu'il émule est l'expansion de %...% -style environment-variable references_:

    • Il ne prend en charge qu'une commande _single_ cmd , étant donné qu'un | (et
      && et || , même s'ils n'étaient pas implémentés à l'époque) terminent implicitement la partie pass-to- cmd .

    • Un _single_ & - qui est cmd s séparateur multi-commandes - _est_ passé, mais cela n'a pas non plus abouti à un support multi-commandes, car - puisque cmd est t réellement impliqué - le & est passé _verbatim au programme cible_; par exemple
      echoArgs.exe --% a & b ne fait pas écho a puis invoque b , il transmet des arguments textuels a , & et b à echoArgs .

    • cmd caractère d'échappement de ^ ) dans les arguments sans guillemets n'est pas respecté.

    • En corollaire, les jetons %...% qui font référence à des variables d'environnement existantes sont _invariablement_ développés; une tentative d'empêcher que via ^ ne fonctionne pas correctement (par exemple
      echoArgs.exe Don't expand %^USERNAME% , qui dans cmd empêche l'expansion _et supprime le ^ _, conserve le ^ lorsqu'il est appelé depuis PowerShell comme echoArgs.exe --% Don't expand %^USERNAME%

  • En tant que solution à (b), il a également échoué:

    • Tout comme dans l'utilisation (a), il n'est pas possible d'incorporer des variables et des expressions _PowerShell_ dans la commande - sauf si vous définissez d'abord maladroitement un _aux. environnement_ variable_ que vous référencez ensuite via %...% après --% ; par exemple
      $env:FOO='foo'; echoArgs.exe --% %FOO%!

La bonne solution pour (a) aurait été Invoke-NativeShell / ins , comme discuté ici.

La bonne solution pour (b) aurait été de:

  • supporte --% aussi spécial que l'argument _first_
  • et, tout comme avec ins , exigent qu'il soit suivi _ par une seule chaîne_ qui constitue la ligne de commande d'intercommunication _ dans son ensemble_, à nouveau avec la possibilité d'incorporer des variables et des expressions _PowerShell_ par interpolation de chaîne (ou autres façons de construire la chaîne)
  • Par exemple, msiexec --% "/i installer.msi INSTALLLOCATION=`"$loc`"" , avec $loc contenant 'c:\foo\bar none' , entraînerait le passage de la ligne de commande verbatim /i installer.msi INSTALLLOCATION="c:\foo\bar none" , satisfaisant l'exigence non standard de msiexec que dans les arguments <prop>=<value> , seule la partie <value> soit entre guillemets.

Bien que ce ne soit pas pratique, c'est le prix à payer pour une solution robuste à l'anarchie qui est un argument basé sur la ligne de commande passant sur Windows.

@ mklement0 Merci pour la réponse patiente et attentionnée (comme d'habitude.)

Donc, je suis entièrement d'accord avec;

La bonne solution pour (b) aurait été de:

  • support -% aussi spécial que le premier argument
  • et, tout comme avec ins, exigent qu'il soit suivi d'une seule chaîne qui constitue la ligne de commande directe dans son ensemble,> - encore une fois avec la possibilité d'incorporer des variables et des expressions PowerShell au moyen d'une interpolation de chaîne (ou d'autres moyens de construction de la chaîne)

    • par exemple msiexec -% "/ i installer.msi INSTALLLOCATION = "$loc " ", avec $ loc contenant 'c: foobar none', aboutirait à une ligne de commande verbatim / i installer.msi INSTALLLOCATION =" c: foobar aucun "ne passe, satisfaisant l'exigence non standard de msiexec qui=arguments seulementune partie doit être citée deux fois.

C'est ce que j'ai essayé d'exposer comme une solution généralisée à la fois (a) et (b), sauf que je ne comprends toujours pas vraiment que (a) devrait exister en tant que problème indépendant à résoudre? Ce avec quoi je me débat vraiment, c'est si (b) est implémenté _spécifiquement_ pour & , pourquoi ne peut-il pas couvrir (a) aussi? Par exemple, qu'est-ce que ins ... donne que & cmd --% ... ne peut pas? Et ce ne sera pas un changement de rupture autre que de ne pas permettre à & de passer --% comme argument (ce qui semble ridiculement improbable.) Bonus que vous n'avez pas à vous soucier de comspec, shells ou peu importe. Laissez l'appelant décider.

@ PowerShell / PowerShell-Committee a examiné ceci:

  • Ce sera une fonctionnalité expérimentale pour obtenir de vrais retours sur l'utilisation; nous sommes toujours ouverts au sceau actuel; nous reportons /bin/sh vs /bin/env sh à l'examen PR
  • Nous avancerons avec --% comme nouvel opérateur spécifiquement pour le scénario "stackoverflow" où l'utilisateur coupe et colle une ligne de commande dans PowerShell et cela devrait simplement fonctionner (avec, bien sûr, le nouvel opérateur qui le précède )
  • Une cmdlet Invoke-NativeShell est distincte de cette proposition et pourrait être publiée par la communauté dans PowerShellGallery; De plus, exiger une chaîne ici ne résout pas le problème de l'expérience utilisateur sans savoir à l'avance pour l'envelopper comme une chaîne ici
  • Nous ne voulons pas apporter un changement radical au commutateur --% où les utilisateurs existants peuvent dépendre de ce comportement
  • Les utilisateurs qui ont besoin d'un comportement de streaming comme le commutateur --% doivent continuer à utiliser ce commutateur et échapper les caractères spéciaux si nécessaire
  • Il y a toujours un problème de découverte pour les utilisateurs à savoir sur --% (ou toute solution)
  • Nous ne sommes actuellement pas en train d'envisager une modification de PSReadLine pour modifier le contenu collé en tant que chaîne ici essayant de prédire l'intention de l'utilisateur
  • Nous ne cherchons pas plusieurs solutions qui permettent des scénarios ici-chaîne vs non-ici chaîne (expansion); par exemple & --% vs --%

Je veux également ajouter que, malgré @ SteveL-MSFT et moi fermant # 1995 en même temps que l'ouverture de celui-ci, ce n'était vraiment pas notre intention de communiquer cela comme une solution définitive à ce problème.

À mon avis, les avantages de cette fonctionnalité particulière sont bien plus importants que l’impact # 1995. Je pense qu'il existe BEAUCOUP d'exemples StackOverflow et d'exemples de documents pour les outils non-PS que les gens aimeraient couper / coller (moi y compris).

Il y a aussi un désir de fournir une évasion appropriée dans le propre langage de PowerShell, mais je ne vois pas de gens frapper # 1995 dans la nature en masse (mais je vais élaborer plus là-dessus).

  • Nous avancerons avec --% tant que nouvel opérateur

Désolé de faire glisser ceci, et je ne cherche pas de discussion supplémentaire, mais quel type d'opérateur? (Je suis principalement intéressé par la syntaxe.)

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

Qui obtient la pipe? Que deviennent les $ ? Similaire avec & , && et || .

Dans quelles conditions, le cas échéant, --% sera-t-il utilisable dans un tube PS à l'avenir?

Merci.

Désolé de faire glisser ceci, et je ne cherche pas de discussion supplémentaire, mais quel type d'opérateur? (Je suis principalement intéressé par la syntaxe.)

AST sage, ce ne sera probablement pas techniquement un opérateur, mais c'est son propre type AST. S'il est implémenté en tant qu'opérateur, ce serait un opérateur unaire.

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

Qui obtient la pipe? Que deviennent les $ ? Similaire avec & , && et || .

La chaîne find . -iname *$pdf -print0 | xargs -0 ls -ltr serait envoyée telle quelle au shell natif. Cependant, $a serait toujours rempli, donc vous pourriez dans la ligne suivante diriger cela vers quelque chose.

Dans quelles conditions, le cas échéant, --% sera-t-il utilisable dans un tube PS à l'avenir?

Si ~ dans le premier emplacement d'élément de commande ~ c'est au début d'une instruction, probablement pas. C'est l'un des atouts de la nouvelle syntaxe. Sinon ~ dans le premier emplacement ~ au début, il continuera à fonctionner comme aujourd'hui.

(@ daxian-dbw @ SteveL-MSFT si tout cela est faux, veuillez corriger ❤️)

@SeeminglyScience a tout à fait raison

Si dans le premier emplacement d'élément de commande, probablement pas. C'est l'un des atouts de la nouvelle syntaxe. Si ce n'est pas dans le premier emplacement, cela continuera à fonctionner comme aujourd'hui.

Il peut être difficile de transmettre la sortie de la commande amont à la commande native qui sera appelée par --% . --% transmet simplement la chaîne telle quelle à un shell natif, comme bash , mais la sortie de la commande en amont ne peut pas être simplement envoyée à bash , mais doit être transmise au commande native elle-même, telle que find .

Je pense que le comportement de streaming n'est pas une considération pour cette fonctionnalité car c'est principalement pour le scénario "StackOverflow". Donc, je pense que nous ferions mieux de simplement faire --% une instruction spéciale ast, afin qu'il soit clair que cela ne fonctionnera pas avec les pipelines _PowerShell_.

Ahh je savais qu'il y avait une raison pour laquelle "l'emplacement d'élément de commande" était faux. Je voulais dire au début d'une déclaration. Je vais le réparer, merci!

la sortie de la commande en amont ne peut pas être simplement envoyée à bash

_Upstream_ signifie _us_? Je pense que je peux faire: 'ls' | bash . Ce n'est pas une chose particulièrement intelligente à faire mais possible.

Discutait avec #! comme sceau. Dans ce cas, il vous suffit de taper:

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

(où le deuxième cas suppose que sh est dans le chemin). Dans ce cas, nous devrons décider si la spécification du shell est requise ou si nous l'exécutons toujours sous le shell par défaut, donc dans cet exemple, sh est démarré deux fois. Cependant, cela pose un problème en raison du fonctionnement actuel des fichiers shebang. Ils fonctionnent dans PowerShell car le shebang est ignoré en tant que commentaire. Si nous décidons d'ignorer la première ligne d'un script s'il s'agit d'un commentaire, alors nous avons toujours un problème dans le shell interactif où chaque nouvel ensemble de lignes est un nouveau script et il n'y a aucun moyen aujourd'hui de déterminer si ce script est exécuté en tant que script ou interactivement.

Une alternative pourrait être d'emprunter la syntaxe de démarquage:

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

Dans cet exemple, nous utiliserions la syntaxe de clôture de code de démarque. Cela résoudrait également le problème multi-lignes et utiliserait un concept qui n'est pas entièrement nouveau. Étant donné que nous avons encore un problème de découverte initial pour les nouveaux utilisateurs, je ne pense pas que ce soit bien pire dans la mesure où vous devez avoir les triples backticks de fin.

Dans ce cas, cela pourrait potentiellement fonctionner:

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

@ La syntaxe de Markdown de SteveL-MSFT a beaucoup de sens. Si nous sommes en mesure de préciser. d'autres types comme python ou peut-être par extension?

Le shebang #! Interférerait avec les commentaires existants.

Cela me semble extrêmement compliqué, à la fois à gérer lors de l'analyse et pour les utilisateurs à utiliser réellement.

Et oui, nous n'avons pas besoin de confondre les gens avec des lignes qui _ devraient_ être des commentaires ne se révélant pas comme des commentaires. #Requires est déjà un peu bizarre, un full on shebang va au mieux dérouter les gens de Windows, et probablement aussi les gens de Linux.

Personnellement, je préférerais ne pas mélanger cette fonctionnalité avec shebang, c'est déroutant à mon avis.
De plus, si nous commençons à utiliser la syntaxe de bloc de code markdown, ce n'est qu'une question de temps pour que les gens demandent l'exécution de code de langage arbitraire directement dans PowerShell. Je ne pense pas que nous voulions aller dans cette direction ...

De plus, si nous commençons à utiliser la syntaxe de bloc de code markdown, ce n'est qu'une question de temps pour que les gens demandent l'exécution de code de langage arbitraire directement dans PowerShell.

Je ne cesserais littéralement de demander du C # en ligne même si je sais que c'est une mauvaise idée.

La syntaxe de démarque n'introduit pas de code de langage arbitraire car le script incorporé s'exécutera dans un processus enfant. Vous pouvez placer du code de langue arbitraire dans @' maintenant et rien de mal ne se passe.

Dans la discussion d'équipe et aussi avec Jeffrey, nous ne devrions pas être contre les personnes souhaitant exécuter d'autres langages dans un script PowerShell. Comme @ yecril71pl l'a noté, nous ne prenons pas directement en charge d'autres langages, mais nous nous

Nous n'avons pas besoin de mélanger les propositions sur une seule ligne et sur plusieurs lignes dans la mesure où nous pourrions prendre en charge les deux, même si l'inconvénient est que nous avons maintenant deux façons de faire des choses très similaires, mais avec une syntaxe différente.

Je préférerais abandonner les trucs sur une seule ligne. Les îlots de code extraterrestres devraient être bien en vue, sinon personne ne comprendra ce qui se passe.

@ yecril71pl Je me penche vers cela moi-même principalement parce que du point de vue de la découverte, la syntaxe de démarquage serait plus facilement reconnaissable, mais c'est peut-être parce que j'écris souvent des démarques. Je m'attends également à ce que de nombreux exemples de scripts soient probablement sur plusieurs lignes plutôt que sur une seule ligne, car ils s'attendent à ce qu'un état soit conservé où chaque ligne serait un processus indépendant.

Dans la discussion d'équipe et aussi avec Jeffrey, nous ne devrions pas être contre les personnes souhaitant exécuter d'autres langages dans un script PowerShell.

Absolument à 100%, mais cela ne veut pas dire qu'il doit être intégré directement dans la langue.

Comme @ yecril71pl l'a noté, nous ne prenons pas directement en charge d'autres langages, mais nous nous

Cela peut déjà être fait avec des chaînes ici, non? Pouvez-vous expliquer un peu les avantages que cela procure par rapport aux méthodes existantes?

Aussi pourquoi s'arrêter à python et bash? Pourquoi pas C #, CIL, C ++?

Rien n'oblige les gens à faire cela. Ils peuvent décider de porter cet autre script vers PowerShell s'ils le souhaitent. Cela fournit simplement une option aux utilisateurs pour pouvoir couper et coller dans PowerShell pour faire avancer les choses.

Je ne suis pas contre cela parce que je pense que je vais être obligé de l'utiliser. Je n'aime pas ça parce que ça va être difficile à maintenir du point de vue de la langue, un cauchemar pour les éditeurs, et finalement encourager les scripts qui sont illisibles à moins que vous ne connaissiez plusieurs langues.


Vraiment, c'est un concept assez différent du sujet original de ce fil avec des implications beaucoup plus larges. Je recommanderais de créer un nouveau problème pour cela.

Cela peut déjà être fait avec des chaînes ici, non? Pouvez-vous expliquer un peu les avantages que cela procure par rapport aux méthodes existantes?

Un éditeur de code universel pourra les détecter et les décorer de manière appropriée, alors qu'il n'y a rien à faire avec @' car l'éditeur n'a aucune idée de ce qu'il y a à l'intérieur.

Aussi pourquoi s'arrêter à python et bash? Pourquoi pas C #, CIL, C ++?

Parce qu'ils ne sont pas des langages de script?

Un éditeur de code universel pourra les détecter et les décorer de manière appropriée, alors qu'il n'y a rien à faire avec @' car l'éditeur n'a aucune idée de ce qu'il y a à l'intérieur.

Un éditeur peut dire que bash -c @' contient bash aussi facilement qu'une clôture de code. De plus, le défi n'est pas de déterminer la langue d'une section de code, mais de jongler avec les serveurs de langage et les surligneurs de syntaxe. La seule façon dont cela fonctionnerait sans un investissement important en temps et en efforts serait de le rendre essentiellement comme s'il s'agissait d'une chaîne ici.

Parce qu'ils ne sont pas des langages de script?

C # et C ++ ont tous deux des sous-ensembles orientés script. Même s'ils ne l'ont pas fait, le «langage de script» est subjectif sans véritable définition. Cela n'est pas utile en tant que limite définitive de ce qui serait et ne serait pas considéré comme inclus.

C # et C ++ ont tous deux des sous-ensembles orientés script. Même s'ils ne l'ont pas fait, le «langage de script» est subjectif sans véritable définition.

Un langage est un langage de script (autonome) lorsque vous appelez normalement interpet options… script arguments… et que cet appel ne laisse rien sauf ce qu'il était censé laisser, à l'exclusion des éléments temporaires et privés pour l'interpréteur. Cela signifie également qu'il nécessite normalement une copie de l'interpréteur pour s'exécuter. Il existe des langages de script intégrés que vous ne pouvez pas exécuter de cette manière.

@SeeminglyScience

Cela peut déjà être fait avec des chaînes ici, non? Pouvez-vous expliquer un peu les avantages que cela procure par rapport aux méthodes existantes?

Les gens peuvent utiliser des chaînes ici aujourd'hui ou échapper à tous les arguments passés aux commandes natives s'ils peuvent trouver comment faire les choses correctement. L'intention ici est de simplifier la tâche pour les nouveaux utilisateurs pour les scénarios de copier-coller (Stackoverflow).

Aussi pourquoi s'arrêter à python et bash? Pourquoi pas C #, CIL, C ++?

Les scénarios pris en charge ici sont un bloc de texte passé à une commande native. N'importe quelle langue peut être prise en charge si elle prend en charge cette convention d'appel. Il n'est pas proposé de créer un fichier source temporaire et de le faire compiler.

Je ne suis pas contre cela parce que je pense que je vais être obligé de l'utiliser. Je n'aime pas ça parce que ça va être difficile à maintenir du point de vue de la langue, un cauchemar pour les éditeurs, et finalement encourager les scripts qui sont illisibles à moins que vous ne connaissiez plusieurs langues.

Il n'y a aucune attente que vous obteniez une coloration syntaxique mixte. Tout script imbriqué d'une autre langue sera simplement monotone.

Vraiment, c'est un concept assez différent du sujet original de ce fil avec des implications beaucoup plus larges. Je recommanderais de créer un nouveau problème pour cela.

L'implémentation proposée est différente du problème d'origine, mais le problème est le même qui est le scénario de copier-coller et de "juste travailler".

Les gens peuvent utiliser des chaînes ici aujourd'hui ou échapper à tous les arguments passés aux commandes natives s'ils peuvent trouver comment faire les choses correctement. L'intention ici est de simplifier la tâche pour les nouveaux utilisateurs pour les scénarios de copier-coller (Stackoverflow).

Mais comment est-ce vraiment plus facile? Je comprends que c'est subjectif mais cela ne me semble pas si différent:

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

~~~

vs

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

Ils semblent presque les mêmes, sauf que ce dernier est nettement plus clair dans ce qui va se passer.

Les scénarios pris en charge ici sont un bloc de texte passé à une commande native. N'importe quelle langue peut être prise en charge si elle prend en charge cette convention d'appel. Il n'est pas proposé de créer un fichier source temporaire et de le faire compiler.

Eh bien, C # n'aurait pas besoin d'un fichier source temporaire. Peut-être pas non plus CIL (aucune idée de C ++). Dans tous les cas, bien qu'une clôture de code soit strictement du sucre syntaxique pour appeler un exécutable (par exemple bash / python / cmd) me déroute. Les clôtures de code impliquent une prise en charge du langage imo.

Il n'y a aucune attente que vous obteniez une coloration syntaxique mixte. Tout script imbriqué d'une autre langue sera simplement monotone.

Ce sera toujours un cauchemar pour les analyseurs basés sur tmLanguage. Ils peuvent à peine gérer la syntaxe qu'ils attendent de atm. Plus que cela, si c'est le cas, je ne comprends pas pourquoi c'est différent d'une chaîne ici.

L'implémentation proposée est différente du problème d'origine, mais le problème est le même qui est le scénario de copier-coller et de "juste travailler".

Correct mais nous avons déjà 145 commentaires sur les solutions proposées précédemment. D'autant plus que ce fil est déjà arrivé à une conclusion, vous verrez probablement beaucoup plus d'implication dans un nouveau fil dédié.

Eh bien, C # n'aurait pas besoin d'un fichier source temporaire. Peut-être pas non plus CIL (aucune idée de C ++). Dans tous les cas, bien qu'une clôture de code soit strictement du sucre syntaxique pour appeler un exécutable (par exemple bash / python / cmd) me déroute. Les clôtures de code impliquent une prise en charge du langage imo.

Je veux juste faire écho au commentaire de exécuter un script C # sans impliquer la compilation avec la bibliothèque Microsoft.CodeAnalysis.CSharp.Scripting . C'est ce qu'utilise dotnet-interactive dans le noyau C # Jupyter. Il n'est donc pas déraisonnable pour les utilisateurs de demander un script C # ou même un support de script F # avec une telle syntaxe.

Vous pouvez dire que la syntaxe de séparation de code dans PowerShell sert à appeler une commande native, ce qui permet théoriquement ceci:

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

mais alors c'est différent de la compréhension établie par les utilisateurs de celui-ci du markdown, qui, comme @SeeminglyScience l'a appelé, implique la prise en charge du langage. Utiliser une syntaxe similaire avec une sémantique différente est à mon avis déroutant.

ce qui permet théoriquement cela

Vous mettez le script à l'intérieur, pas les arguments, et le script est équivalent au flux d'entrée. De plus, si cela est destiné à fournir une prise en charge SO, les chemins complets ne devraient pas être autorisés.

Vous mettez le script à l'intérieur, pas les arguments, et le script est équivalent au flux d'entrée

Le script est l'argument de bash / cmd / python.

De plus, si cela est destiné à fournir une prise en charge SO, les chemins complets ne devraient pas être autorisés.

Puis changez-le pour cet exemple:

~ PowerShell$ a = ipconfig /all ~

Ou ajoutez d'abord l'exe arbitraire au chemin.

Vous mettez le script à l'intérieur, pas les arguments, et le script est équivalent au flux d'entrée

Le script est l'argument de bash / cmd / python.

Aucun d'eux ne permet de passer un script comme argument de position. CMD n'autorise même pas un fichier batch.

De plus, si cela est destiné à fournir une prise en charge SO, les chemins complets ne devraient pas être autorisés.

Puis changez-le pour cet exemple:

$ a = `` `` ipconfig
/tout

Cela ne marcherait pas.

On dirait que nous allons créer énormément de cas spéciaux sur une base très arbitraire pour que cette idée de démarque fonctionne.

En outre, la principale raison pour laquelle cela est même pris en charge dans Markdown est la coloration syntaxique. Ce sera une demande constante si nous ajoutons quelque chose comme ça dans la langue.

Ce n’est pas une solution efficace.

Discutait avec #! comme sceau

juste pour ajouter mes 2 centimes, je pense que c'est une erreur de relancer cette discussion après l'annonce d'une solution. et personnellement je n'aime pas à la fois la syntaxe shebang et markdown (et les deux sont des changements majeurs).

Cela ne me dérange pas de reprendre les discussions si quelqu'un a trouvé quelque chose de manifestement meilleur. En effet, il vaut mieux le faire maintenant qu'après la livraison de la fonctionnalité. Je ne pense tout simplement pas que #! ou l'approche de démarque est une meilleure solution pour fournir une chaîne de ligne de commande "non-PowerShell-adultérée" à un exe natif. Peut-être ai-je juste besoin de l'essayer pour m'y habituer, mais ma première réaction est ... euh, eew.

_Si nous (et PowerShell) savons que nous exécutons un exécutable natif, pourquoi avons-nous besoin de "appeler un opérateur natif"? _

Si PowerShell analyse une chaîne d'entrée et obtient CommandAst, PowerShell effectue une découverte de la commande.
Si la commande est une commande native, PowerShell convertit ast en NativeCommandProcessor.
Nous pouvons implémenter new (expérimental NativeCommandProcessor) pour le cas qui analysera à nouveau l'étendue Ast (c'est-à-dire la chaîne d'entrée) afin d'obtenir le nom / chemin de l'exécutable natif et le reste de la chaîne comme argument ou arguments par les règles du système d'exploitation.

Si un utilisateur veut copier-coller depuis le shell, il doit taper "cmd"ou" bash"- cela fonctionnera sans modification.
Si un utilisateur souhaite copier-coller une ligne de commande nativefonctionnera aussi comme g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Si un utilisateur souhaite copier-coller depuis le shell

Nous avons Get-/Set-Clipboard pour cela.

pour le cas qui analysera à nouveau l'étendue Ast (c'est-à-dire la chaîne d'entrée) afin d'obtenir le nom / chemin de l'exécutable natif et le reste de la chaîne comme argument ou arguments par les règles du système d'exploitation.

Ma pensée ici est que faire cela vous soit:

  • Analyser de manière inefficace la commande AST, uniquement pour analyser l'étendue plus tard sous forme de chaîne et jeter l'AST d'origine, OU
  • Vous prenez l'AST et essayez de le reformer en une chaîne (comme le fait le NativeCommandProcessor actuel), mais ce faisant, les choses seront perdues

Nous devons donc à la place conserver la chaîne depuis le début. Maintenant, je pense que PowerShell a déjà trois types de jetons de chaîne, mais la principale demande de cette discussion ne semble pas vouloir échapper aux choses. Toute chaîne doit pouvoir échapper à son terminateur, mais la solution discutée ici semble utiliser une nouvelle ligne comme terminateur et ne pas pouvoir échapper aux nouvelles lignes (il y a toujours des points-virgules).

Je ne suis pas totalement attaché à un terminateur de nouvelle ligne uniquement, mais cela semble être la seule solution pour ne pas vouloir échapper à quoi que ce soit. Fondamentalement, cela ferait un tokenise "opérateur d'appel natif" de la même manière qu'un commentaire de ligne est aujourd'hui - de l'opérateur à la fin de la ligne est la chaîne passée au "shell natif".

Alors:

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

sera symbolisé comme:

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

(Notez que nous envoyons même l'espace directement après --% au shell natif, puisque la chaîne commence immédiatement après --% )

Ensuite, ce jeton est enveloppé dans un AST très simple, comme:

// 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; }
}

Ensuite, une fois que cela est compilé et envoyé au canal, cela utilise un nouveau processeur de commande natif pour envoyer la chaîne directement en tant qu'argument au shell natif.

Bien que tout cela semble réalisable, certaines questions dans mon esprit sont:

  • Le shell natif est-il configurable? Sinon, comment pouvons-nous justifier cela? Si tel est le cas, doit-il être explicite dans le cadre de la syntaxe (compliquant la syntaxe) ou implémenté en tant que variable de préférence (ce qui rend plus difficile à découvrir et à gérer)?
  • Une telle implémentation sera-t-elle découvrable et ne déroutera pas les gens? Les utilisateurs comprendront-ils à quel point PowerShell fait peu ici, ou ce qui se passe lorsqu'ils utilisent | ou & ou même des chaînes?
  • Combien de scénarios vont être desservis par une chaîne verbatim d'une seule ligne ici? Allons-nous mettre en œuvre cela juste pour découvrir que beaucoup de gens veulent des invocations multilignes? Le besoin d'éviter de faire quelque chose comme échapper à un guillemet simple est-il suffisant pour ajouter ces contraintes?
  • Cela semble être un scénario très spécifique pour une toute nouvelle syntaxe de langage. Si nous créons une nouvelle syntaxe de chaîne textuelle, pourquoi la couplons-nous directement au concept d'invocation native? Si nous créons un nouveau mécanisme d'invocation, pourquoi le couplons-nous directement au concept d'autres shells? Une bonne syntaxe et de bonnes constructions de langage composent, à la fois syntaxiquement et fonctionnellement, et essayez de ne pas trop cuire dans le langage lui-même (au lieu de cela, en fournissant une plate-forme d'éléments que l'utilisateur peut assembler en tant que programme) - notre syntaxe d'appel native proposée répond-elle à la barre pour une nouvelle syntaxe élémentaire à composer en programmes? Y a-t-il une meilleure approche alternative que nous pourrions adopter qui résout ce problème de manière simple, tout en nous permettant de garder les blocs de construction découplés afin qu'ils puissent être composés pour résoudre d'autres problèmes?
  • Est-ce que d'autres shells ou d'autres langages implémenteront ceci ou l'ont-ils déjà? Au niveau de la syntaxe, ou à tout autre niveau? Si c'est le cas, comment?

Ma pensée ici est que faire cela vous non plus

@rjmholt CommandAst a la propriété Extent avec une chaîne d'entrée. La ré-analyse est aussi simple que divisée par le premier espace sous Windows ou par des espaces sous Unix. J'espère donc que nous n'avons rien perdu dans la corde et que nous obtenons de bonnes performances.

Quelle que soit la manière dont cela est implémenté, je pense que nous avons besoin d'un ensemble de cas d'utilisation à tester. Permettez-moi de "proposer" ce qui suit:

  • Cas d'utilisation 1: Je veux qu'une seule invocation exe littérale (sans interpolation) fonctionne sans connaître les règles de citation / d'échappement, par exemple: g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 . La solution actuelle consiste à déterminer ce qu'il faut citer e..g: g++ timer.cpp '@conanbuildinfo.args' -o timer --std=c++11 Un exemple plus compliqué peut inclure des lignes de commande s'étendant sur plusieurs lignes, par exemple:
tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found 

Solution de contournement actuelle (swap backtick pour \ ) par exemple:

tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz `
    --exclude=/proc `
    --exclude=/lost+found
  • Cas d'utilisation 1a: Je veux qu'un seul appel exe avec une interpolation PS fonctionne, par exemple: g++ $CppFile @conanbuildinfo.args -o timer --std=c++11 . La solution actuelle consiste à déterminer ce qu'il faut citer (simple ou double) et ce qu'il faut échapper. E..g: g++ $CppFile '@conanbuildinfo.args' -o timer --std=c++11 Un exemple plus compliqué peut inclure des lignes de commande s'étendant sur plusieurs lignes, par exemple:
tar -cvpzf "/share/Recovery/Snapshots/${ComputerName}_`$(date +%Y%m%d).tar.gz" \
    --exclude=/proc \
    --exclude=/lost+found 

Solution de contournement actuelle, double-guillemet du premier argument, permutez le backtick pour \ et échappez au $ au début de `$ (date + ...).

  • Cas d'utilisation 2: je veux exécuter une commande en par exemple: ps -o pid,args -C bash | awk '/running_script/ { print $1 }' et wsl ls $foo && echo $PWD . _Je ne sais pas si cela doit être un cas d'utilisation distinct du précédent._ La solution actuelle consiste à citer la commande bash, par exemple: bash -c 'ps -o pid,args -C bash | awk ''/-bash/ { print $1 }''' . Ce cas d'utilisation peut également inclure des chaînes multilignes, par exemple:
ps -o pid,args -C bash | \
awk '/running_script/ { print $1 }'
  • Cas d'utilisation 2a: Je veux exécuter une commande en - est-ce un cas d'utilisation?

  • Cas d'utilisation 3: j'ai besoin de npm run $scriptName pour continuer à travailler, c'est-à-dire interpoler des variables . (cela peut être implicite et n'a pas besoin d'être indiqué).

Il y a une question de savoir s'il devrait y avoir des cas d'utilisation pour les chaînes interpolating c'est- g++ $CppFile @conanbuildinfo.args -o timer --std=c++11 dire

Ce ne sont probablement pas les bons (ou tous) cas d'utilisation, mais je pense qu'il serait utile d'avoir des cas d'utilisation documentés et de pouvoir y faire référence lors de l'examen de cette fonctionnalité, sinon il est facile de se perdre dans la manière dont la fonctionnalité traite réellement (ou non ) cas d'utilisation client.

De plus, le deuxième cas d'utilisation me semble exiger "l'invocation du shell". Je ne suis pas sûr que le premier le fasse. On dirait que l'exe pourrait être invoqué directement.

@rkeithhill Pourriez-vous ajouter des solutions de contournement existantes par souci d'exhaustivité dans votre article précédent?

@oising ,

Que donne ins ... que & cmd --% ... ne peut pas?

--% dans sa forme actuelle, _paramètre_ (symbole) - dont je présume que nous voulons conserver le comportement - ne peut pas être utilisé pour cmd /c --% , car il ne prend pas en charge la _multi-commande_ cmd lignes de commande (ni ^ comme caractère d'échappement.), étant donné que le premier | sans guillemets sera interprété comme l'opérateur de canal _PowerShell_ et que & est passé textuellement.
La même chose s'appliquerait sous Unix, où --% est généralement presque inutile, et avec sh -c échoue complètement, car sh attend la ligne de commande comme un argument _single_ (par exemple, sh -c --% echo hi renvoie une ligne vide, car echo seul est exécuté en tant que commande).

ins , avec son obligation de délimiter la ligne de commande directe en la passant _comme une seule chaîne_ résout ces problèmes et, comme indiqué, permet également l'utilisation de l'interpolation de chaîne pour incorporer la variable _PowerShell_ et les valeurs d'expression.

Et ce dernier nous amène à pourquoi la proposition --% _statement_ est une _dead-end street_ :

  • Il n'y a _aucun chemin de transition_ entre l'exécution d'une ligne de commande préexistante _exactement telle quelle_ et l'incorporation de la variable _PowerShell_ et des valeurs d'expression.

    • Le seul moyen - obscur et encombrant - d'y parvenir est de définir des _ variables d'environnement auxiliaires_ qui stockent les valeurs PowerShell d'intérêt et que vous pouvez ensuite référencer (de manière appropriée avec le shell avec %...% ou $... ) dans la ligne de commande native.
  • Vous avez l'inconvénient de ne pas pouvoir incorporer une instruction --% dans un pipeline PowerShell plus grand.

    • Notez qu'il existe des raisons légitimes _non_-couper-coller d'appeler le shell natif, où vous pouvez vous attendre à cette intégration; plus précisément, le passage de _raw byte data_ à travers un pipeline (intermédiaire) et l'envoi de _strings sans newline de fin_ nécessitent l'utilisation du tube natif - voir cette réponse SO pour un exemple réel.
  • Il y a aussi la maladresse du --% _parameter_ étant pratiquement inutile sous Unix, ce qui introduit une asymétrie étrange: si le --% _statement_ fonctionne aussi sous Unix, pourquoi pas le --% _parameter_ ? (par exemple,
    /bin/echo --% 'hi "noon"' donne textuellement 'hi noon' (plutôt que le hi "noon" attendu), car echo reçoit _deux_ arguments, textuellement 'hi et noon' , avec les guillemets simples conservés en tant que données et les guillemets doubles supprimés).


ins , comme alternative suggérée à l'instruction --% , serait idéalement juste un _ wrapper de commodité_ autour de cmd /c et sh -c , mais c'est en fait _requis_:

  • Au moins _temporairement_ sous Unix, tant que # 1995 n'est pas corrigé, car appeler sh -c '...' directement actuellement ne passe pas correctement les arguments avec " incorporés.

  • _Invariablement_ sous Windows, car vous devez passer la ligne de commande _as-is_ à cmd via le paramètre lpCommandLine de CreateProcess (ce que le paramètre --% ne peut pas faire en raison d'être limité à une commande _one_); Sinon, essayer de passer la ligne de commande sous la forme d'une _single string_ incluse dans "..." globalement à nouveau ne fonctionne pas de manière robuste en raison de # 1995.


Les problèmes de passage de _argument_ pourraient être évités en fournissant la ligne de commande pour exécuter _via stdin_ (que ins pourrait et devrait également prendre en charge), c'est-à-dire à _pipe_ la ligne de commande / le texte du script à l'exécutable du shell:

  • Sous Unix, cela fonctionne très bien, car passer du code via _stdin_ à sh revient essentiellement à passer un _fichier script_ pour l'exécution (tout comme sh -c '<code>' is ):
PSonUnix> @'
echo '{ "foo": "bar" }' | cat -n
'@ | sh

     1  { "foo": "bar" }
  • Hélas, sous Windows cmd ne gère pas correctement cette entrée: il imprime son logo et fait écho à chaque commande avant l'exécution, avec la chaîne d'invite:
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>

Malheureusement, PowerShell lui-même présente un comportement tout aussi inutile: voir # 3223


Tout comme sh et tous les principaux shells de type POSIX ( dash , bash , ksh , zsh ), la plupart des langages de script _do_ correctement prend en charge les entrées stdin-as-script, telles que python , node , ruby et perl .

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

hi

Pour incorporer des valeurs de variable et d'expression _PowerShell_, vous avez à nouveau la possibilité d'utiliser l'interpolation / concaténation de chaîne:

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

bar

ou en utilisant la fonction de passage d'arguments de l'interpréteur:

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

bar

J'espère que ce qui précède montre clairement qu'il n'y a _aucune_ nécessité d'une syntaxe spéciale - ni une instruction --% , ni une instruction #! , ni des blocs de code Markdown - qui à nouveau manqueraient le possibilité d'incorporer les valeurs du script PowerShell appelant.

Quant à la configurabilité du shell natif à utiliser: pour la prévisibilité, je ne pense pas que nous devrions en proposer un; les utilisateurs qui veulent cibler un shell différent peuvent simplement diriger la ligne de commande vers l'exécutable spécifique de ce shell ou, une fois # 1995 corrigé, le passer en argument.

  • Une variante discutable est de cibler /bin/bash (avec un repli (improbable) vers /bin/sh ) - alors que l'on pourrait espérer que les lignes de commande publiées reposent uniquement sur les fonctionnalités mandatées par POSIX, les lignes de commande avec Bash-isms (des fonctionnalités non POSIX que toutes les implémentations de /bin/sh ne devraient pas prendre en charge) sont probablement disponibles, étant donné la prévalence de bash .

@joeyaiello

À mon avis, les avantages de cette fonctionnalité particulière sont bien plus importants que l’impact # 1995.

Je trouve ahurissant que nous dépensions autant d'efforts que nous pour faire de cette fonction de copier-coller - principalement pour la commodité _interactive_ - un citoyen de première classe (maladroit, limité en fonctionnalités), alors que l'incapacité fondamentale de PowerShell à passer vide arguments et arguments avec " chars.

mais je ne vois pas les gens frapper # 1995 dans la nature en masse

Je le fais, et pour les raisons dont j'ai déjà parlé, vous le verrez de plus en plus.

Encore une fois, je dirais qu'un shell qui fait ce qui suit, comme le fait actuellement PowerShell, a un problème sérieux :

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

{ foo: bar }

@rjmholt CommandAst a la propriété Extent avec une chaîne d'entrée. La ré-analyse est aussi simple que divisée par le premier espace sous Windows ou par des espaces sous Unix. J'espère donc que nous n'avons rien perdu dans la corde et que nous obtenons de bonnes performances.

Ouais, je comprends le tirage au sort, et peut-être que les allocations de l'AST dans ce scénario ne sont pas terribles (bien que je soutienne que le langage ne devrait pas générer d'allocations inutiles pour une syntaxe simple). Mais tout ce que vous avez à faire est d'imaginer une entrée que bash / cmd voit comme une chaîne et PowerShell ne commence pas à rencontrer des problèmes:

N'analyse pas avec PowerShell actuel:

--% my_command "\" "

Analyse, mais la division par espaces donne un mauvais résultat:

--% my_command "\"    "\"

(Nous devons envoyer 4 espaces dans la chaîne, mais PowerShell voit deux chaînes séparées dans un tableau)

@rjmholt , toutes les grandes questions au bas de votre commentaire ci -

J'ai abordé la question de l'exécutable du shell natif à utiliser dans mon commentaire précédent , mais comme pour:

Est-ce que d'autres shells ou d'autres langages implémenteront ceci ou l'ont-ils déjà? Au niveau de la syntaxe, ou à tout autre niveau? Si c'est le cas, comment?

Je ne suis au courant d'aucune autre langue ayant fait cela au niveau de la syntaxe sans utiliser des délimiteurs explicites et autoriser l '_interpolation_.
Par exemple, perl a `...` pour exécuter des commandes shell, vous pouvez donc exécuter la commande suivante à partir d'un script sous Unix, par exemple:
my $foo='/'; print `ls $foo | cat -n`

Les aspects clés sont l'utilisation de délimiteurs explicites et la possibilité d'incorporer des valeurs de l'appelant - qui sont tous deux absents de la proposition actuelle pour l'instruction --% .

Comme indiqué précédemment, nous _pouvons_ utiliser cette approche pour un nouvel _opérateur_ (ou une sorte de syntaxe d'interpolation basée sur des délimiteurs), mais je ne pense pas que cela en vaille la peine, étant donné que
ins "ls $foo | cat -n" (ou avec une variante de chaîne ici) fera l'affaire, sans alourdir le langage d'une complexité supplémentaire.

perl offre une (bonne) solution au niveau de la syntaxe _parce qu'il n'est pas lui-même un shell_ mais veut rendre les appels shell aussi pratiques que possible.

En revanche, nous _sommes_ un shell (aussi), et proposons des invocations de style shell _directement_, sans aucune syntaxe supplémentaire (bien qu'actuellement défectueuse - voir # 1995).
Fournir une syntaxe spéciale pour rendre les invocations _d'un shell différent_ aussi faciles que possible semble inutile.

@iSazonov

_Si nous (et PowerShell) savons que nous exécutons un exécutable natif, pourquoi avons-nous besoin de "appeler un opérateur natif"? _

Si PowerShell analyse une chaîne d'entrée et obtient CommandAst, PowerShell effectue une découverte de la commande.
Si la commande est une commande native, PowerShell convertit ast en NativeCommandProcessor.
Nous pouvons implémenter new (expérimental NativeCommandProcessor) pour le cas qui analysera à nouveau l'étendue Ast (c'est-à-dire la chaîne d'entrée) afin d'obtenir le nom / chemin de l'exécutable natif et le reste de la chaîne comme argument ou arguments par les règles du système d'exploitation.

Si un utilisateur souhaite copier-coller depuis le shell, il doit taper "cmd" ou "bash" - cela fonctionnera sans modification.
Si un utilisateur souhaite copier-coller une ligne de commande native fonctionnera également comme g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Mais attendez, ne seriez-vous jamais capable d'utiliser une syntaxe PowerShell avec des commandes natives si nous faisions tout cela? Donc, comme sc.exe query $serviceName passerait $serviceName comme une chaîne littérale?

Mais attendez, ne seriez-vous jamais capable d'utiliser une syntaxe PowerShell avec des commandes natives si nous faisions tout cela? Donc, comme sc.exe query $serviceName passerait $serviceName comme une chaîne littérale?

L'option --% n'est pas destinée à répondre à ce besoin.

sc.exe query $serviceName est géré par &"<cmd>"

L'option --% n'est pas destinée à répondre à ce besoin.

sc.exe query $serviceName est géré par &"<cmd>"

Oui, bien sûr, je ne pense pas que ce soit ce à quoi @iSazonov fait référence. Je lis cela comme s'ils suggéraient une modification générale du fonctionnement de la liaison de paramètres pour les commandes natives.

Oui, bien sûr, je ne pense pas que ce soit ce à quoi @iSazonov fait référence. Je lis cela comme s'ils suggéraient une modification générale du fonctionnement de la liaison de paramètres pour les commandes natives.

Seigneur, j'espère que non.

Seigneur, j'espère que non.

Eh parfois, lorsque vous réfléchissez à l'abstrait, il est facile d'oublier l'évidence. Je veux dire, pour être juste, nous l'avons tous lu et avons immédiatement commencé à penser à sa mise en œuvre sans se rendre compte à quel point cela casserait.

Ou il me manque quelque chose d'évident et ce n'est pas du tout ce qui est proposé 😁

@essentialexch , pour être clair, re:

sc.exe query $serviceName est géré par &"<cmd>"

& "<cmd>" ne fonctionne que si <cmd> évalue à une simple commande _name ou path_, _pas d'arguments_ - les arguments doivent être passés séparément, individuellement - et ils n'ont pas besoin en soi de "...." citant les références de variables à développer (par exemple,
$exe = 'findstr'; & "where.exe $exe" échoue par conception; doit être & "where.exe" $exe (guillemets doubles facultatifs ici; si omis, le & est alors également facultatif))

Oui, bien sûr, je ne pense pas que ce soit ce à quoi @iSazonov fait référence. Je lis cela comme s'ils suggéraient une modification générale du fonctionnement de la liaison de paramètres pour les commandes natives.

Ma proposition est de corriger le # 1995 (avec un changement de rupture) aussi simple que possible. L'interpolation de cause ne fonctionne pas. Bien sûr, ce n'est pas une proposition finale - c'est une démonstration de la façon dont nous pourrions implémenter un correctif en quelques minutes. Et cela montre principalement que nous n'avons pas du tout besoin d'un opérateur d'appel natif, comme l'a souligné

Analyse, mais la division par espaces donne un mauvais résultat:

@rjmholt Oui, mais je pense que vous comprenez l'idée.
Le premier enfant de CommandAst est StringConstantExpressionAst avec la commande native comme BareWord. Nous pouvons couper le mot simple de CommandAst Extent et obtenir la liste des paramètres sans changement ast-s. Mais nous pourrions également ajouter de nouveaux ast pour conserver la liste des paramètres.
Je passe un temps pour l'explication parce que ci-dessous, je veux résumer la discussion et je pense que nous devrons mettre en œuvre des améliorations qui semblent complexes, mais je pense qu'elles pourraient être implémentées de manière simple et __ avec un retrait facile pour la compatibilité descendante__.


Je crois que l'intention de @ SteveL-MSFT de la proposition initiale était d'éviter un changement radical.
Je suis d'accord avec @TSlivede et @ mklement0 pour

__ La seule façon d'aller de l'avant est d'apporter une ou plusieurs modifications de rupture .__ (Après cela, un nouvel opérateur ou une nouvelle applet de commande peut également être utile dans certains scénarios.)


Nous devrions commencer par résumer les cas d'utilisation et les attentes des utilisateurs.
Voir le super message de @rkeithhill ci-dessus

  1. Les utilisateurs veulent activer / désactiver l'interpolation.
    PowerShell a déjà des chaînes / ici-chaînes avec des guillemets simples et doubles.
    Notre objectif est de comprendre comment appliquer / affiner / améliorer son.
  2. Les utilisateurs veulent exécuter n'importe quel exécutable ou un shell

    • tout exécutable - la commande commence par le nom de l'application et suit les arguments

      • avec / sans interpolation

      • sur une seule ligne / sur plusieurs lignes

    • shell est probablement un cas particulier car le ou les arguments sont un _script_

      • dans le scénario de copier-coller, les utilisateurs ne veulent pas d'interpolation et veulent une et plusieurs lignes

      • dans le scénario de script, les utilisateurs veulent activer / désactiver l'interpolation et veulent une et plusieurs lignes

    Pensées:
    Cela nous fait penser que le comportement du scénario copier-coller doit être le comportement par défaut.
    Les terminateurs de ligne dépendent de l'exécutable (dans bash, `dans PowerShell). Cela nous fait penser que:
    (1) un shell doit être explicitement déclaré comme n'importe quel exécutable,
    (2) peut-être que tout exécutable avec plusieurs lignes devrait être appelé au moyen d'un shell ou nous devons faire une analyse spéciale (pour supprimer le terminateur et construire une liste d'arguments en revenant au problème de l'échappement)
    (3) nous pourrions aborder le scénario du copier-coller dans PSReadline. Il pourrait convertir un tampon en commande PowerShell droite.

  3. Les utilisateurs veulent une seule ligne et multiligne.

    • peut-être devons-nous améliorer les chaînes ici pour une seule ligne afin de répondre à certains scénarios comme discuté précédemment.
    • voir 2 - (2) - utilisez toujours un shell pour la multiligne ou implémentez une analyse spéciale

Je ne veux pas en écrire plus parce que @TSlivede et @ mklement0 pourraient mettre à jour leurs pensées et leurs conclusions antérieures, mais qui peuvent maintenant être basées sur l'acceptation de changement (s). Je demanderais d'ouvrir un nouveau problème (et de fermer tous les anciens), d'énumérer tous les cas d'utilisation (en commençant par @ @rkeithhill et en ajouter d'autres), et peut-être créer des tests Pester - ce serait une bonne étape pour résoudre le problème.

Je veux seulement ajouter que nous pourrions envisager de nouvelles applets de commande pour simplifier les adoptions des utilisateurs telles que Invoke-Shell (Invoke-NativeShell), Test-Arguments (comme echoargs.exe pour montrer que l'application native obtient et affiche une aide pour les utilisateurs), Convert-Arguments ( pour convertir l'entrée utilisateur en arguments échappés à droite).

PS: Comme je l'ai montré ci-dessus, le comportement sur une seule ligne pourrait être facilement désactivé pour la compatibilité descendante au moment de l'exécution.

Les terminateurs de ligne dépendent de l'exécutable (dans bash, `dans PowerShell).

\ n'est pas un terminateur de ligne dans bash .

L'interpolation de cause ne fonctionne pas. Bien sûr, ce n'est pas une proposition finale - c'est une démonstration de la façon dont nous pourrions implémenter un correctif en quelques minutes. Et cela montre principalement que nous n'avons pas du tout besoin d'un opérateur d'appel natif, comme l'a souligné

De la même manière que nous pourrions corriger tous les bogues en quelques minutes si nous supprimions simplement le dépôt. C'est une pause assez énorme que vous proposez. Même si ce n'est qu'une hypothèse, je ne vois pas en quoi c'est utile.

Même si ce n'est qu'une hypothèse, je ne vois pas en quoi c'est utile.

@SeeminglyScience Il semble que nous soyons dans des contextes différents. Je dis que nous pouvons implémenter un correctif pour l'appel natif sur une seule ligne en tant que changement de rupture, mais sans interrompre l'analyseur. En d'autres termes, nous pouvons même changer de comportement à la volée, quel que soit le nouveau comportement que nous implémenterions.

En d'autres termes, nous pouvons même changer de comportement à la volée, quel que soit le nouveau comportement que nous implémenterions.

Je suppose que ce qui me manque, c'est de savoir comment changer ce comportement à la volée sans le demander explicitement (comme avec un opérateur d'appel natif).

@iSazonov

_Si nous (et PowerShell) savons que nous exécutons un exécutable natif, pourquoi avons-nous besoin de "appeler un opérateur natif"? _

Si PowerShell analyse une chaîne d'entrée et obtient CommandAst, PowerShell effectue une découverte de la commande.
Si la commande est une commande native, PowerShell convertit ast en NativeCommandProcessor.
Nous pouvons implémenter new (expérimental NativeCommandProcessor) pour le cas qui analysera à nouveau l'étendue Ast (c'est-à-dire la chaîne d'entrée) afin d'obtenir le nom / chemin de l'exécutable natif et le reste de la chaîne comme argument ou arguments par les règles du système d'exploitation.

Si un utilisateur souhaite copier-coller depuis le shell, il doit taper "cmd" ou "bash" - cela fonctionnera sans modification.
Si un utilisateur souhaite copier-coller une ligne de commande native fonctionnera également comme g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Je veux confirmer ce que vous entendez par là: suggérez-vous d'ignorer essentiellement toute la syntaxe PowerShell, lors de l'appel d'exécutables externes?

Plus précisément: suggérez-vous que courir par exemple

/bin/echo (1..5)

dans PowerShell ne mettra plus 1 2 3 4 5 mais devrait plutôt afficher littéral (1..5) ?

Si c'est vraiment ce que vous suggérez, alors je dois dire: je pense que c'est une idée terrible.

Cela ne simplifie pas les choses (les exécutables externes se comportent maintenant de manière syntaxique différente des applets de commande internes) et cela n'a rien à faire avec # 1995 IMHO ...

@rjmholt
D'en haut :

  • Est-ce que d'autres shells ou d'autres langages implémenteront ceci ou l'ont-ils déjà? Au niveau de la syntaxe, ou à tout autre niveau? Si c'est le cas, comment?

La seule chose qui me vient à l'esprit serait l'opérateur ipythons ! :

Exécution de !ps -'fax' || true dans les sorties ipython:

  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 n'affiche pas les guillemets autour des arguments , mais ps -'fax' || true est vraiment donné comme un seul argument à bash.)

Cependant, cette fonctionnalité permet toujours l'interpolation des variables python dans cette commande ( {pyvar} ).

Et semblable à @ de » l'exemple , ipython seulement met en œuvre cette

car ce n'est pas lui-même une coquille

Si ipython n'autorisait que la syntaxe python, ce ne serait pas très utile en tant que shell, ils permettent donc à cette syntaxe de transmettre des commandes à bash pour être utilisable comme shell. Powershell sur les autres revendications de la main pour être une coquille (jusqu'à # 1995 est résolu, je ne dirai pas qu'il est une coquille), donc il serait assez étrange d'ajouter une syntaxe spéciale pour appeler d' autres coquilles.

Si quelque chose comme ça est réellement implémenté pour une raison quelconque (espérons-le non), je suggérerais d'utiliser ! au lieu de --% comme "opérateur d'appel du shell".

! est quelque chose que les gens pourraient deviner (parce qu'ils savent ! à cette fin depuis ipython (ou depuis le mode de commande vim ou depuis less ou depuis ftp ) )

--% d'un autre côté est plus difficile à taper, peu probable d'être deviné et surtout: l'utilisation actuelle de --% a à mon avis très peu de choses en commun avec le "passer des trucs à un autre shell" comportement. Le courant --% émule exactement un élément de la syntaxe cmd: la substitution de variables (et même pas complètement). Il ne prend pas en charge le caractère d'échappement ^ , n'autorise pas la redirection vers les fichiers, etc. La seule chose quelque peu utile à propos de l'actuel --% est la possibilité de passer du contenu textuel dans le lpCommandLine - mais c'est quelque chose pour lequel l '"opérateur d'appel du shell" proposé n'est pas bon.

Une autre différence (qui a déjà été mentionnée) entre le "call shell operator" proposé et l'actuel --% : l'un se termine par des tuyaux, l'autre pas - très déroutant pour les nouveaux utilisateurs. ! d'autre part transfère | au shell dans toutes les applications que j'ai testées jusqu'à présent (ipython, vim, less, ed, etc.).

HELP about_Logical_Operators -S

Ouais, ! en lui-même est problématique car c'est un opérateur de négation logique dans le langage.

BTW l'une des propositions alternatives était &! - une sorte d'opérateur "appel natif / shell".

J'aurais aimé que @TSlivede ait insisté

  • Aucun opérateur / instruction _avec des arguments individuels_ ne peut résoudre proprement le problème de la ligne de commande I-want-to-use-PowerShell-values-in-the-native-command-line, étant donné que $ est utilisé comme variable sigil dans _both_ Shell de type PowerShell et POSIX.

  • Aucun opérateur / instruction _sans délimiteurs explicites autour de toute la ligne de commande native_ ne peut résoudre le problème où se trouve la fin de la ligne de commande native (dont vous pouvez ou non vous soucier).

@ PowerShell / PowerShell-Committee en a discuté aujourd'hui. À ce stade, il ne semble pas y avoir d'accord sur l'énoncé du problème initial ni sur la manière de le résoudre, nous ne pensons donc pas pouvoir intégrer cela en 7.1 avec une conception stable.

@ PowerShell / PowerShell-Committee en a discuté aujourd'hui. À ce stade, il ne semble pas y avoir d'accord sur l'énoncé du problème initial ni sur la manière de le résoudre, nous ne pensons donc pas pouvoir intégrer cela en 7.1 avec une conception stable.

182 commentaires!

Il y a eu de bonnes discussions à ce sujet! Je continuerai à surveiller tout nouveau commentaire sur ce problème. Nous encourageons toujours la communauté à implémenter l'applet de commande de type Invoke-NativeCommand publiée sur PSGallery indépendamment de ce problème.

Je pense qu'il est malheureux, lorsque vous avez eu un compromis mais une solution très utile à https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -662732627, de laisser tomber cela. Je sais que vous voulez un consensus, mais parfois un compromis est le meilleur qui puisse arriver. Non, ce n'est pas parfait, mais une solution SO à une seule ligne serait extrêmement utile. Au moins de l'avis de cette personne.

@essentialexch pour être clair, nous n'avons même pas de consensus au sein de l'équipe PowerShell

Comme cela est reporté, qu'en est-il de la mise en œuvre des helpers

nous pourrions envisager de nouvelles applets de commande pour simplifier les adoptions des utilisateurs comme Invoke-Shell (Invoke-NativeShell), Test-Arguments (comme echoargs.exe pour montrer que l'application native obtient et affiche une aide pour les utilisateurs), Convert-Arguments (pour convertir l'entrée utilisateur en arguments échappés à droite).

Je pense que Test-Arguments pourrait être très utile.

Je viens de publier un module, Native , ( Install-Module Native -Scope CurrentUser ) que je vous invite tous à essayer et dont je vais utiliser les commandes ci-dessous ; les cas d'utilisation numériques référencés proviennent du commentaire de ci-dessus .

Si jamais nous voulons que le brouillard conceptuel se dissipe, je pense que nous devons encadrer les cas d'utilisation différemment.
Aucun d'entre eux ne nécessite de modifications dans l'analyse_.

Cas d'utilisation [appel shell natif]:

Vous voulez exécuter une _ commande individuelle_ (cas d'utilisation 1) ou une _ ligne de commande multi-commandes_ entière_ (cas d'utilisation 2) _ écrite pour le shell natif de la plateforme_ (ce problème):

  • Puisque vous utilisez la syntaxe d'un shell différent, vous passez la commande (ligne) _ comme une seule chaîne_ au shell cible, pour que _it_ fasse l'analyse; L'interpolation de chaîne de PowerShell offre la possibilité d'incorporer des valeurs PowerShell dans la chaîne transmise (cas d'utilisation 2a).
  • ins ( Invoke-NativeShell ) couvre ces cas d'utilisation.
# 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'

La syntaxe here-string n'est pas la plus pratique (d'où la suggestion d'implémenter une variante en ligne - voir # 13204), mais vous n'avez pas à l'utiliser; pour les commandes textuelles, vous pouvez utiliser '...' , qui ne nécessite que _doubling_ intégré ' , s'il est présent.

En outre, voici un substitut raisonnable à «l'opérateur StackOverflow» :

Si vous placez le gestionnaire de clés PSReadLine dans votre fichier $PROFILE , vous pourrez utiliser Alt-v pour échafauder un appel à ins avec une chaîne verbatim here-string dans lequel le texte actuel du presse-papiers est collé.Enter soumet l'appel.

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

Cas d'utilisation [appel exécutable direct]:

Vous voulez appeler un _exécutable externe unique_ en utilisant la syntaxe de _PowerShell_, avec des _ arguments individuels_ (cas d'utilisation 1a et 3).

  • Il s’agit d’un mandat fondamental de toute coquille et il est d’une importance vitale qu’elle fonctionne de manière robuste.

    • Vous devez pouvoir compter sur PowerShell pour pouvoir transmettre tous les arguments qui résultent de _its_ parsing _as-is_ à l'exécutable cible. Ce n'est actuellement pas le cas - voir # 1995 .
  • Cela nécessite que vous compreniez la syntaxe de _PowerShell_ et les métacaractères en mode argument de _its_.

    • Vous devez être conscient que ` est utilisé comme caractère d'échappement et pour la continuation de la ligne.
    • Vous devez être conscient que PowerShell a des métacaractères supplémentaires qui nécessitent des guillemets / échappements pour une utilisation verbatim; ce sont (notez que @ n'est problématique que comme premier caractère d'un argument.):

      • pour les shells de type POSIX (par exemple, Bash): @ { } ` (et $ , si vous voulez empêcher l'expansion initiale par PowerShell)
      • pour cmd.exe : ( ) @ { } # `
      • Individuellement ` échapper à ces caractères. est suffisant (par exemple, printf %s `@list.txt ).

Pendant que nous attendons que # 1995 soit corrigé, la fonction ie (for i nvoke (external) e xecutable) comble le vide, simplement en précédant un appel direct; par exemple, au lieu de l'appel suivant:

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

vous utiliseriez ce qui suit:

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

Le module Native est livré avec quelque chose qui ressemble à echoArgs.exe , la commande dbea ( Debug-ExecutableArguments ); exemple de sortie sous 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"

En utilisant le commutateur -UseIe ( -ie ), vous pouvez dire à dbea de passer les arguments via ie , ce qui démontre qu'il résout les problèmes:

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

Remarque: une fois que # 1995 est corrigé, ie n'est plus nécessaire; dans l'intérêt de la compatibilité ascendante, ie est conçu pour détecter et se reporter automatiquement à un correctif; c'est-à-dire qu'une fois le correctif en place, ie cessera d'appliquer ses solutions de contournement et agira effectivement comme un appel direct / & .

Cas d'utilisation [appel exécutable Windows non autorisé direct]:

Il existe deux problèmes principaux, qui se posent sous _Windows uniquement_:

  • Vous invoquez un exécutable «non autorisé» dont les exigences de cotation diffèrent de la convention la plus utilisée ; par exemple, msiexec.exe et msdeploy.exe nécessitent que prop="<value with spaces>" soient cités précisément de cette façon - la citation juste autour de la partie _value_ - même si "prop=<value with spaces>" - citation de l'ensemble argument - _devrait_ être équivalent (ce dernier est ce que PowerShell - à juste titre - fait dans les coulisses).

  • Vous appelez _un fichier batch_ avec _un argument qui ne contient pas d'espaces, mais contient cmd.exe métacaractères_ tels que & , ^ , ou | ; par exemple:

    • .\someBatchFile.cmd 'http://example.org/a&b'
    • PowerShell - à juste titre - transmet http://example.org/a&b _unquoted_ (car il n'y a pas d'espace blanc incorporé), mais cela interrompt l'appel du fichier batch, car cmd.exe - sans raison - soumet les arguments passés à un fichier batch au même règle d'analyse qui s'appliquerait à l' intérieur de cmd.exe plutôt que de les accepter comme littéraux.

    • Remarque: Bien que l'utilisation de fichiers de commandes pour implémenter _directement_ les fonctionnalités diminue probablement, leur utilisation comme des _ points d'entrée CLI_ est toujours très courante, comme la CLI az Azure, qui est implémentée en tant que fichier de commandes az.cmd .

Remarque: ie gère automatiquement ces scénarios pour les fichiers batch et pour msiexec.exe et msdeploy.exe , donc pour les appels _most_ aucun effort supplémentaire ne devrait être nécessaire ; cependant, il est impossible d'anticiper tous les exécutables "escrocs".

Il existe deux façons de résoudre ce problème:

  • Étant donné que cmd.exe , après avoir appliqué sa propre interprétation aux arguments sur une ligne de commande, _does_ conserve les guillemets comme spécifié, vous pouvez déléguer à un appel ins sous Windows:

    • ins '.\someBatchFile.cmd "http://example.org/a&b"'
    • Si vous utilisez une chaîne _expandable_, cela vous permet à nouveau d'incorporer des valeurs PowerShell dans la chaîne de commande.

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

  • Vous pouvez également utiliser l'implémentation actuelle de --% , mais attention à ses limitations:

    • .\someBatchFile.cmd --% "http://example.org/a&b"'
    • Étant donné la façon dont --% est implémenté, la seule façon d'incorporer des valeurs PowerShell est de - maladroitement - définir un _aux. variable d'environnement_ et référencez-la avec la syntaxe %...% :

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


La gestion des erreurs

Le https://github.com/PowerShell/PowerShell-RFC/pull/88 en attente apportera une meilleure intégration de la gestion des erreurs avec les exécutables externes (natifs).

En attendant, le module Native est livré avec deux fonctionnalités permettant d' accepter ad-hoc de traiter un code de sortie différent de zéro comme une erreur de fin de script :

  • ins prend en charge le commutateur -e / -ErrorOnFailure , qui renvoie une erreur si $LASTEXITCODE est différent de zéro après l'appel au shell natif.

  • iee est une fonction wrapper pour ie qui renvoie de manière analogue une erreur si $LASTEXITCODE est différent de zéro après l'appel à l'exécutable externe.

Il existe de nombreuses subtilités dans les commandes fournies avec le module.
Nous espérons que l’aide assez étendue basée sur des commentaires les couvre suffisamment.

Si jamais nous voulons que le brouillard conceptuel se dissipe, je pense que nous devons encadrer les cas d'utilisation différemment.

Génial! Ce que j'ai publié plus tôt était juste pour amener les gens à réfléchir à la création de cas d'utilisation plus nombreux et meilleurs.

Je devrais clarifier quelques aspects importants de ins ( Invoke-NativeShell ) qui diffèrent de ce que j'ai proposé précédemment; le but était de rendre compte de l'ubiquité de bash et de fournir une CLI cohérente sur toutes les plates-formes.

  • Sous Unix , j'ai décidé d'appeler /bin/bash plutôt que /bin/sh , étant donné l'ubiquité de Bash, mais vous pouvez choisir d'utiliser /bin/sh avec -UseSh ( -sh ) ( /bin/sh sert également de solution de secours dans le cas peu probable où /bin/bash n'est pas présent).

    • Dans l'intérêt d'une expérience cohérente sur les formulaires et plates-formes d'invocation, j'ai masqué la possibilité de définir $0 , le nom d'appel; c'est-à-dire que tous les arguments directs commencent par $1 , ce qui est cohérent avec la façon dont les arguments sont passés lorsque vous dirigez le script depuis le pipeline, et probablement ce que les utilisateurs attendent:
# 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
  • Sous Windows, j'ai dû utiliser un _batch file_ temporaire dans les coulisses pour des raisons techniques, mais je pense que l'utilisation de la syntaxe de fichier batch est finalement le meilleur choix de toute façon ( %%i plutôt que %i en for variables de boucle, possibilité d'échapper à % comme %% ).

    • L'utilisation d'un fichier de commandes permet également de prendre en charge les arguments d'intercommunication, comme sous 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]
Cette page vous a été utile?
0 / 5 - 0 notes