Powershell: Llamar al operador nativo

Creado en 30 jun. 2020  ·  189Comentarios  ·  Fuente: PowerShell/PowerShell

Planteamiento del problema

Actualmente, hay casos en los que cortar y pegar una línea de comandos nativa no se ejecuta como se esperaba en PowerShell. Esto puede deberse a un análisis incorrecto de las comillas que deben pasarse al comando nativo o al uso de la sintaxis de PowerShell que no debe interpretarse como PowerShell. PowerShell tiene un argumento especial --% cuando se usa con comandos nativos trata el resto de los argumentos como literales pasados ​​al comando nativo, pero tiene varios problemas:

  1. No es detectable, los usuarios deben conocer este parámetro especial con anticipación.
  2. | , && , y || tienen prioridad, entonces: wsl --% ls | less ejecutaría wsl ls y canalizaría los resultados a less ejecuta en PowerShell en lugar de less ejecuta en wsl
  3. Si corta y pega una línea de comando, necesitará editar la línea para insertar --% hacia el principio
  4. En los sistemas Unix, los argumentos después de -% se pasan palabra por palabra sin globbing donde los comandos nativos en Unix esperan que el shell realice globbing

Detalles de implementación técnica propuesta

La propuesta es introducir un nuevo operador --% (Call Native).

Cualquier contenido de texto después de este operador llamará al "shell predeterminado" del sistema operativo para ejecutarlo. En Windows, sería cmd.exe y en los sistemas basados ​​en Unix sería / bin / sh. Esto resuelve el problema de la expansión global en los sistemas basados ​​en Unix y también permite la expansión %variable% en Windows. A diferencia de --% switch, esto también significa que | , && y || se tratan como parte de la línea de comandos nativa.

Esto significa que estos dos son funcionalmente iguales:

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

donde $foo y $PWD son evaluados por el shell dentro de WSL. Tenga en cuenta que en el primer ejemplo, debería saber escapar && para que se ejecute dentro de WSL en lugar de dentro de PowerShell.

Para canalizar la salida de dicha ejecución a PowerShell, el usuario debe almacenar los resultados en una variable primero:

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

Tenga en cuenta que, a diferencia del operador de llamada actual & , no puede usar ninguna sintaxis de PowerShell, por lo que:

--% $commandline

no resolvería $commandline como una variable primero por PowerShell, sino que pasaría $commandline al shell predeterminado para procesar sin resolver.

El problema de cortar y pegar se resuelve simplemente pegando después de escribir --% .

El ejemplo anterior para wsl se vería así:

--% wsl ls | less

donde la intención es ejecutar toda la línea dentro de la instancia de WSL Linux.

Descubribilidad

Los usuarios que ya estén familiarizados con --% como conmutador pueden pasar fácilmente al uso de este nuevo operador cuando tenga sentido. Para los nuevos usuarios, --% es único para que los motores de búsqueda lo encuentren fácilmente relacionado con PowerShell.

Consideraciones alternativas

&! y &n se propusieron como sigilo, pero hubo un rechazo porque &! es un operador válido en algunos idiomas, lo que dificulta la búsqueda de documentación en la web. También existía la preocupación de si una imagen similar al operador de llamada & sería confusa para los usuarios.

Hay dudas sobre la compatibilidad con la continuación de línea cuando se utiliza este nuevo operador. Sugeriría que no lo apoyemos inicialmente.

Se propuso una solución de cmdlet en lugar de una solución de operador. Creemos que esto no resuelve el problema de "cortar y pegar", ya que ahora necesita saber cómo poner la canalización entre comillas simples y / o escapar de los caracteres especiales. Creemos que un cmdlet como Invoke-NativeCommand (sustantivo por determinar) sería útil como una opción adicional en lugar de reemplazar la necesidad de un operador.

Asuntos relacionados

Esto también debería resolver estos problemas:

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

Committee-Reviewed Issue-Discussion Issue-Enhancement

Comentario más útil

Acabo de publicar un módulo, Native , ( Install-Module Native -Scope CurrentUser ) que los invito a todos a probar y cuyos comandos usaré a continuación ; los casos de uso numéricos a los que se hace referencia son del comentario anterior de @rkeithhill .

Si alguna vez queremos que se aclare la niebla conceptual, creo que debemos enmarcar los casos de uso de manera diferente.
_Ninguno de ellos requiere ningún cambio en el análisis_.

Caso de uso [llamada de shell nativo]:

Quiere ejecutar un _comando individual_ (caso de uso 1) o una línea de comando completa _multi-comando_ (caso de uso 2) _escrito para el shell nativo de la plataforma_ (este problema):

  • Ya que está usando una sintaxis de shell diferente, está pasando el comando (línea) _ como una sola cadena_ al shell de destino, para que _it_ realice el análisis; La interpolación de cadenas de PowerShell ofrece la capacidad de incrustar valores de PowerShell en la cadena pasada (caso de uso 2a).
  • ins ( Invoke-NativeShell ) cubre estos casos de uso.
# 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 sintaxis here-string no es la más conveniente (de ahí la sugerencia de implementar una variante en línea - vea # 13204), pero no _ tiene_ que usarla; para los comandos textuales, puede usar '...' , que solo requiere _doubling_ embedded ' , si está presente.

Además, aquí hay un sustituto razonable del "operador StackOverflow" :

Si coloca el siguiente controlador de clave PSReadLine en su archivo $PROFILE , podrá usar Alt-v para realizar una llamada a ins con una cadena aquí textual en el que se pega el texto actual del portapapeles.Entrar envía la llamada.

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

Caso de uso [llamada ejecutable directa]:

Desea llamar a un _solo ejecutable externo_ usando la sintaxis de _PowerShell_, con _ argumentos individuales_ (casos de uso 1a y 3).

  • Este es un mandato básico de cualquier shell, y es de vital importancia que funcione con solidez.

    • Debe poder confiar en que PowerShell puede pasar a través de cualquier argumento que resulte de _su_ analizar _ como está_ al ejecutable de destino. Este no es el caso actualmente - ver # 1995 .
  • Requiere que comprenda la sintaxis de _PowerShell_ y sus metacaracteres en modo argumento.

    • Debe tener en cuenta que ` se usa como carácter de escape y para la continuación de la línea.
    • Debe tener en cuenta que PowerShell tiene metacaracteres adicionales que requieren citar / escapar para su uso literal; estos son (tenga en cuenta que @ solo es problemático como el primer carácter de un argumento):

      • para shells similares a POSIX (por ejemplo, Bash): @ { } ` (y $ , si desea evitar la expansión inicial de PowerShell)
      • por cmd.exe : ( ) @ { } # `
      • Individualmente ` escapando de dichos caracteres. es suficiente (por ejemplo, printf %s `@list.txt ).

Mientras esperamos para # 1995 que se fije, la función ie (para i nvoke (externo) e xecutable) llena el vacío, simplemente anteponiendo a una llamada directa; por ejemplo, en lugar de la siguiente llamada:

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

usarías lo siguiente:

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

El módulo Native viene con algo parecido a echoArgs.exe , el comando dbea ( Debug-ExecutableArguments ); salida de muestra en 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"

Al usar el modificador -UseIe ( -ie ), puede decirle a dbea que pase los argumentos a través de ie , lo que demuestra que soluciona los problemas:

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

Nota: Una vez que se corrige # 1995, ie ya no es necesario; en aras de la compatibilidad con versiones posteriores, ie está diseñado para detectar y diferir automáticamente una solución; es decir, una vez que la solución esté en su lugar, ie dejará de aplicar sus soluciones y actuará efectivamente como una llamada directa / & .

Caso de uso [llamada ejecutable de Windows no autorizada directa]:

Hay dos problemas principales, que surgen en _Solo Windows_:

  • Está invocando un ejecutable "deshonesto" que tiene requisitos de citas que difieren de la convención más utilizada ; p. ej., msiexec.exe y msdeploy.exe requieren que prop="<value with spaces>" se cite precisamente de esa manera - la cita alrededor de la parte _value_ - aunque "prop=<value with spaces>" - cita de argumento - _ debería_ ser equivalente (este último es lo que PowerShell - justificadamente - hace detrás de escena).

  • Está invocando _un archivo por lotes_ con _un argumento que no contiene espacios, pero que contiene cmd.exe metacaracteres_ como & , ^ o | ; p.ej:

    • .\someBatchFile.cmd 'http://example.org/a&b'
    • PowerShell - justificadamente - pasa http://example.org/a&b _unquoted_ (ya que no hay espacios en blanco incrustados), pero esto rompe la invocación del archivo por lotes, porque cmd.exe - irrazonablemente - somete los argumentos pasados ​​a un archivo por lotes al misma regla de análisis que se aplicaría dentro de cmd.exe lugar de aceptarlos como literales.

    • Nota: Si bien el uso de archivos por lotes para _directly_ implementar la funcionalidad probablemente está disminuyendo, usarlos como _ puntos de entrada de CLI_ sigue siendo muy común, como az CLI de Azure, que se implementa como archivo por lotes az.cmd .

Nota: ie maneja automáticamente estos escenarios para archivos por lotes y para msiexec.exe y msdeploy.exe , por lo que para la mayoría de las llamadas no debería ser necesario ningún esfuerzo adicional ; sin embargo, es imposible anticipar _todos_ los ejecutables "deshonestos".

Hay dos formas de resolver esto:

  • Dado que cmd.exe , después de aplicar su propia interpretación a los argumentos en una línea de comando, _does_ conserva las comillas como se especifica, puede delegar en una llamada ins en Windows:

    • ins '.\someBatchFile.cmd "http://example.org/a&b"'
    • Si usa una cadena _expandable_, esto nuevamente le permite incrustar valores de PowerShell en la cadena de comando.

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

  • Alternativamente, use la implementación actual --% , pero tenga cuidado con sus limitaciones:

    • .\someBatchFile.cmd --% "http://example.org/a&b"'
    • Dado cómo se implementa --% , la única forma de incrustar valores de PowerShell es, torpemente, definir un _aux. variable de entorno_ y haga referencia a ella con la sintaxis %...% :

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


Manejo de errores

El https://github.com/PowerShell/PowerShell-RFC/pull/88 pendiente traerá una mejor integración de manejo de errores con ejecutables externos (nativos).

Mientras tanto, el módulo Native se envía con dos funciones para la suscripción ad-hoc para tratar un código de salida distinto de cero como un error de terminación del script :

  • ins admite el modificador -e / -ErrorOnFailure , que arroja un error si $LASTEXITCODE es distinto de cero después de la llamada al shell nativo.

  • iee es una función contenedora para ie que, de forma análoga, arroja un error si $LASTEXITCODE es distinto de cero después de la llamada al ejecutable externo.

Hay muchas sutilezas en los comandos que se envían con el módulo.
Es de esperar que la ayuda bastante extensa basada en comentarios los cubra lo suficiente.

Todos 189 comentarios

Me gusta la idea y la encuentro útil, pero creo que sería mucho más útil (estoy pensando específicamente en el caso de un DSL) si puedo obtener una expansión de una sola cadena. Entonces, si he creado una cadena compatible con cmd.exe en mi PS, tengo una manera de encadenar literalmente a cmd.exe. Quizás

&n -command $string

Eso potencialmente también me permitiría especificar mi intérprete:

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

en términos del nombre / operador, es "&!" ¿en uso? Eso tiene similitud con shbang ("#!").

$! es una buena sugerencia!

El shell se puede especificar simplemente especificándolo como el comando:

&! /bin/sh -c blah

La expansión $var crea un problema en el sentido de que $ puede necesitar ser literal para pasar al comando / shell nativo. Uno de los problemas que esto intenta evitar es cuando las personas necesitan descubrir cómo escapar de todo correctamente. Si necesita una expansión variable, siempre puede hacer:

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

Sin embargo, en los casos en los que desee mezclar PowerShell con el comando nativo, probablemente sea mejor usar la sintaxis actual y ser consciente de lo que debe escaparse correctamente, ya que consideraría ese uso avanzado.

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

No puedo creer que hayas sugerido iex. :-)

@essentialexch, hay algunas ocasiones en las que es apropiado :)

Tenga en cuenta que en el ejemplo, la expectativa es que el usuario haya validado correctamente el contenido de $mycommand antes de ejecutar.

Definitivamente se necesita una excelente redacción y una solución para problemas como el que describió.
Yo mismo no sabía nada de --% y, lamentablemente, no recuerdo haberlo visto nunca. Por lo tanto, el operador &n podría sufrir problemas de descubrimiento similares y no me parece natural combinar un símbolo con un carácter (y me recuerda a% f en declaraciones C printf). Dado que es un cambio radical de todos modos, ¿hay alguna razón por la que no podría ser, por ejemplo, && ?
Tal vez me resultaría más intuitivo usar algo como corchetes (¿o llaves dobles?) Para marcar el área que desea ejecutar. Ejemplo: & [ wsl ls ] | less

Estoy de acuerdo en que &! es la forma más intuitiva de hacerlo para aquellos que provienen de otros shells, sin embargo, en super-duper-linter generalmente estoy ejecutando comandos nativos construyendo una matriz de parámetros y luego aplicándola al comando.

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

Entonces esta es la mejor manera en caso de condicional y todo eso para mí.

@bergmeister Originalmente pensé en && porque visualmente es similar a & que mucha gente conoce hoy. Sin embargo, && ser un operador de cadena de canalización puede causar confusión sobre lo que se supone que debe hacer. Me gusta la sugerencia &! actualmente.

@JustinGrote , no hay razón para no seguir &! es realmente para los casos en los que solo desea cortar y pegar o tiene algunos argumentos que entran en conflicto con la sintaxis de PowerShell y no quiere o no sabe cómo escapar correctamente

Oh, hombre, recuerdo haber hablado con Bruce y Jason sobre esto hace una década. Mi intuición es que inventar otro operador parece innecesario aquí. Sé que algunas personas en este hilo no han oído hablar de --% pero apuesto a que hay más de los que piensas. Qué hay de malo en:

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

¿Por qué nadie ha sugerido esto? Es lógicamente consistente con el uso de --% otros lugares para decir "todo lo que sigue a esto debe pasarse sin un tratamiento especial de PowerShell".

@oising bien para uno, ese comando no funciona porque tiene que precederlo con el comando que desea ejecutar, ¿está sugiriendo agregar la funcionalidad?
image

Esto hace lo que se espera:
function Invoke-LiteralCommand ($command) {Invoke-Expression "& $command --% $args"}

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

@JustinGrote Sí, sé que ahora no funciona :) Estoy sugiriendo que _en lugar de &n o &! o cualquier otra cosa de la que se esté hablando. Simplemente tiene más sentido para mí: & es llamada y --% es suprimir el análisis de PowerShell; juntos son coherentes y más fáciles de descubrir que agregar algo nuevo. No me gusta la idea de tener una "llamada" y un operador de "llamada nativa".

He ampliado mi ejemplo para mostrar las diferencias.

@oising Estaría bien con eso sobre un nuevo operador, aunque eso es una gran cantidad de tipeo obtuso para "nuevo usuario de powershell que conoce bash", que es para lo que supongo que estaría destinado.

@oising Estaría bien con eso sobre un nuevo operador, aunque eso es una gran cantidad de tipeo obtuso para "nuevo usuario de powershell que conoce bash", que es para lo que supongo que estaría destinado.

De ningún modo. Es para el usuario de PowerShell que no comprende las complejas reglas de cotización entre PowerShell / cmd.exe / bash. Como dice el título del problema "llamar nativo", para llamar ejecutables nativos.

@oising Estaría bien con eso sobre un nuevo operador, aunque eso es una gran cantidad de tipeo obtuso para "nuevo usuario de powershell que conoce bash", que es para lo que supongo que estaría destinado.

De ningún modo. Es para el usuario de PowerShell que no comprende las complejas reglas de cotización entre PowerShell / cmd.exe / bash. Como dice el título del problema "llamar nativo", para llamar ejecutables nativos.

Cierto, o para aquellos de nosotros que preferiríamos no tener que pensar en ellos en absoluto.

Sí, estoy con @oising. Este concepto existe, es lamentablemente insuficiente. Si vamos a implementar algo completamente nuevo, es mejor que desaprovechemos / eliminemos la sintaxis anterior.

Siento que a menudo estas ideas se expresan, no se le da suficiente peso a su objetivo demográfico real. Si los usuarios _ya_ están luchando por encontrar los métodos existentes, y encuentran los métodos existentes que faltan cuando se encuentran, es una llamada a a) mejorar la documentación yb) mejorar la funcionalidad real de los operadores existentes.

En su lugar, obtenemos una opción extraña c) que se supone que de alguna manera aborda los problemas pasados ​​al tiempo que presenta un operador _otro_ que se basa en la documentación que ya no es lo suficientemente accesible para que los usuarios la encuentren de forma natural.

Estoy de acuerdo en que los mensajes de error y / o el sistema de sugerencias deben usarse para ayudar a introducir estos conceptos. ¿No estoy de acuerdo en que necesitemos un tercero? ¿cuarto? ¿quinto? (Literalmente he perdido la cuenta, que alguien me ayude aquí) forma de invocar comandos. Los métodos existentes son insuficientes, eso significa que deberíamos mejorarlos, no dejarlos atrás como telarañas para confundir aún más a los usuarios cuando empiecen a investigarlos.

La primera pregunta que suelo hacerme cuando la gente se da cuenta de que hay 5 formas de hacer algo es "¿por qué hay 2 o 3 formas que literalmente hacen lo mismo pero no funcionan tan bien?", Y mi respuesta es la de siempre: - el equipo de PS está _way_ demasiado concentrado en asegurarse de que todo lo de la última década ++ todavía funcione, cuando intenta agregar / mejorar la funcionalidad. Aquí lo vemos de nuevo, tenemos implementaciones existentes pero insuficientes que necesitan revisión y mejora, y la solución propuesta es enturbiar aún más el grupo agregando otra implementación potencialmente incompleta.

Deberíamos terminar lo que comenzamos, no comenzar otra implementación desde el principio, en mi opinión.

Creo que si vamos a utilizar & para más de unos pocos casos de uso únicos, deberíamos empezar a pensar en convertir esto en un verbo o considerar la estandarización en todo el lenguaje.

Lo último que me gustaría es tener confusión sobre lo que se supone que debe hacer &. En su totalidad.

Consideraría 3 escenarios:

  1. Expansión completa: comportamiento actual de PowerShell.
  2. Literal completo: que se propone en el OP.
  3. Expansión parcial: wsl ls $path todavía funciona

Me pregunto dónde comenta @ mklement0 . :-)

Al comenzar a leer este hilo pensé; ¡gran idea! Pero después de leer los comentarios, me puse a pensar. Siempre he encontrado que el comando & 'no es digno de los estándares de PS' y algo que me recuerda a los 'días de PERL' (ugh). Introducir otro comando de PS no estándar (que no sea sustantivo-verbo) no ayudará. Ciertamente, no los menos entendidos en PD.

Tampoco sabía nada del parámetro -%. Utilizo el mismo principio que @JustinGrote como solución.

Cortar / pegar comandos en PS shell nunca ha sido mi principal 'cosa'.
¿No sería mejor reemplazar esos comandos nativos con los de PowerShell?
Hacer ruta para reemplazar cmd.exe por completo ...

Voto por explorar la mejora de los comandos existentes. Y, de hecho, mejorando algo de documentación sobre -% y & uso

@iSazonov, el caso de uso que propone @ SteveL-MSFT es el escenario de 'cortar y pegar'. La expansión parcial haría las cosas mucho más difíciles, creo (en el lado AST de las cosas)

Inmediatamente aunque de "#!" y entonces "!#". Me gusta el "&!".

Probablemente podamos considerar agregar aceleradores de tokens a las llamadas a funciones en el tokenizador. Esto permitiría el uso de lo siguiente.

Iex -l
O Invoke-Expression -literal que deja de analizar antes de pasar.

Siento que agregar más tokens únicos en el nivel de análisis hace que la barrera de entrada suba y la capacidad de descubrimiento de esta función disminuya.

Get-Help &, por ejemplo, no aparece con nada. Y tenemos que buscarlo en about_Operators.

Sin embargo, hay casos únicos para call_operator que no se documentan.
& modulename {comando}
le permite acceder a un módulo. Y estoy seguro de que hay más. Pero la capacidad de detección es tan baja que resulta difícil encontrarla en la documentación nativa. Y la comunidad lo sabe solo a través de JSnover y una charla que dio mostrándonos cosas interesantes.

Creo que cualquier cosa que se decida debe tener en cuenta cuán visible es esta "función", desde la perspectiva de un nuevo usuario y tener en cuenta que los usuarios más nuevos intentarán usar esto en Powershell 5, no sin saber que es nuevo en el núcleo de pwsh si la capacidad de descubrimiento es demasiado baja.

Me encantaría que cambiemos el comportamiento existente, pero cambiar una API tan fundamental en PowerShell simplemente rompería muchas cosas. Posiblemente podríamos considerar migrar con una configuración.

Romper las cosas de manera más directa podría haber sido posible antes de que PS 6 pasara a GA, pero honestamente, incluso entonces habría sido una seria amenaza para la compatibilidad con versiones anteriores (no muy diferente a la función print() Python).

En términos de pasar argumentos a los subprocesos, creo que tanto la invocación sincrónica como la invocación Start-Process ya tienen problemas históricos al discutir una revisión, y mi sensación es que ambas necesitan invertir a la vez para lograr coherencia. Start-Process en particular necesita actualizar su soporte para pasar una matriz de argumentos.

Para la invocación sincrónica con el paso de cmdline arg, veo cuatro tipos de paso de argumentos posibles:

  • Comportamiento actual, que se convertiría en heredado, que está sujeto a CommandLineToArgvW en Windows y tiene resultados inesperados
  • Comportamiento predeterminado, que debe pasar argumentos por su valor de expresión pero aplicar las reglas habituales de token de palabra clave, de modo que cosas como > y | separen comandos. En este modo, el valor que un subproceso toma de la expresión debe ser lo que se muestra en Write-Host $val
  • Comportamiento literal, donde todos los tokens se interpretan como cadenas de palabras desnudas hasta que se ve el token de escape. Esto es lo que se supone que debe hacer --% hoy, pero lo ideal sería que solo tuviera un token final que se pueda incrustar en la misma línea como --% ... %--
  • Comportamiento de línea de lectura Bash, donde se aplica una lógica de escape alternativa, orientada a sh, lo que permite una compatibilidad más simple

Creo que el primer comportamiento debería eliminarse gradualmente introduciendo el segundo como una función experimental y luego intercambiándolos.

El segundo y tercer comportamiento podrían tener sus propios sellos como &! y &# o quizás +% ... -% o algo así. Pero para el modo palabra por palabra, creo que una faceta importante sería simplificar la ficha de escape para que se tomen más fichas palabra por palabra. Similar a un heredoc.

Si la solicitud es solo para abordar el escenario de copiar y pegar como "Cualquier contenido de texto después de este operador llamará al" shell predeterminado "del sistema operativo para ejecutar". ¿Por qué no decir esto explícitamente por shell ?
`` powerhell
PS> shell wsl ls | Menos
PS> (shell wsl ls * .txt) | Seleccionar cadena Hola

Es más visible y legible que los operadores crípticos en estilo Forth / APL.

No veo absolutamente cómo se resolvería esto # 1995: Ver https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -653174261

Además, el problema con WSL es un problema del comportamiento de wsl.exe (consulte https://github.com/PowerShell/PowerShell/issues/12975#issuecomment-650353021) no de powershell.
(Ok, hay un problema con powershell, pero ese es el # 1995)

Ah, ¿y no es esto casi un duplicado de https://github.com/PowerShell/PowerShell/issues/12975 ? Porque esencialmente puedo repetir principalmente lo que mencioné allí:

@bitlocos
El problema es que la falta de capacidad para delimitar una parte de una línea de comandos para que se pase varbatim al comando / script de recepción es algo que confunde a los usuarios todo el tiempo.
¿Qué quiere decir con "parte de una línea de comandos que se pasará varbatim"? ¿Quiere decir 1. pasar una secuencia de caracteres literalmente al ejecutable llamado, de modo que el ejecutable llamado tenga esta secuencia de caracteres en un elemento de su matriz de argumentos (por ejemplo, esos caracteres están disponibles en `argv [1]` en [main ] (https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs-2019#command-line-arguments)) O te refieres a 2. insertar alguna secuencia de caracteres textualmente en el [parámetro `lpCommandLine` de` CreateProcess`] (https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#parameters) - - Si te refieres a (1.), entonces esencialmente ya funciona:
PS /home/User> /bin/echo 'cd / && ls . | cowsay'
cd / && ls . | cowsay
PS /home/User>
(Excepto por el problema de las comillas incrustadas como se discute en https://github.com/PowerShell/PowerShell/issues/1995) Se podría argumentar que agregar una cadena de una línea aquí mejoraría algunos casos de uso, pero creo que ese no es realmente el punto de este problema. Como esto ya funciona más o menos, supongo que quisiste decir (2.) --- Si te refieres a (2.), déjame expresar mi opinión al respecto de una manera algo dramática: Por favor, por favor, no agregue sintaxis especial para esto. Esto es básicamente lo que intentó hacer `-%`, que tampoco debería haberse implementado nunca. ¿Por qué estoy tan firmemente en contra de esto? 1. Es un problema exclusivo de Windows, por lo que agregar sintaxis significaría que powershell en Windows tiene una sintaxis diferente a la de Linux. (O la sintaxis sería compatible pero no tiene ningún significado, como es el caso actual de `-%`) 2. Si el shell de línea de comandos principal en Windows publicado por Microsoft agrega una característica de primera clase (a través de una sintaxis especial opuesta a via un cmdlet) para llamar a ejecutables que no siguen las típicas [reglas de análisis de línea de comandos] (https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs -2019 # parsing-c-command-line-parameters) (si la herramienta sigue las reglas típicas, no necesita (2.), generalmente puede usar mejor (1.)), entonces eso alienta a los autores de la línea de comando herramienta, para no seguir estas reglas, lo que solo empeora la ["anarquía de la línea de comandos de Windows"] (https://stackoverflow.com/a/4094897/2770331). Mientras menos personas sigan las reglas típicas, más difícil será llamar programáticamente ejecutables externos o escribir código multiplataforma, por lo que definitivamente creo que se debe alentar a los autores de programas a seguir esas reglas típicas. 3. Dudo mucho que este sea un caso de uso común> Y este no es solo un problema que afecta a WSL: también afecta las invocaciones de la línea de comandos wt de Windows Terminal, y muchas otras herramientas. ¿Podría agregar algunos ejemplos donde ocurren estos problemas? Porque en el caso de WSL, diría que el análisis de WSL de la línea de comandos está simplemente [roto] (https://github.com/Microsoft/WSL/issues/1746) (el problema era sobre `bash.exe` pero la situación es de forma predeterminada, no es mejor con `wsl.exe`) en el caso predeterminado: consideraría todas las herramientas que no siguen las típicas [reglas de análisis de línea de comandos] (https://docs.microsoft.com/en-us/ cpp / cpp / main-function-command-line-args? view = vs-2019 # parsing-c-command-line-parameters) roto, pero el comportamiento predeterminado de WSL es en mi humilde opinión ni siquiera documentado correctamente ... Dije el "predeterminado "El comportamiento de` wsl.exe` está roto - mientras escribía esta respuesta, noté que `wsl.exe` realmente parece comportarse como se esperaba, cuando se usa` -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:\>
Entonces, lo único que falta para este caso de uso explícito es, en mi opinión, un parámetro de `wsl.exe` para llamar al shell predeterminado con los argumentos de la línea de comandos analizados como en cualquier otro exe normal.

Lo único con respecto a "parte de una línea de comandos que se pasará varbatim al comando / script de recepción", que se mejoraría con este operador sería el significado (2.) anterior, y como se mencionó, creo que el desarrollo en esa dirección es malo.

Si todo lo que quiere hacer es un acceso directo, copie y pegue las líneas de comando existentes, ¿por qué no agrega un cmdlet (por ejemplo, Invoke-Shell , que llama a bash -c en Linux y cmd /c en ventanas?
Entonces podrías hacer

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

Si debe ser una línea, agregue la sintaxis de una línea aquí, pero no implemente un operador especial, que no se puede encontrar correctamente y simplemente complica todo.

¡Y no abandones el # 1995 por esto!

+10 puntos por usar la palabra "sigilo".

Lo bueno de &! versus shell es que "shell" es una palabra común en inglés, por lo que la gente puede tener un comando existente llamado shell. (De hecho, tengo una función llamada "shell" y una función llamada "n" (por lo tanto, no me gusta &n ).)

@oising : & --% es bueno, excepto que uno de los propósitos expresos de la característica propuesta es significar que | convierte en algo que se pasa al shell nativo; no es algo que le devuelva al análisis de PowerShell. Y si hacemos que la precedencia de la canalización sea diferente para & --% que para foo $blah --% more args , lo encontraría desconcertante ... ¿y si hiciera & $foo --% more args | blah ? Por lo tanto, en mi opinión, el &! es lo suficientemente diferente como para justificar tener un operador diferente. (Dicho de otra manera: la funcionalidad propuesta está relacionada pero es diferente a la combinación de & y --% ).

Esta característica es más que solo para "nuevos usuarios de PowerShell que conocen bash" y "usuarios que no conocen todas las complejas reglas de citación / escape". También es para los usuarios más expertos, que solo quieren copiar / pegar un comando de StackOverflow sin tener que entrar y jugar con todo. Esto me sucede bastante: un producto en el que trabajo tiene mucha documentación sobre cómo hacer las cosas, y la lengua franca son los comandos cmd.exe, así que tengo que solucionar el problema% VARIABLE_REFERENCES%, etc. .; Sería mucho más agradable escribir &! y luego pegar. De hecho, si esto se implementa, probablemente me referiré a él como "el operador StackOverflow". :RE

@ vexx32 : Este no es el operador de llamada "un anillo para gobernarlos a todos"; simplemente otra herramienta en la caja de herramientas para abordar un problema común. No creo que debamos deshacernos de las herramientas existentes para hacer espacio para esta; hacen cosas diferentes. Entiendo que los nuevos usuarios pueden sentirse abrumados cuando la caja de herramientas tiene un martillo, un destornillador, un taladro, un taladro de impacto y una pistola de clavos ... cosas ", son invaluables para un profesional; todos utilizados en situaciones diferentes (aunque relacionadas).

@TSlivede : Estoy de acuerdo en que # 1995 es independiente. Pero no estoy de acuerdo con "Es un problema exclusivo de Windows": copiar y pegar comandos específicos de la plataforma desde StackOverflow se aplica igualmente a cualquier sistema operativo. (De hecho, esto sería muy útil para muchas personas como yo, que son más fuertes en una plataforma que en otra; soy más competente con las tecnologías de Windows, por lo que cuando estoy en Linux tiendo a copiar y pegar MUCHO desde SO).

Ya sea a través de un nuevo operador &! o algún comando Invoke-Shell , no tengo sentimientos tan fuertes como los demás ... Simpatizo con las personas que se quejan de que los operadores son difíciles de buscar, etc. pero Realmente me gusta la concisión de &! .

@jazzdelightsme esta no es "otra herramienta en la caja de herramientas" tanto como es "otro accesorio de destornillador increíblemente específico para un destornillador que ya tiene alrededor de 3-5 que actualmente ya están _de acuerdo_ y podrían mejorarse" en mi opinión.

@jazzdelightsme La parte "solo para Windows" estaba quizás más relacionada con el problema en el que

Si # 1995 no se abandona y la función solicitada aquí no se resuelve a través de una sintaxis especial sino a través de un comando (o tal vez una nueva sintaxis para una cadena de una línea aquí que funciona en todas partes, no solo para este caso especial), entonces estoy a favor de agregando esta funcionalidad. (O tal vez no "a favor" sino "Esto es totalmente aceptable para mí")

Otra idea: si este problema se trata de copiar y pegar comandos completos que estaban destinados a shells nativos, tal vez PsReadLine podría agregar una función para traducir aproximadamente un comando del estilo bash o cmd a powershell. La traducción puede activarse mediante una combinación de teclas especial o automáticamente si se produce un error de análisis al intentar ejecutar un comando.

Esto tendría la ventaja adicional de enseñar a los usuarios la sintaxis de powershell.

Con respecto a la capacidad de detección: PsReadLine siempre podría mostrar un mensaje durante un breve período de tiempo que indica que se puede usar la combinación de teclas específica, siempre que se pegue contenido sintácticamente incorrecto (que sería válido después de la traducción).

Pero podría entender si esto era un poco complejo de darme cuenta.

@jazzdelightsme ¿ StackOverflow operator que me hizo temblar a pesar de que es muy cierto

@ SteveL-MSFT No, no hablé con nadie sobre esto; Llegué al "operador StackOverflow" de forma independiente. xD

@jazzdelightsme

... excepto que uno de los propósitos expresos de la característica propuesta es significar que | se convierte en algo que se pasa al shell nativo; no es algo que le devuelva al análisis de PowerShell.

No estoy seguro de que hayas entendido mi sugerencia; lo que dices es _precisamente_ lo que estoy diciendo. Permítanme ser más detallado con algunas sugerencias en mayúsculas estratégicas: 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)
@'

¿Que me estoy perdiendo aqui? Si la tubería aparece después de --% es solo otro carácter tonto: un argumento. No se juega con la precedencia de los operadores. Si está contenido en una canalización anidada o subexpresión, comience a analizar para powershell nuevamente.

¿qué pasa si hiciste & $ foo -% más argumentos | ¿paja?

Bueno, eso intentaría ejecutar el argumento en $foo y pasar "more" , "args" , "|" y "blah" para ser tratados como lo que sea en $foo quiere. Las reglas me parecen bastante simples, pero es cierto que he estado usando powershell / monad durante mucho tiempo. Este no es un cambio importante en & ya que & $foo --% ... trata --% como cualquier otro argumento. Entonces, a menos que hayamos estado usando este sigilo en otro lugar de manera nefasta, deberíamos estar a salvo.

Pensamientos

Hice varias ediciones en lo anterior, para cubrir la evaluación de línea parcial, la evaluación de varias líneas y el posicionamiento variable de --% . Todo sin introducir nuevos sigilos, operadores o sintaxis extraña. Creo que eso lo cubre. Lo único es: ¿Puede el analizador manejarlo? ¿Qué opinas @lzybkr @JamesWTruher @BrucePay ?

Escucha, escucha, @TSlivede.

Perdóname por ser franco, pero hemos estado hablando de estos problemas durante mucho tiempo:

Lo que propone este número equivale a un pensamiento mágico:
Está relacionado con el desafortunado, intrínsecamente problemático "símbolo de detener el análisis", --% , que en su forma actual es de uso limitado _en Windows_ (consulte https://github.com/MicrosoftDocs/PowerShell- Docs / issues / 6149) y prácticamente inútil en _Plataformas tipo Unix_ (consulte https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4963).
--% nunca debería haberse implementado, y tampoco esta propuesta debe implementarse tal como se presenta.

El pensamiento mágico es una combinación conceptualmente confusa de dos ideas:

  • (a) "Quiero pasar tokens separados por espacios a algún programa externo, permitiendo cualquier barewords, sin necesidad de preocuparme por los metacaracteres de shell como & o | ." - ver https://github.com/PowerShell/PowerShell/issues/12975#issuecomment -646276628

    • Por supuesto, tal característica está en desacuerdo con el uso de tales comandos _como parte de una tubería_ o con _operadores de encadenamiento de tubería_ ( && y || ), y también colocar comandos adicionales en la misma línea, after ; - y, no menos importante, poder usar cualquier variable y expresión _PowerShell_ en la línea de comando.
  • (b) "Quiero que mi línea de comando se trate de la forma en que estoy acostumbrado _desde el shell que normalmente uso_ (o el shell para el que se escribió originalmente la línea de comando)", que, por supuesto, _está_ sujeto al uso de caracteres sin comillas tales como & o | se interpretan como metacaracteres y, por lo tanto, tienen un significado _sintáctico_.

    • Caso en cuestión: ni cmd ni /bin/sh (o cualquier shell similar a POSIX como bash ) aceptarían wsl ls | less de una manera que pase ls , | , y less _verbatim_ hasta wsl ; todos interpretarían _unquoted_ | como _su_ símbolo de tubería.

Estas ideas están en desacuerdo entre sí y requieren soluciones distintas, ninguna de las cuales requiere una sintaxis especial per se:

  • La solución a (a) es _abandonar_ la idea y, en su lugar, utilizar la sintaxis existente de _PowerShell_ para garantizar que los metacaracteres se utilicen _verbatim_, que invariablemente requiere _citar_ o _escapar_,

    • O bien: cite palabras simples (tokens no entre comillas) que contengan metacaracteres _como un todo_: wsl ls '|' less
    • O: ` -escape de metacaracteres _individualmente_ en palabras simples: wsl ls `| less
    • O: use un _array [variable] _ para pasar los argumentos _verbatim_: $wslArgs = 'ls', '|', 'less'; wsl $wslArgs
    • Por supuesto, # 1995 demuestra que, lamentablemente, este enfoque es y siempre se ha roto cuando se trata de argumentos que se han _embrado_ " _ al llamar a programas externos_ - _esto debe abordarse_: ver más abajo.
  • La solución a (b) es en realidad _ llamar al shell que normalmente uso_, pasando la línea de comando _ como una sola cadena_.

    • Como sugiere @TSlivede , la Invoke-Shell cmdlet al que le pasa la línea de comando _como una sola cadena_ y que llama al _platform-native shell_, y ya tenemos sintaxis para cadenas textuales ( '...' o su variante here-string, @'<newline>...<newline>'@
    • Mi recomendación es llamarlo Invoke-NativeShell , y llamar /bin/sh - no bash - en plataformas similares a Unix.

      • Además, sugiero definir ins como un alias sucinto.

    • Este enfoque resuelve todos los problemas conceptuales con --% y con la propuesta en cuestión:

      • Hace que sea muy obvio dónde termina el comando pasado al shell nativo (en virtud de ser _ una sola cadena_), y permite usar una llamada Invoke-NativeShell / ins en una canalización de PowerShell normal / con Redirecciones de PowerShell sin ambigüedad.

      • Opcionalmente, puede usar una cadena _expandable_ ( "...." o su variante here-string) para incrustar valores de variable _PowerShell_ en la línea de comando (algo que --% no puede hacer).


Dado que los poderes públicos han llegado a la conclusión de que # 1995 no se puede arreglar para que no se rompa la compatibilidad con versiones anteriores, después de todo, todas las soluciones alternativas existentes se romperían, consulte https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606 - el Lo mejor que podemos hacer es apoyar el opt-in en el comportamiento adecuado con una sintaxis que sea lo más sucinta posible, a fin de minimizar el dolor de tener que salir de su camino para obtener el comportamiento que siempre debería haber sido el defecto.

Por lo tanto: una sintaxis especial como &! solo debe proporcionarse como opción para obtener el paso adecuado de argumentos a programas externos para abordar # 1995 , según el comportamiento propuesto en el RFC de @TSlivede ; consulte https://github.com/PowerShell/PowerShell-RFC/pull/90. Si alguna vez se permite que una versión futura de PowerShell rompa la compatibilidad con versiones anteriores, entonces es la invocación / invocación directa con & que debería exhibir este comportamiento.

Desde la perspectiva del usuario, esto significa: Todo lo que necesito hacer es satisfacer los requisitos de sintaxis de _PowerShell_; consulte https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -640711192 - y puedo confiar en PowerShell para pasar de manera robusta los tokens resultantes textuales al programa de destino - este es un mandato central de cualquier shell, que lamentablemente PowerShell nunca ha satisfecho con respecto a los programas externos.

Por ejemplo, el siguiente comando, que actualmente _no_ funciona según lo previsto (cuando se invoca directamente o con & , consulte este problema de documentos ), debería funcionar:

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`

Aquellos que buscan una solución alternativa en las versiones actuales / anteriores de PowerShell pueden encontrar una función simple que cubrirá la mayoría de los casos de uso en https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -303345059 (las limitaciones se tratan en https: // github.com/PowerShell/PowerShell/issues/1995#issuecomment-649560892), y una versión más elaborada en https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606

PD: El problema de descubrimiento de operadores (no solo basados ​​en símbolos) es un problema separado que vale la pena abordar por derecho propio: la solución es _enhance Get-Help para permitir la búsqueda de operadores_ - vea # 11339, que también enlaces a una solución provisional.

Dado que los poderes públicos han llegado a la conclusión de que # 1995 no se puede arreglar para que no se rompa la compatibilidad con versiones anteriores (después de todo, todas las soluciones alternativas existentes se romperían, consulte el # 1995 (comentario)), lo mejor que podemos hacer es respaldar la inclusión en el comportamiento adecuado con una sintaxis que sea lo más sucinta posible, a fin de minimizar el dolor de tener que salir de su camino para obtener el comportamiento que siempre debería haber sido el predeterminado.

@ mklement0 Creo que la https://github.com/PowerShell/PowerShell/issues/1995 y eso fue rechazado. Entonces para mí suena como una ilusión. ¿Me equivoco?

@AndrewSav , originalmente solo discutimos una opción de participación a través de la configuración o la variable de preferencia.

Sin embargo, @joeyaiello dijo lo siguiente en https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -653164987 cuando cerró el problema (énfasis agregado):

Todo lo que toquemos aquí es un cambio demasiado importante, y deberíamos abordar el problema con un nuevo operador.

Un nuevo operador _es_ una especie de opt-in, pero espero que no presente problemas (aunque la necesidad es desafortunada, pero ese es el precio de la compatibilidad con versiones anteriores).

Si bien esta propuesta supuestamente también resuelve # 1995, no lo hace, dado que apunta (principalmente) a acomodar la sintaxis de _otros_ shells. Dado que vale la pena resolver ambos problemas, de forma independiente, mi propuesta fue la siguiente:

  • Introduzcamos un nuevo operador, pero utilícelo únicamente para reparar # 1995 (a través de https://github.com/PowerShell/PowerShell-RFC/pull/90), es decir, para reparar el _own_ de PowerShell (no emulación de otro shell) roto manejo del paso de argumentos a programas externos.

    • El nuevo código siempre debe usar &! (o cualquier forma que decidamos) en lugar de & al llamar a programas externos (puede pensar que está en desaprobación & para programas externos) .
    • Al llamar a los comandos _PowerShell_, &! debería actuar como lo hace & ahora (nunca hubo un problema), permitiendo así el uso consistente de &! .
  • Para resolver el problema de reutilización de líneas de comando de otros shells, implementemos un cmdlet Invoke-NativeShell / ins que llame al shell nativo apropiado para la plataforma ( cmd /c en Windows , /bin/sh -c en plataformas tipo Unix) con la línea de comando pasada como una _single string_.

    • En el caso más simple, sin ' presente en la línea de comando, una cadena regular entre comillas funcionará; p.ej:

      ins 'ls | gato -n '

    • Con ' presentes, pueden ser _doble_; p.ej:

      ins 'echo' 'hola' '| gato -n '

    • o, si el deseo es no tener que tocar la línea de comando en absoluto, se puede usar una cadena aquí (como ya lo muestra @TSlivede); aunque esta sintaxis es un poco más engorrosa, siempre debería funcionar:

      En s @'
      echo 'hola' | gato -n
      '@

    • Además, al usar una cadena _expandable_ (interpolando) ( "..." o @"<newline>...<newline>"@ ), tiene la opción de incorporar variables _PowerShell_ en la cadena pasada al shell nativo (algo que --% no es compatible):

      $ foo = 'hola'
      En s @"
      echo '$ foo' | gato -n
      "@

    • Nota:

      • Invoke-NativeShell cmdlet sería una envoltura bastante delgada alrededor de cmd /c / /bin/sh -c llamadas, que obviamente podría hacer directamente usted mismo, pero tiene la ventaja de que no es necesario Conozca el nombre del ejecutable específico y la sintaxis de paso de la línea de comandos.
      • Como efecto secundario potencialmente beneficioso, podrá utilizar parámetros comunes como -OutVariable , y podrá pensar si debe permitir que los parámetros -Error* actúen en _stderr_ (aunque, por simetría, Invoke-Command debería entonces, lo que sería un cambio radical).

        • Dado que los shells tipo POSIX admiten _múltiples_ argumentos con -c , el primero constituye el código a ejecutar y los siguientes son los argumentos para pasar a ese código, comenzando con $0 (!) - por ejemplo, bash -c 'echo $@' _ 1 2 3 - también se deben admitir argumentos adicionales opcionales.

        • Dado que se está llamando al shell nativo de la plataforma, tales llamadas son, por definición, _no_ portátiles, debido a las diferencias fundamentales entre cmd y sh .Esta es la razón por la que es tan importante corregir el paso de argumentos _own_ de PowerShell (# 1995), ya que luego proporciona un método confiable y verdaderamente multiplataforma para invocar programas externos.


La última pieza del rompecabezas es la documentación.

https://github.com/MicrosoftDocs/PowerShell-Docs/issues/5152 es una propuesta de larga data para escribir un tema de ayuda conceptual sobre llamadas a programas externos (por ejemplo, about_Native_Calls ), que debería, en una sola ubicación, cubrir _todos_ los aspectos relevantes para la convocatoria de programas externos; Aparte de lo que ya se cubre allí (errores de sintaxis debido a metacaracteres adicionales, ausencia de una canalización de bytes sin procesar, no integración con el manejo de errores de PowerShell, ...), los problemas en cuestión también deben discutirse:

  • Cómo el paso de argumentos con " incrustados (y argumentos _empty_) se desglosa en (con suerte, pronto) versiones anteriores (que se discuten actualmente en una propuesta separada, https://github.com/MicrosoftDocs/PowerShell-Docs / issues / 2361), y cómo solucionarlo ( \ -escaping manual o uso de una de las funciones auxiliares de # 1995, una de las cuales es lo suficientemente sucinta como para incluirse directamente en el tema).

  • Cómo solo se debe usar &! futuro para llamar a ejecutables nativos (externos), a fin de garantizar el paso correcto de los argumentos.

  • Cómo se puede usar Invoke-NativeShell / ins para ejecutar líneas de comando _ escritas para el shell nativo de la plataforma_ tal cual.

Interesante, los usuarios de hoy pueden:

  1. invocación directa
  2. & operador
  3. Proceso de inicio

No está del todo claro que los usuarios deban utilizar.

Después de corregir el comportamiento roto, los usuarios tendrán las siguientes características:

  1. invocación directa
  2. & operador
  3. Proceso de inicio
  4. &! operador
  5. Inicio-ProcesoV2

¿No es demasiado para el shell ejecutar ping 127.0.0.1 ?

Sí, esa es mi principal preocupación al agregar otro operador también @iSazonov. Ya es confuso y esto no ayudará. La documentación es solo una parte del rompecabezas. Incluso si tenemos documentación decente, el mero hecho de que haya _ cinco formas diferentes_ de hacerlo, cada una de las cuales se comporta de manera un poco diferente, siempre causará confusión.

Si nos importa la UX en absoluto, deberíamos optar por mejorar los casos existentes en lugar de agregar aún más.

Si nos importa la UX en absoluto, deberíamos optar por mejorar los casos existentes en lugar de agregar aún más.

Lo cual no puede porque debe ser compatible con versiones anteriores. Siento que vamos en círculos. https://github.com/PowerShell/PowerShell/issues/1995 fue eso y fue cerrado por esa misma razón.

@iSazonov :

La falta de orientación existente debe abordarse incluso si no presentamos un nuevo operador:

Para resumir brevemente:

  • No use Start-Process para invocar programas _console_ (terminal) (a menos que, que solo está disponible en Windows, desee ejecutarlos en una nueva ventana).

  • La invocación directa y la invocación a través de & son _equivalentes_; (dejando a un lado los bloques de secuencia de comandos), & solo se necesita por razones _sintácticas_, siempre que el nombre o la ruta del comando esté _citado_ y / o contenga _ referencias variables_.


Después de corregir el comportamiento roto, los usuarios tendrán las siguientes características:
invocación directa

  • & operador
  • Proceso de inicio
  • &! operador
  • Inicio-ProcesoV2
  • No, no habrá un Start-ProcessV2 , solo un _parámetro nuevo_, -IndividualArguments ; consulte https://github.com/PowerShell/PowerShell/issues/5576#issuecomment -552124719

  • Sí, habrá &! (o cualquier símbolo que decidamos), pero efectivamente _sustituye tanto & como la invocación directa_ (al menos para _ programas externos_, pero podrás usarlo con _todos_ los formularios de comando), que deberían ser fáciles de comunicar.

¿No es demasiado para el shell ejecutar ping 127.0.0.1 ?

ping 127.0.0.1 seguirá funcionando, pero si desea que bash -c 'echo "one two"' funcione, tendrá que usar
&! bash -c 'echo "one two"'

Eso es lamentable, pero, dado que la invocación directa / comportamiento de & _no se puede arreglar_ - me parece la mejor solución.

Afortunadamente, es una regla simple de memorizar: _Si desea pasar argumentos predecibles a programas externos, use &! _

Las formas alternativas de opt-in - configuración (a través de powershell.config.json ) o una variable de preferencia - son mucho más problemáticas:

  • El alcance dinámico de las variables de preferencia hace que sea demasiado fácil aplicar inadvertidamente la suscripción al código heredado que no fue diseñado para él; consulte https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -650262164

  • La configuración se aplicaría a las sesiones en su conjunto, lo que haría que la exclusión voluntaria fuera completamente imposible.

  • En ambos casos, se necesita una acción que esté _separada_ de una llamada específica, y mirar una llamada determinada no le dirá cómo se comportará; por el contrario, &! no deja dudas sobre qué comportamiento se producirá.

Para el usuario final, parece tan terrible como si hubiéramos lanzado una calculadora y encontráramos allí 5 botones para la operación de suma. 😄

Si queremos mejorarlo tanto que estemos listos para tales complicaciones, entonces quizás esto sea suficiente para aceptar un cambio rotundo (para invocación directa y & ) para simplificar el comportamiento y lanzar V8.0.

Si nuestro deseo no es tan fuerte, es posible mejorar suficientemente los diagnósticos ( prog.exe param1 param2 -Whatif y lo mismo para Start-Process -WhatIf) para que muestre lo que el programa testargs muestra y recomendaciones sobre cómo para realizar correctamente el escape.

El desarrollador de scripts puede darse el lujo de pasar tiempo probando y usando el depurador. Pero en una sesión interactiva, cualquier usuario quiere simplicidad, brevedad y claridad, y esto lo supera todo.

@iSazonov :

entonces tal vez esto sea suficiente para aceptar un cambio importante (para invocación directa y &) para simplificar el comportamiento y lanzar V8.0

Ciertamente tiene _mi_ voto, pero ¿apoyan los poderes este plan ? [_update_ - ver # 13129]
Si tomamos un cambio de esta magnitud, finalmente podremos abordar todo el bagaje histórico - ver # 6745

Si queremos mejorarlo tanto

Hay dos buenas razones para quererlo tanto:

  • Para poner fin a años de dolor debido al paso de argumentos fallidos, como lo demuestra # 1995, muchos problemas relacionados e innumerables preguntas de Stack Overflow.

  • Porque, como se ha dicho varias veces antes, llamar a programas externos con argumentos es un mandato fundamental de cualquier shell.

Históricamente, en los días de solo Windows, la escasez de CLI externas capaces hacía que la imposibilidad de llamarlos correctamente fuera menos problemática, y los usuarios que se quedaban en el jardín amurallado de PowerShell (llamando solo a comandos nativos de PowerShell) no sentían el dolor. Pero los tiempos seguro han cambiado:

Vivimos en la era de una gran cantidad de CLI multiplataforma que son cruciales para las tareas de desarrollo, CI e implementación.
Si PowerShell quiere que lo tomen en serio como shell, este problema debe abordarse.


Incluso si se produce una compatibilidad con versiones anteriores permitidas para romper con la versión 8.0 (dedos cruzados), su lanzamiento podría tardar mucho tiempo, y _también_ necesitamos una solución provisional.

Por mucho que se necesite la mejora propuesta a la documentación, no creo que por sí sola alivien suficientemente el dolor, por falta de una solución de tiempo de ejecución fácilmente disponible.

Entiendo que el operador es feo, pero una solución que no se rompa _necesita_ sintaxis adicional (más para escribir), sea cual sea su forma.

Una alternativa a &! que es menos fea, más fácil de escribir y no "contamina" el lenguaje es enviar una función_ incorporada con un nombre conciso, como la función iep (I nvoke- E xternal p ro) que oculta todos los problemas, que se muestra aquí .
(Tenga en cuenta que ya enviamos funciones que envuelven otras funciones (por ejemplo, pause ) o se adaptan a los usuarios de cmd.exe (por ejemplo, cd.. [sic])).

Por lo tanto, para que bash -c 'echo "one two"' funcione correctamente, llamaría iep bash -c 'echo "one two"'
O, para usar quizás un escenario más realista:

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

Nuevamente, en última instancia, estamos hablando de un _compromiso_ en el servicio para preservar la compatibilidad con versiones anteriores; esto es inevitablemente una molestia para los usuarios finales.


mejorar los diagnósticos (prog.exe param1 param2 -Whatif y lo mismo para Start-Process -WhatIf) para que muestre lo que muestra el programa testargs y recomendaciones sobre cómo realizar correctamente el escape.

Dado que la invocación directa / & -based no es una llamada de cmdlet y se supone que todos los argumentos son argumentos de transferencia, tratar -WhatIf como un parámetro común equivale a un cambio importante (aunque es poco probable que está en la práctica).

Si está dispuesto a aceptar un cambio de este tipo, también podríamos (también) admitir un cambio -DoItRight (o uno basado en símbolos, similar a --% ) como opción para el comportamiento fijo, y eso me parece peor que el enfoque iep -prefix / &! .

(Supongo que una opción de diagnóstico inquebrantable sería mejorar Invoke-Command , que actualmente no admite -WhatIf ; sin embargo, si proporcionamos una opción bien documentada para el comportamiento, no creo que valga la pena hacerlo).

Estoy de acuerdo con @iSazonov en que la situación no es ideal para introducir otra cosa más para hacer algo que se supone que funciona, pero como lo mencionó @ mklement0, esperar un lanzamiento de última hora que solucione correctamente esto podría ser un tiempo muy, muy largo, y muchos de nosotros necesitamos desesperadamente una solución lo antes posible. Por todo lo que puedo ver, la mejor solución actualmente es usar
https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606 ¿verdad?

@ mklement0 No estoy seguro de cómo podría funcionar un programa iep / invoke-external, ya que un cmdlet está sujeto al mismo parámetro que estamos tratando de corregir. Este es un problema del analizador, ¿verdad? Si intentáramos ocultar todo dentro de un cmdlet / función, me imagino que sería horriblemente complejo dado que tendría que deshacer la intención de munging / reconstruir. ¿O me estoy perdiendo algo?

@oising :

Para ser claros: la función vinculada iep corrige # 1995, no de lo que se trata principalmente _esta_ propuesta (que creo que _no_ vale la pena perseguir, como se argumentó anteriormente ), aunque tenga en cuenta que la intención declarada en el OP es _también_ resuelve # 1995.

1995 se trata de poder confiar en la sintaxis de _PowerShell_ (no en la de otro shell) para pasar argumentos a programas externos _ fielmente, como lo ve literalmente PowerShell después de su propio análisis_.

El propio análisis de PowerShell está bien y no hay problemas siempre que pase argumentos a los comandos _PowerShell_.

Por el contrario, pasar argumentos a _programas externos_ está muy mal, y eso es lo que corrige iep .

Está roto debido a _recitar y escapar_ defectuosos de los argumentos _ detrás de escena_, cuando se construye la línea de comando utilizada finalmente.
https://github.com/PowerShell/PowerShell-RFC/pull/90 propone una solución adecuada (sin acuerdo sobre cómo manejar la compatibilidad con versiones anteriores, y con algunas preguntas menores aún por responder), que en esencia se basa en el introdujo recientemente System.Diagnostics.ProcessStartInfo.ArgumentList para realizar el cambio de cotización y el escape adecuados para nosotros (solo pasamos la colección de argumentos que resultaron del propio análisis de PowerShell), que, por cierto, solo se requiere _en Windows_; en Unix, sensiblemente, _no hay línea de comando_ (fuera de un shell) cuando pasa argumentos al inicio del proceso: simplemente pasa los argumentos textualmente, como una matriz.

En breve:

  • Proporcionar iep como función incorporada sería una opción provisional de baja ceremonia para ofrecer el paso de argumentos adecuados a programas externos, hasta que se pueda implementar una solución adecuada, en el contexto de un cambio importante

  • Un cmdlet Invoke-NativeShell ( ins ) es la solución adecuada, no una solución provisional, para proporcionar una forma de llamar a las líneas de comando de _un shell diferente_ (el shell nativo), utilizando la sintaxis de _su_ - ver arriba .

Actualmente, hay casos en los que cortar y pegar un comando PERL no se ejecuta como se esperaba en Python. Imagínate. Esto debe ser una gran desventaja para todos los cortadores y pegadores que hay. Creo que Python debería tener un operador especial para eso.

No estoy seguro de que hayas entendido mi sugerencia; lo que dices es _precisamente_ lo que estoy diciendo. Permítanme ser más detallado con algunas sugerencias en mayúsculas estratégicas: 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 $_ }

¿Que me estoy perdiendo aqui? Si la tubería aparece después de --% es solo otro carácter tonto: un argumento. No se juega con la precedencia de los operadores. Si está contenido en una canalización anidada o subexpresión, comience a analizar para powershell nuevamente.

Pensamientos

Lo que falta es la necesidad de pasar un paréntesis de cierre como argumento a un programa externo. Es un enfoque DWIM típico, pero PowerShell no es HAL.

Gracias a todos por todos los comentarios detallados. Hablando como yo mismo aquí. Algunos súper puntos antes de entrar más en el meollo de la cuestión:

  • Todavía no estoy a bordo con un vNext / 8.0 que decide romper una nueva letanía de cosas. La compatibilidad con versiones anteriores es importante. Sé que es doloroso y prefiero vivir en un mundo en el que podamos volver atrás y corregir cada error que haya cometido PowerShell, muchos de ellos involuntarios. Pero la gran mayoría de los usuarios de PS no están aquí en problemas, hasta las rodillas en los casos extremos de SO. Han copiado / pegado la misma solución alternativa a algo durante 4 a 10 años, no necesariamente saben por qué funciona, y no les importará que "corrijamos errores" cuando su script se rompe en la actualización. Simplemente saben que "actualizar PS me ha roto y no puedo confiar en las actualizaciones", y luego terminamos con más usuarios que ralentizan sus actualizaciones y su adopción.
  • Creo que algunas personas en este problema pueden estar proyectando muchas esperanzas y deseos diferentes en este problema, o lo ven como una opción o con otras soluciones. Me doy cuenta de que exacerbé eso al cerrar # 1995, y lo siento por eso (aunque diré que todavía no estoy seguro de que uno específicamente se pueda arreglar, pero lo analizaremos más detenidamente), pero esto es realmente una solución de "trampilla" que saca a los usuarios nuevos y experimentados de un aprieto rápidamente sin dejar que la frustración aumente. Todavía podemos tener conversaciones a más largo plazo sobre arreglos en el lado de análisis de PS de la casa para que podamos ofrecer una solución verdaderamente multiplataforma para todo.

Ahora, comenta mientras leo:

  • Soy cauteloso sobre el deseo propuesto de alinearse con #! (con &! o $! dado que, en el caso predeterminado, es probable que los usuarios no especifiquen el intérprete . Si quiero hacer algo como $! net <stuff> , net.exe no es mi intérprete. Y en los que no son de Windows, la cosa "nativa" es pasar a sh / bash / lo que sea antes pasando al binario que se está ejecutando. Por ejemplo, no quiero que Python analice *.py en la cadena $! /usr/bin/python *.py o incluso $! python *.py . Quiero que Bash haga el globbing y luego a mano la lista de coincidencias con python .
  • Como sugirió @oising , estoy a favor de --% como operador, sobrecargado para usarse en el frente. La capacidad de descubrimiento es un problema, y ​​dado que ya llevamos 10 años trabajando en la ahora es compatible con Google!, Digo que simplemente lo hagamos (yo fui el que hizo @ SteveL-MSFT ponerlo en su propuesta como una consideración alternativa). Sin embargo, pregunta: ¿necesitamos siquiera el operador de llamada & aquí? ¿Por qué no comenzar la línea con --% native.exe do /some $stuff ?
  • @rjmholt : No entendí totalmente tu comentario aquí , pero creo que probablemente no deberíamos cambiar nada con el comportamiento actual de --% donde viene después del ejecutable, pero creo que podemos hacer tu "nuevo comportamiento "con donde viene antes del ejecutable.
  • Lea esto de @jazzdelightsme y estoy de acuerdo enfáticamente:

    También es para los usuarios más expertos, que solo quieren copiar / pegar un comando de StackOverflow sin tener que entrar y jugar con todo.

    En mi opinión, este es un aspecto enorme del escenario, posiblemente el más grande. No es solo SO, tampoco, son documentos de toda la web que suponen que estás en un shell similar a Bash.

  • Independientemente de si cosas como 1995 se arreglan, el punto es que a) hay una larga lista de casos extremos extraños como ese, yb) son realmente difíciles de arreglar sin romper a la gente. No es necesariamente cierto para todos ellos, pero lo hemos acertado lo suficiente en los últimos dos años que es evidente para mí que necesitamos una "trampilla" como esta para sacar a las personas de situaciones difíciles en las que no quieren sumergirse. el agujero del conejo de nuestra anidada escape (sólo para descubrir que hay un error como # 1995, que les impide trabajar incluso una vez que saben lo que están sabiendo).

Al revisar los comentarios, creo que hay dos preguntas abiertas que aún debemos responder:

  1. ¿Seguimos analizando los separadores de comandos y todo lo que sigue a ellos en PowerShell, o el separador de comandos también se pasa literalmente al shell nativo?
    Digo que deberíamos pasar todo literalmente al shell nativo, ya que actualmente es algo que no se puede hacer, y que la solución de @oising de un paréntesis puede ser la solución en el sentido de que todavía podemos admitir tuberías, cadenas de tuberías, etc. .si desea mezclar / combinar análisis nativo y PS (aunque quiero obtener algunas opiniones de quienes están más cerca del analizador).
  2. ¿Qué es el operador?
    Como se mencionó anteriormente, me gusta --% porque ya tiene la reputación de ser el operador "simplemente haz lo que solía hacer", y tendremos que restablecer la capacidad de descubrimiento con algo nuevo. Entiendo argumentos como @ mklement0 de que, si hacemos esto, estamos cambiando el significado técnico de --% (y @ SteveL-MSFT también me ha hecho este argumento), pero no creo la mayoría de la gente en la naturaleza piensa en --% en términos de "hace lo que solía hacer en cmd". No creo que sea exagerado codificar esta suposición en la definición del operador como "hacer lo que hace el shell nativo aquí".

Ortogonalmente, solo pensé en otro escenario que podríamos querer o no apoyar.

En Windows, cmd (efectivamente) incluye el directorio actual en su búsqueda PATH . Sin embargo, PowerShell requiere que el usuario anteponga un ./ para ejecutar cualquier EXE (o cualquier cosa en PATHEXT ) si el directorio actual no está en PATH .

Entonces, mi pregunta abierta es: ¿realmente queremos entregar toda la cadena de comando a cmd? Propondría que si realmente queremos iluminar el escenario de copiar / pegar con este operador, la respuesta es sí, ya que algunos documentos / tutoriales de terceros no necesariamente detallan cómo colocar las herramientas que esperan que use. en el PATH . Probablemente no importe mucho de cualquier manera, pero quería enumerarlo aquí.

  • Todavía no estoy a bordo con un vNext / 8.0 que decide romper una nueva letanía de cosas.

Sí, no sé cómo solucionar estos problemas sin intervenir en formas increíblemente difíciles de solucionar. Además, no sé ustedes, pero ni siquiera yo quiero tener que hacer malabarismos con todas estas complicadas diferencias entre carpetas y analizadores al escribir para varias versiones.

Por lo general, si estoy discutiendo en contra de cambios importantes, es para otras personas. Sin embargo, parece que probablemente sería un PITA con el que lidiar incluso para las personas presentes en ITT.

  • Como sugirió @oising , estoy a favor de --% como operador, sobrecargado para usarse en el frente. (...) ¿Por qué no empezar la línea con --% native.exe do /some $stuff ?

Realmente me gusta esto. Idealmente, lo más cerca posible de llamar a CreateProcess / exec (más el manejo de nix env var y la redirección de entrada / salida o el adjunto de la consola). O tal vez retrocediendo a cmd.exe / bash completamente.

Tampoco estoy atascado en mantener & frente a --% . Puedo justificar la idea de tratar el crudo --% como una directiva / operador en mi cabeza al menos. ¿Y multilínea? Permitir --% con @" y "@ para sustituciones y subexpresiones var, y comillas simples @' y '@ para pasar literal? Sin las cadenas here, ¿es solo una línea?

Como sugirió @oising , estoy a favor de -% como operador

Funciona para mi.

Sin embargo, pregunta: ¿necesitamos siquiera el operador & call aquí?

Siempre que pueda hacer --% & $computedPathToNativeExe foo;@workspace entonces estoy bien. Necesitamos poder usar el operador de llamada con esto.

Digo que deberíamos pasar todo literalmente al shell nativo, ya que actualmente es algo que no puedes hacer.

Convenido.

No creo que sea exagerado codificar esta suposición en la definición del operador (-%) como "hacer lo que hace el shell nativo aquí".

¡También de acuerdo!

Además, tal vez esta sea una forma de ingresar a la función para definir env vars antes del ejecutable que se definen solo para la sesión del ejecutable (# 3316):

--% JEKYLL_ENV=production jekyll build

Solo un pensamiento.

@rkeithhill , la idea con call native operator es que la línea no sea procesada por PowerShell y enviada literalmente al "shell predeterminado" del sistema operativo. Entonces, en este caso, su primer ejemplo --% & $computerPathToNativeExe foo;@workspace no funcionaría. Tendría que usar el método actual y escapar según corresponda. El segundo caso --% JEKYLL_ENV=productoin jekyll build debería funcionar.

PowerShell no procesa la línea y la envía literalmente al "shell predeterminado" del sistema operativo

OK, eso parece razonable. Sin embargo, este --% JEKYLL_ENV=productoin jekyll build solo funcionaría en Linux / macOS pero no en Windows, al menos no sin la ayuda de PowerShell, ¿verdad?

@rkeithhill, como se propone actualmente, nunca funcionará en Windows, ya que se basa en el shell "predeterminado" del sistema operativo. Para PowerShell declarando env var para un proceso, tenemos un problema diferente abierto para discutir eso.

@ PowerShell / powershell-Committee discutió esto hoy. Actualicé la propuesta original para reflejar el resultado de esa discusión. Avanzaremos con la implementación de una función experimental y obtendremos más comentarios de los usuarios con código de trabajo. Todavía estamos abiertos en el sigilo para el operador, pero estamos usando --% por ahora.

donde $foo es un nombre de archivo literal y $PWD es el directorio actual dentro de WSL. Tenga en cuenta que en el primer ejemplo, debería saber escapar && para que se ejecute dentro de WSL en lugar de dentro de PowerShell.

Tanto $foo como $PWD son referencias variables para ser evaluadas por sh .

Para canalizar la salida de dicha ejecución a PowerShell, el usuario debe almacenar los resultados en una variable primero:

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

Tenga en cuenta que, a diferencia del operador de llamada actual & , no puede usar ninguna sintaxis de PowerShell, por lo que:

--% $commandline

intentará ejecutar $commandline literalmente (por debajo de cmd ) o después de la expansión del shell (por debajo de sh ). PowerShell no intentará resolver esa variable.

El problema de cortar y pegar se resuelve simplemente pegando después de escribir &n .

simplemente pegando después de --%

El ejemplo anterior para wsl se vería así:

¿Cúal?

Dado que todos los argumentos después de --% se pasan al shell "predeterminado", para admitir la canalización en PowerShell, necesitaría usar una variable:

Esta es una repetición.

Si vamos a añadir algo útil os propongo un Pivot.

¿Por qué no podemos tener algo así como un literal heredero?

@! ""! @

Entonces, podemos tener una forma real de hacer esto en lugar de un -% que también tiene su propia serie de problemas.

Al menos de esa manera podemos enterrar el problema con tácticas de diseño "mejores".
Y la capacidad de descubrimiento para que estos casos extremos se ubiquen en About_Strings, donde debería resolverse.

Solo un pensamiento.

No veo cómo @!"…"!@ es mejor que Invoke-NativeShell @"…"@ . ¿Estamos tratando de hacer que PowerShell sea más parecido a Brainfuck?

@ yecril71pl

No veo cómo @!"…"!@ es mejor que Invoke-NativeShell @"…"@ . ¿Estamos tratando de hacer que PowerShell sea más parecido a Brainfuck?

Creo que @ romero126 pretendía sugerir un literal de herestring en línea, de modo que uno pudiera escribir

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

en vez de

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

Si se considerara una cadena de contenido en línea de este tipo, sugeriría usar una sintaxis como @"""…"""@ y @'''…'''@ , esto sería IMO más fácil de escribir que @!"…"!@ .


@ romero126 ¿Podrías aclarar lo que quisiste decir?

@ yecril71pl gracias por detectar esos errores, actualizó el mensaje superior con correcciones.

No se especifica si --% se ejecutará como línea de comando o como un archivo por lotes.

Creo que @ romero126 pretendía sugerir un literal de herestring en línea, de modo que uno pudiera escribir

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

en vez de

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

Si se considerara una cadena de contenido en línea de este tipo, sugeriría usar una sintaxis como @"""…"""@ y @'''…'''@ , esto sería IMO más fácil de escribir que @!"…"!@ .

@ romero126 ¿Podrías aclarar lo que quisiste decir?

Creo que si agregamos flexibilidad a Strings para manejar datos sin analizar "casi sin procesar", no tendríamos problemas con esta ventana emergente.
@TSlivede Estoy de acuerdo casi completamente con lo que dijiste. Solo con una ligera variación. Debe poder manejar AMBAS líneas simples y multilínea sin interpolar nada dentro de las comillas. Cualquiera que sea la sintaxis en la que aterricemos para que eso suceda, estoy abierto. Estoy más centrado en el concepto.

Con estos ejemplos, agrega mucha flexibilidad en la que no necesito "Invoke-NativeShell", ya que solo devolvería una cadena. Lo que significa que somos libres de manipularlo como lo haría una cuerda. De modo que esto potencialmente podría convertirse en un elemento básico de lo que parece genial.

Un pivote como este también significaría que no tendríamos que lidiar con los problemas anteriores en los que giramos nuestras ruedas en una casi solución cuando podemos manejar un caso de borde, en lugar de abordar el problema como un todo.

Ejemplos rápidos:

# 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

Eso no aborda el problema de la cubierta de @ mklement0 en la segunda parte de este comentario . El hecho del asunto es que no podemos simplemente pasar una sola cadena a cada comando; en muchos casos hay que dividirlos.

Creo que si agregamos flexibilidad a Strings para manejar datos sin analizar "casi sin procesar", no tendríamos problemas con esta ventana emergente.

Para esto son ' y @' .

Con estos ejemplos, agrega mucha flexibilidad en la que no necesito "Invoke-NativeShell", ya que solo devolvería una cadena. Lo que significa que somos libres de manipularlo como lo haría una cuerda. De modo que esto potencialmente podría convertirse en un elemento básico de lo que parece genial.

Los comandos nativos devuelven una secuencia de cadenas. @!" tiene el potencial de convertirse en un elemento básico de lo que parece ininteligible.

Creo que si agregamos flexibilidad a Strings para manejar datos sin analizar "casi sin procesar", no tendríamos problemas con esta ventana emergente.

Para esto son ' y @' .

Con estos ejemplos, agrega mucha flexibilidad en la que no necesito "Invoke-NativeShell", ya que solo devolvería una cadena. Lo que significa que somos libres de manipularlo como lo haría una cuerda. De modo que esto potencialmente podría convertirse en un elemento básico de lo que parece genial.

Los comandos nativos devuelven una secuencia de cadenas. @!" tiene el potencial de convertirse en un elemento básico de lo que parece ininteligible.

Creo que estás confundido. Con el punto que estaba haciendo.
Las comillas simples y las comillas dobles solo expanden $ variable o no. No escapa automáticamente a los caracteres. También me limita, por lo que ya no puedo usar su carácter definitorio. Entonces, en cambio, para que funcione correctamente como está ahora. ¡Todavía tendría que escapar de los personajes, que es el punto de por qué necesitábamos incluir un &! Mando.

Si tienes una mejor solución soy todo oídos. Creo que esto afecta a más casos extremos que agregar un &!

Las comillas simples y las comillas dobles solo expanden $ variable o no. No escapa automáticamente a los caracteres. También me limita, por lo que ya no puedo usar su carácter definitorio. Entonces, en cambio, para que funcione correctamente como está ahora. ¡Todavía tendría que escapar de los personajes, que es el punto de por qué necesitábamos incluir un &! Mando.

¿Qué personajes tendrías que escapar y por qué?

@ romero126 Ahora estoy confundido con lo que dices. ¿Sabes que PowerShell tiene una cadena aquí ?

Anteriormente pensé que podría considerar la sintaxis de powershells aquí cadenas molestas (al menos a veces me molesta que tenga que haber una nueva línea al principio y al final ...), pero ahora no estoy tan seguro de qué estás diciendo, ¿podrías aclarar?

@TSlivede , estuvo de acuerdo en que aquí las cadenas son sintácticamente algo engorrosas.

Existe una propuesta, # 2337, que propone mejoras, y si bien se centra en permitir que el delimitador de cierre sea sangrado, @lzybkr también propone una variación _single-line _ (- también) similar a Rust en https: // github. com / PowerShell / PowerShell / issues / 2337 # issuecomment -391152107, lo que nos permitiría hacer lo siguiente, por ejemplo:

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

La necesidad de escapar se evitaría combinando _multiple_ ' con @ , según sea necesario (por ejemplo, @'' can use @' here ''@ .

En otras palabras: esto sería una mejora general de los literales de cadena de PowerShell, utilizable en todas partes, de la que Invoke-NativeShell se beneficiaría.

Dado que el número 2337 se centra principalmente en otra cosa, he creado una propuesta centrada en el número 13204 y sugiero que continuemos la conversación allí.

Se evitaría la necesidad de escapar

Lo he visto un par de veces en este hilo.

Cualquier indicador al tokenizador para comenzar un nuevo modo necesitaría un mecanismo de escape, ya que necesita diferenciar el indicador del tokenizador de que está dejando ese modo de la forma literal de ese carácter.

La forma más natural de eso hoy en día son las cadenas de comillas simples:

  • ' : iniciar la tokenización de comillas simples
  • ' : finalizar la tokenización de comillas simples
  • '' : escribe una comilla simple literal dentro de una cadena de comillas simples

Creo que la razón por la que estamos hablando de escapar a pesar de esto es:

  • El operador --% define demasiadas formas de salir de su modo ( | , && , || , ; , nueva línea) y permite cadenas dentro de él (quitando sus caracteres ' )
  • Heretrings y co se resuelven en un argumento de cadena en lugar de una matriz de ellos

Querer pasar argumentos a una invocación ejecutable significa que en realidad estamos hablando de una sintaxis de matriz:

  • Cómo iniciar la matriz (iniciar el modo literal)
  • Cómo separar la matriz (pasar un solo argumento al nuevo ejecutable)
  • Cómo finaliza la matriz (finaliza el modo literal)

(Este es un concepto nativo en * nix, ya que exec espera una matriz. En Windows, creo que nos vemos obligados a tomar una matriz, serializarla en una sintaxis de cadena en particular y pasarla a través de CommandLineToArgvW . Sin embargo, .NET proporciona una abstracción basada en matrices para ambos, por lo que las matrices tienen aún más sentido)

Eso significa que necesitamos dos escapes:

  • El literal del separador de matriz
  • El literal de fin de matriz

Por ejemplo, digamos:

  • --% antes de que el comando se convierta en la nueva sintaxis literal
  • ¿Cómo terminamos la sintaxis? nueva línea y ; ¿quizás?
  • ¿Cómo separamos los argumentos? ¿tal vez?

Las preguntas entonces son:

  • ¿Cómo pasamos un ; literal o una nueva línea?
  • ¿Cómo pasamos un literal?

Una línea de comando nativa no es una matriz. Es una cadena o un AST. Dado que PowerShell no sabe nada sobre AST en shells nativos, solo queda una cadena.

Una línea de comando nativa no es una matriz. Es una cadena o un AST

No ha proporcionado ningún fundamento para esta afirmación.

Aquí es donde PowerShell construye el AST para un comando (de cualquier tipo, ya que no sabe hasta el tiempo de ejecución cuando el comando resuelve qué tipo de comando es):

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

Observe que los elementos de comando (incluidos los parámetros y argumentos) se agregan a una matriz.

Puede ver esto en acción en PowerShell de esta manera:

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

Esa matriz se compila en expresiones aquí:

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

Y luego eso se pasa a una invocación de canalización aquí (observe que Expression.NewArrayInit(typeof(CommandParameterInternal[]), pipelineExprs) es donde se pasan los argumentos):

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

En tiempo de ejecución, cuando se ejecutan las expresiones compiladas, eso se convierte en esta llamada de método , que pasa a esta llamada que decide qué procesador de comandos usar:

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

Finalmente, cuando se llama a la canalización, esa matriz de argumentos se vincula al comando nativo usando NativeCommandParameterBinder :

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

Eso toma la matriz de argumentos, que incluye metadatos AST como qué tipo de cadena se usó para cada argumento, y construye una nueva cadena para la invocación de argumentos en el NativeCommandProcessor aquí:

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

Esto se hace porque la API .NET que usamos actualmente para la invocación de procesos es la versión anterior de "todos los argumentos en una sola cadena", en lugar de la nueva en .NET Core que le permite pasar una matriz y permite que .NET reconstruya la invocación. según sea necesario de una manera específica de la plataforma.

Pero usar esa API es un detalle de implementación. Por ejemplo, podríamos llamar a fork / exec directamente en * nix, como hacemos aquí:

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

Tenga en cuenta que exec aquí toma una matriz; lo hicimos porque es la forma más simple y directa de pasar argumentos a la llamada al sistema, que es en última instancia el objetivo de una sintaxis de argumento textual.

@rjmholt :

_Funcionalmente_, todas las necesidades ya están cubiertas por las formas de cadena literal existentes, _si pasa la línea de comando nativa como una sola cadena_, lo cual recomiendo encarecidamente, a través de un nuevo cmdlet Invoke-NativeShell / ins .

El punto de partida aquí es como _ una sola cadena_, no una matriz: es una _ línea de comandos completa_ - para ser analizada _ por el shell de destino_ - y tiene más sentido reflejar esto en PowerShell como tal - en lugar de tratar de calzar torpemente un sintaxis de shell diferente en PowerShell con algo como --%

Todo lo que tiene que hacer Invoke-NativeShell es pasar la única cadena que contiene toda la línea de comandos del shell nativo al shell nativo a través de su CLI:

  • en Unix , esto significa: pasar la cadena tal cual, _como todo_, como el segundo elemento del argumento _array_ pasado a exec (siendo -c el primer elemento de la matriz de argumentos, y /bin/sh el ejecutable); expresado en términos .NET con un ejemplo simple, donde printf "'%s'" foo | cat -n es el contenido literal de la cadena única que contiene la línea de comando para pasar (tenga en cuenta el uso de .ArgumentList , no .Arguments ; la duplicación de ' s es un artefacto de este ejemplo únicamente):

    • $psi = [Diagnostics.ProcessStartInfo]::new('/bin/sh'); $psi.ArgumentList.Add('-c'); $psi.ArgumentList.Add('printf "''%s''" foo | cat -n'); [Diagnostics.Process]::start($psi).WaitForExit()
  • en Windows , esto significa: para adaptarse a las peculiaridades de cmd , complete el argumento lpCommandLine de CreateProcess siguiente manera (asumiendo que lpApplicationName es el valor de variable de entorno ComSpec , que apunta a cmd.exe ): "/c " + cmdLine , donde cmdLine es la cadena completa de la línea de comandos tal como está; expresado en términos .NET con un ejemplo simple, donde echo a^&b & ver es el contenido literal de la cadena única que contiene la línea de comando para pasar:

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

      • @ yecril71pl , esto implica la respuesta a su pregunta sobre si se debe usar la semántica del símbolo del sistema o del archivo por lotes: es la primera, como también la usan otros lenguajes de scripting, como Python con os.system() ; la implicación (quizás desafortunada) es que no podrá escapar % caracteres, y que las variables de ciclo for deben usar un solo % (por ejemplo, %i lugar de %%i ); y que debe prefijar los comandos de cuerpo de bucle con @ para evitar que se repitan (por ejemplo, for %i in (*.txt) do <strong i="56">@echo</strong> %i ).

Este proceso será _transparente para el usuario final_, por lo que todo lo que necesitan enfocarse es pasar la línea de comando de shell nativo _verbatim_, solo necesitando satisfacer los requisitos de sintaxis de cadena literal de _PowerShell_:

Con el formulario ins @'<newline>...<newline>'@ , ya _ puede_ usar sus líneas de comando de shell nativo tal como están: vea más arriba cómo se pueden usar las diversas formas de cadena literal existentes, lo cual, debido a la disponibilidad de la _interpolación_ aquí- La forma de cadena ( @"<newline>...<newline>"@ ) también: incluye la capacidad de usar variables y expresiones _PowerShell_ en la línea de comando (la incapacidad de hacerlo es una de las principales deficiencias de --% ).

Lo que propone mi comentario anterior es una _nueva forma general de literal de cadena sin formato_, que está _ separada de esta propuesta_ (y su lógica de análisis está (con suerte) bien definida); aunque, como se indicó, las llamadas Invoke-NativeShell serían entonces _más convenientes_.

En lugar de lo que ya puede hacer (asumiendo un comando Invoke-NativeShell / ins ):

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

con el nuevo literal sin formato de una sola línea propuesto, podría simplificar a:

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

Si es necesario debatir los detalles de esta nueva sintaxis literal de cadena sin formato, por favor, hagámoslo en el # 13204, no aquí.

Entonces, releyendo la propuesta para comprender la intención original, veo que en realidad solo está pidiendo una mejor sintaxis para /bin/sh -c '<command>' + la versión cmd, junto con una lógica de escape de cadena especial para cada shell de destino. Basé mi concepción inicial en una sintaxis especial y en la necesidad de trabajar con el analizador y el procesador de comandos nativo, y en que la propiedad ProcessStartInfo.Arguments .NET es muy difícil de usar correctamente en términos de paso de cotizaciones .

Pero, de hecho, la cadena Arguments acaba de pasar literalmente . Entonces, todo lo que tenemos que hacer es pasar dicha cadena directamente.

En ese caso, creo que cualquier sintaxis nueva es totalmente innecesaria (solo complicará la lógica de tokenización y sospecho que confundirá a la gente) y, en cambio, este es solo un nuevo comando, ins como usted dice. .NET tiene algo de lógica para esto aquí, que nos veremos obligados a volver a implementar, pero está bien, ya que tenemos que ser un poco más inteligentes de todos modos.

Algunos comentarios sobre el comentario de @rjmholt https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -660231641

Cualquier indicador al tokenizador para comenzar un nuevo modo necesitaría un mecanismo de escape, ya que necesita diferenciar el indicador del tokenizador de que está dejando ese modo de la forma literal de ese carácter.

Estoy de acuerdo y no creo que una sintaxis de cadena en línea pueda evitar por completo la necesidad de escapar.

El operador -% define demasiadas formas de salir de su modo (|, &&, ||,;, nueva línea) y permite cadenas dentro de él (eliminando sus 'caracteres)

Cuando se habla de --% , ¿se refiere al conmutador --% ? Si es así, es mejor dejarlo claro en su comentario, ya que ahora se propone que --% sea ​​un operador de llamadas.

Querer pasar argumentos a una invocación ejecutable significa que en realidad estamos hablando de una sintaxis de matriz:

La propuesta es llamar al shell nativo (sh o cmd) y pasar todo literalmente después del operador de llamada --% , como sh -c ... . Entonces, no es PowerShell el que pasa los argumentos al ejecutable de destino. En ese caso, creo que no necesitamos _una sintaxis de matriz_, ¿verdad?

¿Cómo terminamos la sintaxis? nueva línea y; ¿tal vez?
¿Cómo separamos los argumentos? ¿'espacio' tal vez?

Creo que el análisis sintáctico termina en una nueva línea. Para pasar una nueva línea como parte del argumento, use \n supongo, tal como lo hace en bash directamente.

Una línea de comando nativa no es una matriz. Es una cadena o un AST

No ha proporcionado ningún fundamento para esta afirmación.

Aquí es donde PowerShell construye el AST para un comando (de cualquier tipo, ya que no sabe hasta el tiempo de ejecución cuando el comando resuelve qué tipo de comando es):

Si esta propuesta se implementa, introducirá una construcción especial en la que PowerShell no construirá el AST en absoluto.

Me alegro de escucharlo, @rjmholt.

.NET tiene algo de lógica para esto aquí, que nos veremos obligados a volver a implementar

No creo que realmente necesitemos reimplementar nada, como muestran los comandos en mi comentario anterior: en Windows, use .Arguments para pasar la línea de comandos completa a WinAPI; en Unix, use .ArgumentList para construir el argumento _array_ que se usa directamente con exec .

Si esta propuesta se implementa, introducirá una construcción especial en la que PowerShell no construirá el AST en absoluto.

Bueno, construiría un tipo de AST diferente, uno diseñado para este propósito. Si no es parte del AST, es básicamente lo mismo que un comentario, no pasa nada.

_Funcionalmente_, todas las necesidades ya están cubiertas por las formas de cadena literal existentes, _si pasa la línea de comando nativa como una sola cadena_, lo cual recomiendo encarecidamente, a través de un nuevo cmdlet Invoke-NativeShell / ins .

Personalmente, me gusta la idea del cmdlet Invoke-NativeShell , en algunos aspectos mejor que el operador de llamada --% . La principal preocupación que escuché sobre esta idea es que es más escribir que --% + Ctrl + vy diferenciar la cadena de aquí literal de la interpolación de cadena de aquí es otra carga para el usuario (_ personalmente creo que ins es mucho más fácil de escribir que --% _).

La principal preocupación que escuché sobre esta idea es que es más escribir que --% + Ctrl + vy diferenciar la cadena de aquí literal de la interpolación de la cadena de aquí es otra carga para el usuario (_ personalmente creo que ins es mucho más fácil de escribir que --% _).

Quizás PSRL pueda ayudar. Similar al problema sobre un controlador de claves para rodear rutas pegadas, PSRL podría leer CommandAst , ver que es ins y escapar correctamente de la cadena pegada.

Bueno, construiría un tipo de AST diferente, uno diseñado para este propósito. Si no es parte del AST, es básicamente lo mismo que un comentario, no pasa nada.

Seguro, pero el árbol será trivial. No reflejará el significado de los comandos que se ejecutarán.

Inicie el proceso cmd.exe redirigir stdin / stdio y literalmente podemos usar una cadena para invocar-NativeCommand lo mismo con bash

Me alegro de escucharlo, @ daxian-dbw.
Me encanta la idea, @SeeminglyScience .

diferenciar la cadena aquí literal de la interpolación de la cadena aquí es otra carga para el usuario

Creo que es un _plus_ porque, a diferencia de --% , el uso de @"<newline>...<newline>"@ permite incorporar directamente los valores de las variables y expresiones _PowerShell_ en la línea de comandos nativa.

Por supuesto, eso supone que los usuarios conocen la diferencia entre cadenas de comillas simples (textuales) y de comillas dobles (interpolación) y sobre el escape selectivo de ` de $ , pero creo que es un importante opción (avanzada) para tener.

Para ayudar a los usuarios de cortar y pegar, la función PSReadLine propuesta debe ser @'<newline>...<newline>'@ (literalmente).


En última instancia, no queremos promover una mentalidad en la que los usuarios no necesiten conocer y comprender los requisitos de sintaxis únicos de PowerShell; en cambio, queremos promover la comprensión de ellos, a la luz del _poder añadido que aportan_.

Pero para animar a los usuarios a utilizar la sintaxis _ propia_ de PowerShell, que por definición es _entre plataformas_, debe funcionar de forma predecible .
Con ese fin, # 1995 debe arreglarse , idealmente directamente, permitiendo un cambio radical o, si eso no es una opción, mediante una ceremonia mínima _opt-in_.

Para resumir:

  • Algo como --% (el nuevo formulario de operador propuesto aquí), por definición una característica _específica de la plataforma_, no sustituye de ninguna manera a la corrección # 1995 .

  • Invoke-NativeShell ( ins ) evita todas las limitaciones del operador --% y proporciona la solución de "trampilla" que mencionó @joeyaiello : una forma rápida de ejecutar una línea de comando escrita para el shell nativo, sin tener que adaptarlo a la sintaxis de PowerShell.

  • --% debe permanecer en su forma _argumento_ original únicamente, y debe seguir siendo una característica _sólo para Windows_ (que _efectivamente_, aunque no _técnicamente_ lo es); en otras palabras: no se necesitan cambios.

    • Una vez que se corrige # 1995, el único propósito de --% será acomodar _ casos de borde en Windows_: permitiéndole controlar las citas explícitas de la línea de comando que se pasan a programas maliciosos como msiexec que requieren formas muy específicas de citación intraargumentosa.

Una de las otras características que realmente quiero en PS v7.x es la capacidad de hacer que un comando nativo salga con un código de error que detenga mi script, como set -e como se explica en este RFC . Si la solución es algo así como Invoke-NativeCommand , ¿se volvería confuso con Invoke-NativeShell ?

Solo intento reproducir la película un poco hacia adelante porque, por mucho que quiera la función nativa de llamada, realmente quiero que mis scripts de compilación y prueba salgan con errores al llamar a msbuild, cl, cmake, Conan, npm, etc. y esos comandos nativos salen con un código de salida distinto de cero sin tener que acordarse de marcar $ LASTEXITCODE todo el tiempo. Si la implementación es a través de otra variable de preferencia, por ejemplo, $PSNativeCommandInErrorActionPreference entonces supongo que eso afectaría la invocación del shell nativo, ¿es un "comando" nativo y todo?

La principal preocupación que escuché sobre esta idea es que es más escribir que --% + Ctrl + vy diferenciar la cadena de aquí literal de la interpolación de la cadena de aquí es otra carga para el usuario (_ personalmente creo que ins es mucho más fácil de escribir que --% _).

Quizás PSRL pueda ayudar. Similar al problema sobre un controlador de claves para rodear rutas pegadas, PSRL podría leer CommandAst , ver que es ins y escapar correctamente de la cadena pegada.

Pero que pasa:

$c = gcm ins
& $c ...

PSRL va a tener dificultades para reconocer lo que está sucediendo allí. Tendría que tener estado, resolver el alias ins para invoke-nativeshell, luego averiguar que desea llamar al commandinfo y luego eliminar los argumentos. Realmente, realmente no me gusta la idea de poner una orden especial en mayúsculas y minúsculas.

diferenciar la cadena aquí literal de la interpolación de la cadena aquí es otra carga para el usuario

Creo que es un _plus_ porque, a diferencia de --% , el uso de @"<newline>...<newline>"@ permite incorporar directamente los valores de las variables y expresiones _PowerShell_ en la línea de comandos nativa.

No te sigo @ mklement0. ¿Por qué no se puede enseñar a hacer esto a --% ? Ya di esto como un caso de uso.

Realmente, realmente no me gusta la idea de poner una orden especial en mayúsculas y minúsculas.

Aquí no hay un comando falso, solo un cmdlet genuino, Invoke-NativeShell , con alias de ins .

gcm ins ( Get-Command ins ) proporcionará información sobre el alias ins en forma de una instancia [System.Management.Automation.AliasInfo] , que luego puede pasar a & para la invocación, como con cualquier otro comando o alias.

Por lo tanto, su ejemplo funcionará exactamente como se esperaba, es solo que si usa el desvío gcm ( Get-Command ), tendrá que deletrear la sintaxis de cadena literal apropiada _yourself_, sin la _Andamio de comando de conveniencia opcional_ que PSRL _ podría_ proporcionar, si se implementa la sugerencia de @SeeminglyScience :

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

O, si se implementa la nueva sintaxis literal de cadena sin formato propuesta en # 13204:

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

Por supuesto, si está familiarizado con la sintaxis de los literales de cadena de PowerShell, una cadena entre comillas simples también funcionará: simplemente duplique el ' incrustado dentro de la cadena '...' :

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

¿Por qué no se puede enseñar a hacer esto a --% ?

  • --% carece de un delimitador de cierre, lo que impide su uso en tuberías de Powershell, lo cual es una restricción inaceptable (tenga en cuenta que (...) enclosure es _no_ una opción, porque eso implica una recopilación inicial de toda la salida líneas en la memoria en lugar de procesar _streaming_).

  • En Unix, --% no puede _ambos_ admitir (sin escape) $ como metacaracteres de PowerShell _y_ como metacaracteres de shell POSIX.

Nuevamente: no hay justificación ni beneficio para calzar una sintaxis de shell diferente en PowerShell: pase la línea de comando de shell nativo como una cadena a ins , y todos los problemas conceptuales desaparecerán.

Invoke-NativeShell implica que PowerShell no es el shell nativo. ¿Es esa suposición siempre correcta y siempre lo será?

@oizar el caso de uso es que alguien copie y pegue un comando nativo de un archivo README o algo así. Si están haciendo algo más complicado que escribir ins SPACE CTRL + v, entonces simplemente construirán y escaparán su propia cadena como de costumbre.

@ mklement0

Pero para animar a los usuarios a utilizar la sintaxis _ propia_ de PowerShell, que por definición es _entre plataformas_, debe funcionar de forma predecible .
Con ese fin, # 1995 debe arreglarse , idealmente directamente, permitiendo un cambio radical o, si eso no es una opción, mediante una ceremonia mínima _opt-in_.

Esto es independiente de la discusión sobre ins / --% ¿verdad? Si no está de acuerdo, ¿puede aclarar por qué ese problema afectaría al nuevo comando / sintaxis?

@SeeminglyScience , sí, está separado, pero eso no ha quedado claro en este hilo.
La única razón por la que # 1995 apareció aquí es que originalmente se cerró a favor de este problema (aunque afortunadamente se volvió a abrir desde entonces), y el OP aquí todavía afirma que este problema lo solucionará ("Esto también debería resolver estos problemas:") .
Por lo tanto, quería enfatizar que ins / --% _no_ abordaría # 1995, que requiere su propia solución, con urgencia.
(Solo si el propio paso de argumentos de PowerShell funciona correctamente, podemos en buena conciencia recomendar que los usuarios aprendan y adopten completamente la sintaxis de PowerShell, lo cual deberíamos).

@ SP3269 , podemos cruzar ese puente cuando lleguemos 😁.

Pero en serio:

Si piensa en "nativo" como "escrito específicamente para la plataforma de host [familia]", el nombre aún encajaría, incluso si PowerShell alguna vez se convirtiera en el shell del sistema predeterminado en una plataforma determinada (aquí hay esperanza, pero no creo eso es realista, al menos no en el futuro previsible).

En cuanto a alternativas:

  • Invoke-[System]DefaultShell sería el nombre más preciso (aunque sin ambigüedades ya no se aplicaría si PowerShell alguna vez se convirtiera en el shell predeterminado del sistema), pero "nativo" parece ser el término más utilizado en el mundo de PowerShell.

  • Invoke-LegacyShell tiene alguna justificación en Windows, pero no creo que los usuarios de Unix se tomen demasiado amablemente con ese término.

Creo que estoy empezando a desentrañar la total confusión que estoy experimentando al tratar de hacer un seguimiento de los motivos, ideas y problemas de todos aquí. Parece que nosotros ( @ mklement0 y yo, al menos) estamos viendo esto como dos soluciones diferentes a dos visiones diferentes del mismo problema conceptual. Lo que estaba sugiriendo era usar --% como una sugerencia para el _parser_ para cambiar la forma en que se analizan las cosas junto con la llamada & operator (no necesariamente usando --% standalone). Por otro lado, Michael parece estar mirando ins como un cmdlet desplegable para reemplazar el propio intermediario de comandos nativo interno de powershell y el analizador de argumentos, y no busca cambiar el analizador de powershell, sino que usa cadenas / aquí. -cadenas para capturar los parámetros previstos (que estoy diciendo que también se podrían usar con --% ).

-% carece de un delimitador de cierre, lo que evita su uso en tuberías de Powershell

Este punto no lo entiendo en absoluto. No le falta más un delimitador de cierre que ins / invoke-nativecommand. Si necesita delimitar o alimentar desde el LHS, use here-strings, de lo contrario, se considerará como una sola declaración.

Independientemente de la forma que se elija, parece que será necesario elegir un shell nativo para enviar la línea de comandos. Sugiero que usemos las variables de entorno COMSPEC en Windows (el valor predeterminado es %systemroot%\system32\cmd.exe ) y SHELL en Linux (el valor predeterminado es /bin/bash ) - si SHELL no lo hace ' Si existe, deberíamos apuntar a /bin/sh (que en la mayoría de los casos es un enlace simbólico a bash de todos modos).

-% carece de un delimitador de cierre, lo que evita su uso en tuberías de Powershell

Este punto no lo entiendo en absoluto. No le falta más un delimitador de cierre que ins / invoke-nativecommand. Si necesita delimitar o alimentar desde el LHS, use here-strings, de lo contrario, se considerará como una sola declaración.

Básicamente, no puede llamar a cosas como cmd --% /c someapp | someOtherApp y hacer que cmd maneje la canalización; PowerShell aún interpreta una canalización (y algunas otras cosas como && y || que de otro modo serían útiles para la gente de Unix) como un token de PowerShell en lugar de pasarlo al comando nativo como parte de la cadena de argumentos.

y SHELL en Linux (el valor predeterminado es /bin/bash )

No: el _system_ shell en plataformas tipo Unix es _invariablemente_ /bin/sh .
Esto es distinto de un shell interactivo de _user_ dado (reflejado en $env:SHELL - pero, desafortunadamente, actualmente no si _PowerShell_ es el shell del usuario, ver # 12150), que es (a) configurable y (b) a menudo _defaults to_ /bin/bash , pero ni siquiera eso es un hecho, como lo demuestra macOS que recientemente hizo la transición a /bin/zsh como el shell interactivo predeterminado para nuevos usuarios.

Sugiero que usemos las variables de entorno COMSPEC en Windows

Buen punto, esa es la mejor manera de referirse al shell del sistema (intérprete de comandos) en Windows; he actualizado el comando de muestra anterior en consecuencia.

Una de las otras características que realmente quiero en PS v7.x es la capacidad de hacer que un comando nativo salga con un código de error que detenga mi script, como set -e como se explica en este RFC . Si la solución es algo así como Invoke-NativeCommand , ¿se volvería confuso con Invoke-NativeShell ?

Solo intento reproducir la película un poco hacia adelante porque, por mucho que quiera la función nativa de llamada, realmente quiero que mis scripts de compilación y prueba salgan con errores al llamar a msbuild, cl, cmake, Conan, npm, etc. y esos comandos nativos salen con un código de salida distinto de cero sin tener que acordarse de marcar $ LASTEXITCODE todo el tiempo. Si la implementación es a través de otra variable de preferencia, por ejemplo, $PSNativeCommandInErrorActionPreference entonces supongo que eso afectaría la invocación del shell nativo, ¿es un "comando" nativo y todo?

Tenemos Start-NativeExecution en nuestro módulo build para ese propósito.

Eso lo he visto. ¿Cómo combinaría exactamente Start-NativeExecution con Invoke-NativeShell si desea que la última versión arroje un código de salida distinto de cero? Realmente espero que algo como PR # 3523 lo haga junto con esta función nativa de llamada porque quiero que los dos funcionen juntos. Supongo que si la llamada nativa termina siendo implementada como un cmdlet (Invoke-NativeShell vs -%), entonces -ErrorAction Stop podría implementarse para que arroje (error de terminación) en un código de salida distinto de cero.

-% carece de un delimitador de cierre, lo que evita su uso en tuberías de Powershell

Este punto no lo entiendo en absoluto. No le falta más un delimitador de cierre que ins / invoke-nativecommand. Si necesita delimitar o alimentar desde el LHS, use here-strings, de lo contrario, se considerará como una sola declaración.

Básicamente, no puede llamar a cosas como cmd --% /c someapp | someOtherApp y hacer que cmd maneje la canalización; PowerShell aún interpreta una canalización (y algunas otras cosas como && y || que de otro modo serían útiles para la gente de Unix) como un token de PowerShell en lugar de pasarlo al comando nativo como parte de la cadena de argumentos.

Sí, entiendo el comportamiento actual @ vexx32 . Pero no estamos (al menos no lo estoy) hablando del comportamiento actual de --% , estamos hablando de mejorarlo, ¿no? ¿Por qué tengo la sensación de que todos estamos hablando entre nosotros aquí? :)

Como dije, muy arriba, podríamos mejorar & para trabajar con --% por lo que esto es posible . También creo que debemos ser claros sobre el exec nativo implícito versus la invocación explícita de un shell con argumentos. Esta es una fuente constante de confusión, incluso para los usuarios experimentados de Windows, que de alguna manera se necesita cmd.exe (un shell) para "ejecutar" otros ejecutables; lo mismo se aplica a linux / osx.

Sí, entiendo el comportamiento _ actual_ @ vexx32 . Pero no estamos (al menos no lo estoy) hablando del comportamiento _ actual_ de --% , estamos hablando de mejorarlo, ¿no? ¿Por qué tengo la sensación de que todos estamos hablando entre nosotros aquí? :)

Entonces, el tono actual para ajustar --% es que si es donde normalmente estaría el nombre del comando, todo a la derecha se analiza como está (hasta una nueva línea) y se envía directamente a bash / cmd. No hay espacio para ninguna sintaxis de PowerShell como here-strings y qué no porque entonces tiene los mismos problemas que --% y el procesador de comandos nativo que tienen actualmente.

Eso lo he visto. ¿Cómo combinaría exactamente Start-NativeExecution con Invoke-NativeShell si desea que la última versión arroje un código de salida distinto de cero? Realmente espero que algo como PR # 3523 lo haga junto con esta función nativa de llamada porque quiero que los dos funcionen juntos. Supongo que si la llamada nativa termina siendo implementada como un cmdlet (Invoke-NativeShell vs -%), entonces -ErrorAction Stop podría implementarse para que arroje (error de terminación) en un código de salida distinto de cero.

Actualmente será Start-NativeExecution { Invoke-NativeShell <strong i="10">@whatever</strong> } .

Este punto no lo entiendo en absoluto. No le falta más un delimitador de cierre que ins / invoke-nativecommand. Si necesita delimitar o alimentar desde el LHS, use here-strings, de lo contrario, se considerará como una sola declaración.

Invoke-NativeShell no _necesita_ ningún delimitador de cierre porque es un comando regular, mientras que el horror de --% no lo es.

y SHELL en Linux (el valor predeterminado es /bin/bash )

No: el _system_ shell en plataformas tipo Unix es _invariablemente_ /bin/sh .

Creo que la cosa debería ser equivalente a hacer un archivo de script ejecutable y ejecutarlo, lo que delega la tarea de elegir el shell correcto al sistema operativo y no deberíamos molestarnos. Incluso, si comienza con #! , que así sea.

La idea detrás de Invoke-NativeShell es proporcionar una envoltura conveniente alrededor de _CLI_ del shell nativo.

  • En Unix, eso es totalmente suficiente, y la creación de un archivo de script auxiliar no solo introduce una sobrecarga adicional, sino que luego informa un valor aleatorio en $0 (la ruta del archivo de script), mientras que la invocación a través de la CLI contiene, como era de esperar, /bin/sh e incluso se puede establecer explícitamente siguiendo el argumento de código con otro argumento (por ejemplo, sh -c 'echo $0' foo )

    • (Aparte: crear un script de shell con la línea shebang #!/bin/sh significa apuntar al shell del sistema por la ruta completa tan explícitamente como invocar /bin/sh directamente).
  • En Windows, la ejecución a través de un archivo por lotes _ traería ventajas (pero no para delegar la tarea de ubicar el shell del sistema, lo que $env:ComSpec predeciblemente hace), ya que evitaría los problemas con el escape de % y el comportamiento de for mencionado anteriormente .

Para abordar este último como una cortesía (para que los usuarios no tengan que escribir sus propios archivos auxiliares por lotes), para mayor claridad conceptual, podríamos ofrecer una
-AsBatchFile cambiar como _opt-in_.

Dicho esto, si hay consenso en que la mayoría de las líneas de comando públicas cmd utilizadas para cortar y pegar están escritas con la semántica de _batch-file_ en mente ( %%i lugar de %i como una variable de bucle, capacidad para escapar de un % literalmente como %% ), quizás invariablemente usando un auxiliar El archivo por lotes _en Windows_ (si bien me quedo con la CLI en Unix) es la mejor solución; no estoy muy convencido de esto, pero tendría que estar claramente documentado, especialmente dado que significa exhibir un comportamiento diferente al de otros lenguajes de scripting.

@rkeithhill

Para mí, vale la pena abordar el RFC de integración de errores nativos al que se vincula con la misma urgencia que en el número 1995:

Ambos representan pasos necesarios para hacer de los programas externos (utilidades nativas) ciudadanos de primera clase en PowerShell (tanto como sea conceptualmente posible), lo que siempre debería haber sido.

Ser un ciudadano de primera clase significa: invocación _directa_ (con & necesarios solo por razones _sintácticas_, situacionalmente), no _mediante un cmdlet_.
En otras palabras: nunca debería haber un cmdlet Invoke-NativeCommand o un cmdlet Start-NativeExecution .

(Aparte: Start-NativeExecution tiene un nombre incorrecto, ya que muchas funciones en el módulo build son; Start señales de operación _asincrónica_, mientras que la mayoría de estas funciones son _sincrónicas_; consulte la discusión sobre Start-Sleep en https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4474).

Por lo tanto, Invoke-NativeShell no requiere consideración especial: PowerShell, como ya lo hace, establecerá $LASTEXITCODE función del código de salida informado por el proceso ejecutable de shell nativo ( cmd / sh ) invocado.

Entonces, el mecanismo propuesto en el RFC actuará sobre él, ya que actuaría sobre ejecutables invocados directamente (por ejemplo, si se establece $PSNativeCommandErrorAction = 'Stop' , se produciría un error de terminación del script si $LASTEXITCODE no es cero. )

(Un comentario rápido: esta discusión no pertenece aquí: en cuanto a un mecanismo _por llamada_: algo como
/bin/ls nosuch || $(throw 'ls failed) funciona, al igual que /bin/ls nosuch || $(exit $LASTEXITCODE) (el equivalente a /bin/ls nosuch || exit un shell POSIX); # 10967 explica por qué lamentablemente no podemos evitar $(...) (sin cambios importantes en la gramática); de manera similar, probablemente no se pueda evitar la necesidad de hacer referencia explícita a $LASTEXITCODE ).

(Aparte: crear un script de shell con la línea shebang #!/bin/sh significa apuntar al shell del sistema por la ruta completa tan explícitamente como invocar /bin/sh directamente).

Eso no es lo que quería decir. Quería decir que PowerShell no necesita saber cuál es el shell predeterminado porque los sistemas operativos tienen esta función incorporada. Linux sabe cómo ejecutar un script ejecutable en caso de que no comience con #! o comience con algo más, como #!/usr/bin/env python y Windows sabe qué hacer para llamar a un script .CMD , por lo que no deberíamos echar un vistazo a %COMSPEC% 'n'stuff tampoco en mi humilde opinión. Windows también puede ejecutar otros scripts, pero se basa en la extensión del archivo del script, por lo que obviamente no se aplica a este caso.

Nuevamente, el mandato de Invoke-NativeShell debería ser llamar al _CLI_ del shell nativo, no crear archivos de script auxiliares (con efectos secundarios) detrás de escena (excepto por diferentes razones, como se discutió anteriormente ).

Con respecto a la determinación robusta del shell del sistema, no hay ningún problema que resolver aquí: codificar /bin/sh en Unix es perfectamente apropiado, al igual que consultar $env:ComSpec en Windows.

No, las plataformas Unix a nivel de llamada al sistema _no_ saben cómo ejecutar un archivo ejecutable de texto plano _sin una línea shebang_; que todavía pueda _ invocar tales archivos es una característica conveniente de los shells tipo POSIX: intentan exec , luego _ retroceden_ para interpretar tales archivos como escritos _ para ellos_; es decir, es cualquier shell similar a POSIX que se está ejecutando el que termina ejecutando el archivo, mientras que PowerShell simplemente _falla_, porque no implementa este respaldo de cortesía (obtendrá Program 'foo' failed to run: Exec format error ).

No hace falta decir que, por lo tanto, no es aconsejable crear dichos scripts.

No, las plataformas Unix a nivel de llamada al sistema _no_ saben cómo ejecutar un archivo ejecutable de texto plano _sin una línea shebang_; que todavía pueda _ invocar tales archivos es una característica conveniente de los shells tipo POSIX: intentan exec , luego _ retroceden_ para interpretar dichos archivos como escritos _ para ellos_; es decir, es cualquier shell similar a POSIX que se está ejecutando el que termina ejecutando el archivo, mientras que PowerShell simplemente _falla_, porque no implementa este respaldo de cortesía (obtendrá Program 'foo' failed to run: Exec format error ).

csh no es similar a POSIX y no falla en exec un script simple. Invoca sh . También perl .

Espero que sea obvio que esto demuestra mi punto: depende de cada shell decidir qué hacer, y diferentes shells / lenguajes de scripting hacen cosas diferentes; Actualmente, PowerShell simplemente falla: si tuviera que tomar una decisión y usted quisiera que esa opción fuera /bin/sh , volvemos al punto de partida: se debe suponer /bin/sh .

Espero que sea obvio que esto demuestra mi punto: depende de cada shell decidir qué hacer, y diferentes shells / lenguajes de scripting hacen cosas diferentes; Actualmente, PowerShell simplemente falla: si tuviera que tomar una decisión y usted quisiera que esa opción fuera /bin/sh , volvemos al punto de partida: se debe suponer /bin/sh .

exec in perl es una llamada directa al sistema, perl no lo modifica de ninguna manera. En particular, no _elige_ al intérprete.

Por supuesto, eso supone que los usuarios conocen la diferencia entre cadenas entre comillas simples (textuales) y comillas dobles (interpoladas) y sobre el ` -escaping selectivo de $ , pero creo que es un importante opción (avanzada) para tener.

Tal vez sea solo yo, pero cambiaría mil " por uno -f .

@ yecril71pl

exec en perl es una llamada directa al sistema, perl no lo modifica de ninguna manera. En particular, no elige al intérprete.

Eso no puede ser cierto si funciona con scripts sin la línea #! . El exec syscall en Linux no funciona sin #! , ya que se puede probar fácilmente:

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$ 

Editar: Acabo de notar que si usa la función de biblioteca execlp , de acuerdo con la página del manual:

Si no se reconoce el encabezado de un archivo (el intento de ejecución (2) falló con el error ENOEXEC), estas funciones ejecutarán el shell (/ bin / sh) con la ruta del archivo como primer argumento. (Si este intento falla, no se realizan más búsquedas).

Sin embargo, esta no es una característica de "linux" sino de la biblioteca gnu c, y de nuevo, de acuerdo con la página del manual, usa explícitamente / bin / sh y no un shell definible por el usuario. (Codificado en la fuente de posix/execvpe.c ( new_argv[0] = (char *) _PATH_BSHELL; ) y sysdeps / unix / sysv / linux / paths.h (o sysdeps / generic / paths.h - no sé qué encabezado es usado ...) ( #define _PATH_BSHELL "/bin/sh" ))

Sin embargo, esta no es una característica de "linux" sino de la biblioteca gnu c, y de nuevo, de acuerdo con la página del manual, usa explícitamente / bin / sh y no un shell definible por el usuario. (Codificado en la fuente de posix/execvpe.c ( new_argv[0] = (char *) _PATH_BSHELL; ) y sysdeps / unix / sysv / linux / paths.h (o sysdeps / generic / paths.h - no sé qué encabezado es usado ...) ( #define _PATH_BSHELL "/bin/sh" ))

Dado que proviene de sysdeps/**/linux , es una cosa de Linux. También puede ser algo genérico, pero es reemplazable, obviamente no localmente sino por sistema operativo.

Sí, entiendo el comportamiento _ actual_ @ vexx32 . Pero no estamos (al menos no lo estoy) hablando del comportamiento _ actual_ de --% , estamos hablando de mejorarlo, ¿no? ¿Por qué tengo la sensación de que todos estamos hablando entre nosotros aquí? :)

Entonces, el tono actual para ajustar --% es que si es donde normalmente estaría el nombre del comando, todo a la derecha se analiza _ como está_ (hasta una nueva línea) y se envía directamente a bash / cmd. No hay espacio para ninguna sintaxis de PowerShell como here-strings y qué no porque entonces tiene los mismos problemas que --% y el procesador de comandos nativo que tienen actualmente.

Oh Dios ... ¿Todos están tratando de engañarme? 😄 No, Patrick, si quieres usar expresiones de PowerShell, usa here-strings con --% . Si desea multilínea, use here-strings. Si desea multilínea sin sustitución de variables, puede usar cadenas de aquí de comillas simples.

De todos modos, no tengo ningún aspecto en este juego y creo que ya he dicho suficiente. Sigo pensando que hay algo muy malo en usar un cmdlet para diferir a otro shell desde dentro de un shell. Es simplemente torpe. Es especialmente torpe cuando está utilizando un cmdlet para ejecutar otro shell para ejecutar un comando nativo que no necesita ese shell secundario en primer lugar. Todo huele mal.

editar: última palabra

El título de este artículo es "llamar al operador nativo". Veo una oportunidad perdida cuando ya tenemos un operador de llamada & y ya tenemos un token que fue diseñado para lidiar con el paso de argumentos a comandos nativos, --% . En este momento, el uso de & con --% NO se reconoce y, como tal, NO introducirá un cambio importante si decidimos habilitar este escenario y darle un comportamiento específico para solucionar los problemas que se discuten aquí.

si desea utilizar expresiones de PowerShell, utilice here-strings con --% . Si desea multilínea, use here-strings.

El punto del operador es que no analiza literalmente ninguna sintaxis de PowerShell. No existen las cadenas aquí. No existen las constantes de cadena entre comillas simples. Es "enviar el texto a la derecha como está", no "evaluar la expresión de la derecha, convertir a cadena y enviar su valor"

Entonces, por ejemplo, si tuvieras esto:

--% @'
echo my native command
'@

se traduciría a:

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

Excepto que obtendría un error del analizador porque '@ sería solo el comienzo de una única constante de cadena entre comillas que contiene @ .

Creo que tal vez te estás imaginando la implementación de manera diferente para que sea más o menos lo mismo que el tono de ins , pero lo anterior es el tono actual que se está discutiendo para --% .

También vale la pena señalar que así es como funciona la funcionalidad existente. Este ejemplo funciona básicamente de la misma manera:

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

Repito:

En este momento, el uso de & con -% NO se reconoce / indefinido y, como tal, NO introducirá un cambio importante si decidimos habilitar este escenario y darle un comportamiento específico para solucionar los problemas que se discuten aquí .

Permítanme deletrearlo aún más claro: Permitir que here-strings sigan --% cuando se usan con &

Hah, eso es un gran oof, sí, me perdí eso, lo siento @oising.

Acerca de esa idea, creo que sería un poco confuso que --% signifique dejar de analizar en algunos contextos y "hacer un mejor trabajo en la vinculación de argumentos nativos" en otros (de la misma manera con hacer que funcione como Invoke-NativeShell si eso es lo que quieres decir).

Gracias por investigar más profundamente sobre exec , @TSlivede. Permítanme intentar resumir y, por tanto, con suerte cerrar esta tangente; se aplica tanto a Linux como a macOS:

No hay una función del sistema (o biblioteca) llamada exec en Unix, solo hay una _familia_ de funciones relacionadas con exec en su nombre (la mayoría de ellas como prefijo).

  • Las funciones del _sistema_ ( man sección 2 ), execve en su esencia, _ fallan_ cuando intenta ejecutar un script sin shebang.

  • Entre las funciones de _library_ que _construyen las funciones del sistema_ ( man sección 3 - man 3 exec ), solo las que tienen p (para "ruta", presumir) tienen el respaldo de pasar al shell del sistema (por ejemplo, execlp ).

    • Las implementaciones de la biblioteca GNU de estas funciones codifican /bin/sh , como ha mostrado @TSlivede , mientras que las basadas en BSD usan solo sh , y dependen de sh para estar en $env:PATH (aunque, curiosamente, la página man indica /bin/sh ).

Como se indicó, para la _ ejecución directa_ los principales shells similares a POSIX ( bash , dash , ksh y zsh ) recurren a la ejecución de scripts sin shebang _ ellos mismos_, lo que implica que _no_ usan las variantes de respaldo entre las funciones de la biblioteca exec ; por el contrario, la elección de perl para su propia función exec fue confiar en el respaldo; de manera similar, las exec _builtins_ de las principales shells similares a POSIX se basan en el respaldo, excepto en bash .

Dado que en Unix el uso de un archivo de script detrás de escena es innecesario, podemos hacer las mismas suposiciones sobre la ruta del shell del sistema que las funciones de la biblioteca de respaldo, y recomiendo /bin/sh sobre sh , por seguridad y previsibilidad:

A pesar de que la especificación POSIX _no_ exige la ubicación de sh (es decir, estrictamente hablando, las funciones de la biblioteca BSD son compatibles):

  • /bin/sh es seguro de asumir, porque es la ubicación estándar _de facto_, sobre todo porque escribir shell Unix portátil _necesita_ referirse a sh por su ruta completa, dado que las líneas shebang solo admiten _full, literal_ rutas ( #!/bin/sh ).

  • Por el contrario, confiar en la ubicación de sh través de $env:PATH puede ser un riesgo de seguridad, dado que $env:PATH podría manipularse para invocar un sh .

El mismo riesgo se aplica a la ubicación de cmd.exe través de $env:ComSpec , por cierto, por lo que quizás la mejor manera es llamar a la función GetSystemDirectory WinAPI y agregar \cmd.exe a su resultado (necesitamos un respaldo de todos modos, si $env:ComSpec no está definido).

/bin/sh es seguro de asumir, porque es la ubicación estándar _de facto_, sobre todo porque escribir shell Unix portátil _necesita_ referirse a sh por su ruta completa, dado que las líneas shebang solo admiten _full, literal_ rutas ( #!/bin/sh ).

Normalmente, dice #!/usr/bin/env sh (excepto en los scripts del sistema).

  • Normalmente, _no_: buscar en Google "#! / Bin / sh" arroja alrededor de 3.900.000 coincidencias, "#! / Usr / bin / env sh" arroja alrededor de 34.100 coincidencias.

  • Normalmente, _no debería_: desea apuntar de manera predecible a _el_ shell del sistema, /bin/sh , no a la utilidad sh que suceda primero en $env:PATH .

La única razón para apuntar a un ejecutable llamado sh es apuntar de manera portátil al shell _system_ del mínimo común denominador-asumir-POSIX-features-only, es decir, /bin/sh .

Normalmente, _no_: buscar en Google "#! / Bin / sh" arroja alrededor de 3.900.000 coincidencias, "#! / Usr / bin / env sh" arroja alrededor de 34.100 coincidencias.

No hay forma de limitar una búsqueda en la Web a los scripts de usuario, especialmente porque muchos scripts de usuario no están marcados como ejecutables en absoluto, e incluso si lo son, pueden depender de execlp ; pero incluso si la mayoría de los scripts de usuario dicen eso, no deberíamos tomar _customary_ por _normal_. Los scripts que PowerShell ejecutará son scripts de usuario; cuando los usuarios quieren un shell, llaman a sh , no a /bin/sh , a menos que estén paranoicos.

@oising

Pasar la línea de comandos del shell nativo _como una sola cadena_ (en cualquier forma de cadena (-literal) que sea más fácil), y _sólo_ como una sola cadena (salvo los argumentos adicionales de pasar a la línea de comandos compatibles con Unix ( solo) discutido anteriormente ), suponiendo que esté de acuerdo con eso, suena como un terreno común.

Pasar la línea de comando como _ una sola cadena_ es el requisito previo para:

  • poder incorporar dicha llamada en una tubería _PowerShell_.
  • pudiendo incorporar _PowerShell variables y expression_ en la línea de comandos de native-shell.

Pasar una sola cadena es una _expresión conceptual directa_ de lo que hará la llamada; por el contrario, tratar de incorporar de alguna manera directamente una sintaxis de shell diferente directamente en PowerShell con argumentos _individual_ (opcionalmente, sin palabras) trae problemas sin solución que resultan en un compromiso confuso con limitaciones fundamentales, que el --% ya representa.


Si bien podría argumentar que entonces no es tan importante si elegimos un _operador_ o un _cmdlet_ para pasar esa línea de comando de una sola cadena (excepto para preguntas de _descubrimiento_), tengo preocupaciones conceptuales sobre el uso de ambos & y --% (precedidos por & ):

  • La preocupación con respecto a & : si necesitamos pasar una cadena - entre comillas - que contiene la línea de comando, estamos operando efectivamente en modo _expresión_, mientras que & actualmente implica el modo _argumento_ (lo que significa: argumentos _individuales_, citando solo cuando sea necesario). Por lo tanto, la participación de & es confusa.

  • La preocupación con respecto a --% tiene que ver con el origen exclusivo de Windows y la semántica confusa de --% como símbolo de detención del análisis; Sería especialmente confuso si --% como símbolo de parada de análisis operara en modo _argument_ (p. ej.
    & /bin/ls --% dir1 dir2 ), mientras que & --% operan en modo _expresión_ (por ejemplo, & --% '/bin/ls dir1 dir2 >out.txt' )


Permítanme dar un paso atrás y examinar --% , el símbolo de detener el análisis, tal como se implementa actualmente:

Fue un intento de resolver dos problemas que en última instancia no estaban relacionados (solo Windows en ese momento):

  • (a) Un caso de uso que también se aplica a otras plataformas: Permítanme reutilizar _líneas de comando_ escritas para cmd.exe / el shell nativo tal cual, para no tener que adaptarlas a la sintaxis de PowerShell.

  • (b) Un problema siempre solo para Windows: Permítame llamar a un _rogue CLI_ (es decir, una llamada a una aplicación de consola externa _single_) que requiere un estilo muy específico de citación que la re-cotización genérica detrás de escena de PowerShell no puede proporcionar , al permitirme crear la línea de comandos que finalmente se pasó a WinAPI para tales CLI _verbatim_. (Limitar el enunciado del problema a esto supone que la recitación de PowerShell generalmente funciona correctamente, lo que nunca ha sido así, ese es el tema de # 1995).

--% se implementó como una _conflación_ confusa de intentos de solución para (a) y (b), y terminó por resolver ninguno de los problemas correctamente:

  • Se anunció como la solución a (a), pero en realidad tiene serias limitaciones, porque _la única característica cmd exe que emula es la expansión de %...% -style referencias de variables de entorno_:

    • Solo admite un comando _single_ cmd , dado que un | (y
      && y || , aunque no se implementaron en ese momento) terminan implícitamente la parte de paso a cmd .

    • Un _single_ & - que es cmd s separador de múltiples comandos - _es_ pasado, pero eso también _no_ resultó en soporte para múltiples comandos, porque - desde cmd isn ' t realmente involucrado - el & se pasa _verbatim al programa de destino_; p.ej
      echoArgs.exe --% a & b no hace eco a luego invoca b , pasa los argumentos textualmente a , & y b hasta echoArgs .

    • El carácter de escape de cmd ( ^ ) en los argumentos sin comillas no se respeta.

    • Como corolario, los tokens %...% que se refieren a variables de entorno existentes se expanden _invariablemente_; un intento de evitar que a través de ^ no funcione correctamente (p. ej.
      echoArgs.exe Don't expand %^USERNAME% , que en cmd evita la expansión _ y elimina el ^ _, retiene el ^ cuando se llama desde PowerShell como echoArgs.exe --% Don't expand %^USERNAME%

  • Como solución a (b), también se quedó corto:

    • Al igual que en el uso (a), no hay capacidad para incorporar variables y expresiones _PowerShell_ en el comando, excepto si primero define torpemente un _aux. environment_ variable_ que luego hace referencia a través de %...% después de --% ; p.ej
      $env:FOO='foo'; echoArgs.exe --% %FOO%!

La solución adecuada para (a) habría sido Invoke-NativeShell / ins , como se explica aquí.

La solución adecuada a (b) habría sido:

  • admite --% tan especial solo como el argumento _first_
  • y, al igual que con ins , requiere que sea seguido _ por una sola cadena_ que constituye la línea de comando de paso _como un todo_, nuevamente con la capacidad de incorporar _PowerShell_ variables y expresiones por medio de interpolación de cadenas (o otras formas de construir la cuerda)
  • Por ejemplo, msiexec --% "/i installer.msi INSTALLLOCATION=`"$loc`"" , con $loc conteniendo 'c:\foo\bar none' , daría como resultado que la línea de comando textual /i installer.msi INSTALLLOCATION="c:\foo\bar none" se pasara, satisfaciendo el requisito no estándar de msiexec que en los argumentos <prop>=<value> solo la parte <value> sea ​​entre comillas dobles.

Si bien eso no es _conveniente_, es el precio a pagar por una solución robusta a la anarquía que es un argumento basado en la línea de comandos que pasa a Windows.

@ mklement0 Gracias por la paciente y la respuesta reflexiva (como de costumbre).

Entonces, estoy totalmente de acuerdo con;

La solución adecuada a (b) habría sido:

  • support -% tan especial solo como el primer argumento
  • y, al igual que con ins, requiere que sea seguida por una sola cadena que constituye la línea de comando de paso en su conjunto,> - nuevamente con la capacidad de incorporar variables y expresiones de PowerShell por medio de interpolación de cadenas (u otras formas construyendo la cuerda)

    • por ejemplo, msiexec -% "/ i installer.msi INSTALLLOCATION = "$loc " ", con $ loc conteniendo 'c: foobar none', resultaría en una línea de comando textual / i installer.msi INSTALLLOCATION =" c: foobar ninguno ", satisfaciendo el requisito no estándar de msiexec de que en=argumentos solo elparte se cotiza doble.

Esto es lo que he estado tratando de exponer como una solución generalizada para (a) y (b), excepto que todavía no entiendo que (a) debería existir como un problema independiente para resolver. Lo que realmente estoy luchando es si (b) se implementa _específicamente_ por & , ¿por qué no puede cubrir (a) también? por ejemplo, ¿qué da ins ... que & cmd --% ... no puede? Y no será un cambio decisivo aparte de no permitir que & pase --% como argumento (lo que parece ridículamente improbable). Bonificación de que no tienes que preocuparte por comspec, shells o lo que sea. Deje que la persona que llama decida.

@ PowerShell / powershell-Committee revisó esto:

  • Esta será una función experimental para obtener comentarios reales sobre el uso; todavía estamos abiertos al sigilo real; diferimos /bin/sh vs /bin/env sh a la revisión de relaciones públicas
  • Avanzaremos con --% como nuevo operador específicamente para el escenario "stackoverflow" donde el usuario corta y pega una línea de comando en PowerShell y debería funcionar (con, por supuesto, el nuevo operador que lo precede )
  • Un cmdlet Invoke-NativeShell es independiente de esta propuesta y la comunidad podría publicarlo en PowerShellGallery; Además, requerir un here-string no resuelve el problema de la experiencia del usuario sin saber de antemano cómo envolverlo como here-string
  • No queremos hacer un cambio rotundo a --% switch donde los usuarios existentes pueden depender de ese comportamiento
  • Los usuarios que necesitan un comportamiento de transmisión como --% switch deben seguir usando ese interruptor y escapar de los caracteres especiales según sea necesario
  • Todavía hay un problema de descubrimiento para que los usuarios conozcan --% (o cualquier solución)
  • Actualmente no estamos considerando un cambio en PSReadLine para modificar el contenido pegado como una cadena aquí que intenta predecir la intención del usuario
  • No estamos buscando soluciones múltiples que permitan escenarios here-string vs non-here-string (expansión); por ejemplo, & --% frente a --%

También quiero agregar que, a pesar de que @ SteveL-MSFT y yo cerramos # 1995 al mismo tiempo que abrimos este, realmente no era nuestra intención comunicar esto como una solución definitiva a ese problema.

En mi opinión, los beneficios de esta característica en particular son mucho más significativos que el impacto # 1995. Creo que hay MUCHOS ejemplos de StackOverflow y ejemplos de documentos para herramientas que no son de PS que a la gente le gustaría cortar / pegar (incluido yo mismo).

También existe el deseo de proporcionar un escape adecuado dentro del propio lenguaje de PowerShell, pero no veo que la gente golpee # 1995 en forma masiva (pero explicaré más sobre esto allí).

  • Avanzaremos con --% como nuevo operador

Lamento prolongar esto, y no estoy buscando una discusión adicional, pero ¿qué tipo de operador? (Me interesa principalmente la sintaxis).

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

¿Quién recibe la pipa? ¿Qué pasa con el $ ? Similar con & , && y || .

¿En qué condiciones, si las hay, se podrá utilizar --% en una tubería PS en el futuro?

Gracias.

Lamento prolongar esto, y no estoy buscando una discusión adicional, pero ¿qué tipo de operador? (Me interesa principalmente la sintaxis).

En cuanto a AST, probablemente no sea técnicamente un operador, pero es su propio tipo de AST. Si se implementa como operador, sería un operador unario.

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

¿Quién recibe la pipa? ¿Qué pasa con el $ ? Similar con & , && y || .

La cadena find . -iname *$pdf -print0 | xargs -0 ls -ltr se enviaría tal cual al shell nativo. $a embargo,

¿En qué condiciones, si las hay, se podrá utilizar --% en una tubería PS en el futuro?

Si ~ en el primer espacio de elemento de comando ~ está al comienzo de una declaración, probablemente no. Ese es uno de los atractivos de la nueva sintaxis. Si no está ~ en la primera ranura ~ al principio, seguirá funcionando como lo hace hoy.

(@ daxian-dbw @ SteveL-MSFT si algo de eso es incorrecto, corríjalo ❤️)

@SeeminglyScience tiene toda la razón

Si está en la ranura del primer elemento de comando, probablemente no. Ese es uno de los atractivos de la nueva sintaxis. Si no está en la primera ranura, seguirá funcionando como lo hace hoy.

Puede ser complicado pasar la salida del comando ascendente al comando nativo que será invocado por --% . --% básicamente solo entrega la cadena como si fuera un shell nativo, como bash , pero la salida del comando ascendente no se puede alimentar a bash , sino que debe enviarse al comando nativo en sí, como find .

Creo que el comportamiento de transmisión no es una consideración para esta característica, ya que es principalmente para el escenario "StackOverflow". Así que creo que será mejor que hagamos --% una declaración especial para que quede claro que no funcionará con las canalizaciones _PowerShell_.

Ahh, sabía que había una razón por la que la "ranura del elemento de comando" estaba mal. Me refiero al comienzo de una declaración. ¡Lo arreglaré, gracias!

la salida del comando upstream no se puede simplemente alimentar a bash

¿_Upstream_ significa _us_? Creo que puedo hacer: 'ls' | bash . No es algo particularmente inteligente, pero es posible.

Estaba discutiendo con @jpsnover sobre esto y propuso el shebang #! como sigilo. En este caso, simplemente escribiría:

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

(donde el segundo caso asume que sh está en la ruta). En este caso, tendríamos que decidir si se requiere especificar el shell o si siempre lo ejecutamos bajo el shell predeterminado, por lo que en este ejemplo, sh se inicia dos veces. Sin embargo, hay un problema con esto debido a cómo funcionan los archivos shebang en la actualidad. Funcionan en PowerShell porque el shebang se ignora como comentario. Si decidimos ignorar la primera línea de un script si es un comentario, entonces todavía tenemos un problema en el shell interactivo donde cada nuevo conjunto de líneas es un nuevo script y hoy no hay forma de determinar si ese script se ejecuta como un guión o de forma interactiva.

Una alternativa podría ser tomar prestada la sintaxis de rebajas:

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

En este ejemplo, usaríamos la sintaxis de cercado de código de rebajas. Esto también resolvería el problema de las múltiples líneas y usaría un concepto que no es completamente nuevo. Dado que todavía tenemos un problema de descubrimiento inicial para los nuevos usuarios, no creo que esto sea mucho peor en el sentido de que necesita tener los triple backticks finales.

En este caso, esto podría funcionar potencialmente:

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

@ SteveL-MSFT La sintaxis de Markdown tiene mucho sentido. Si somos capaces de especificar. otros tipos como Python o quizás por extensión?

¡El shebang #! Interferiría con los comentarios existentes.

Eso me parece enormemente complicado, tanto de manejar en el análisis como para que los usuarios realmente _usen_.

Y sí, no necesitamos confundir a la gente con líneas que _deberían_ ser comentarios que no se convierten en comentarios. #Requires ya se está acercando a algo extraño, un shebang completo confundirá a la gente de Windows en el mejor de los casos, y probablemente también a la gente de Linux.

Personalmente, preferiría no mezclar esta función con shebang, es confuso en mi opinión.
Además, si comenzamos a usar la sintaxis de bloque de código de descuento, entonces es solo cuestión de tiempo que la gente solicite ejecutar código de lenguaje arbitrario directamente en PowerShell. No creo que queramos ir por ese camino ...

Además, si comenzamos a usar la sintaxis de bloque de código de descuento, entonces es solo cuestión de tiempo que la gente solicite ejecutar código de lenguaje arbitrario directamente en PowerShell.

Literalmente, nunca dejaría de pedir C # en línea, aunque sé que es una mala idea.

La sintaxis de Markdown no introduce código de lenguaje arbitrario porque la secuencia de comandos incrustada se ejecutará en un proceso secundario. Puede colocar código de idioma arbitrario dentro de @' ahora y no pasa nada malo.

En la discusión del equipo y también con Jeffrey, no deberíamos estar en contra de las personas que quieran ejecutar otros lenguajes dentro de un script de PowerShell. Como señaló @ yecril71pl , no estamos apoyando directamente otros lenguajes, sino que confiamos en un proceso diferente para interpretar y ejecutar ese código. El escenario aquí es que un usuario ve un bloque de script python o bash y solo quiere usarlo tal como está dentro de su script de PowerShell. No hay nada que requiera que la gente haga esto. Pueden decidir portar ese otro script a PowerShell si lo prefieren. Esto solo proporciona una opción para que los usuarios puedan cortar y pegar en PowerShell para hacer las cosas.

No necesitamos mezclar las propuestas de una sola línea con las de varias líneas, ya que podríamos admitir ambas, aunque la desventaja es que ahora tenemos dos formas de hacer cosas muy similares pero con una sintaxis diferente.

Preferiría dejar las cosas de una sola línea. Las islas de código alienígena deben ser prominentes, de lo contrario nadie entenderá lo que está sucediendo.

@ yecril71pl Me

En la discusión del equipo y también con Jeffrey, no deberíamos estar en contra de las personas que quieran ejecutar otros lenguajes dentro de un script de PowerShell.

Absolutamente al 100%, pero eso no significa que deba integrarse directamente en el idioma.

Como señaló @ yecril71pl , no estamos apoyando directamente otros lenguajes, sino que confiamos en un proceso diferente para interpretar y ejecutar ese código. El escenario aquí es que un usuario ve un bloque de script python o bash y solo quiere usarlo tal como está dentro de su script de PowerShell.

Eso ya se puede hacer con here-strings, ¿verdad? ¿Puede explicar un poco los beneficios que esto proporciona sobre los métodos existentes?

Además, ¿por qué detenerse en python y bash? ¿Por qué no C #, CIL, C ++?

No hay nada que requiera que la gente haga esto. Pueden decidir portar ese otro script a PowerShell si lo prefieren. Esto solo proporciona una opción para que los usuarios puedan cortar y pegar en PowerShell para hacer las cosas.

No estoy en contra de esto porque creo que me veré obligado a usarlo. No me gusta porque va a ser difícil de mantener desde la perspectiva del idioma, una pesadilla para los editores y, en última instancia, alentar a los guiones que son ilegibles a menos que conozca varios idiomas.


Realmente, aunque este es un concepto bastante diferente del tema original de este hilo con implicaciones mucho más amplias. Recomiendo crear un nuevo problema para él.

Eso ya se puede hacer con here-strings, ¿verdad? ¿Puede explicar un poco los beneficios que esto proporciona sobre los métodos existentes?

Un editor de código universal podrá detectarlos y decorarlos adecuadamente, mientras que no hay nada que hacer con @' porque el editor no tiene idea de lo que hay dentro.

Además, ¿por qué detenerse en python y bash? ¿Por qué no C #, CIL, C ++?

¿Porque no son lenguajes de script?

Un editor de código universal podrá detectarlos y decorarlos adecuadamente, mientras que no hay nada que hacer con @' porque el editor no tiene idea de lo que hay dentro.

Un editor puede decir que bash -c @' contiene bash tan fácilmente como una valla de código. Además, la parte desafiante no es determinar qué idioma es una sección de código, sino hacer malabarismos con los servidores de idiomas y los marcadores de sintaxis. La única forma en que funcionaría sin una inversión significativa de tiempo y esfuerzo sería esencialmente renderizarlo como si fuera una cadena aquí.

¿Porque no son lenguajes de script?

C # y C ++ tienen subconjuntos orientados a secuencias de comandos. Incluso si no lo hicieran, el "lenguaje de programación" es subjetivo sin una definición real. No es útil como límite definitivo de lo que se consideraría y no se consideraría para su inclusión.

C # y C ++ tienen subconjuntos orientados a secuencias de comandos. Incluso si no lo hicieran, el "lenguaje de programación" es subjetivo sin una definición real.

Un lenguaje es un lenguaje de secuencias de comandos (independiente) cuando normalmente llama a interpet options… script arguments… y esa llamada no deja nada excepto lo que se pretendía dejar, excluyendo cosas temporales y privadas para el intérprete. También significa que normalmente requiere una copia del intérprete para ejecutarse. Hay lenguajes de secuencias de comandos incrustados que no puede ejecutar de esta manera.

@CienciaCiencia

Eso ya se puede hacer con here-strings, ¿verdad? ¿Puede explicar un poco los beneficios que esto proporciona sobre los métodos existentes?

Las personas pueden usar here-strings hoy o pueden escapar de todos los argumentos pasados ​​a los comandos nativos si pueden descubrir cómo hacerlo bien. La intención aquí es simplificar a los nuevos usuarios los escenarios de cortar y pegar (Stackoverflow).

Además, ¿por qué detenerse en python y bash? ¿Por qué no C #, CIL, C ++?

Los escenarios que se admiten aquí son un bloque de texto que se pasa a un comando nativo. Se puede admitir cualquier idioma si admite esa convención de llamada. No hay ninguna propuesta para crear un archivo fuente temporal y compilarlo.

No estoy en contra de esto porque creo que me veré obligado a usarlo. No me gusta porque va a ser difícil de mantener desde la perspectiva del idioma, una pesadilla para los editores y, en última instancia, alentar a los guiones que son ilegibles a menos que conozca varios idiomas.

No hay ninguna expectativa de que obtenga colores de sintaxis de lenguaje mixto. Cualquier script anidado de otro idioma simplemente se monotoneará.

Realmente, aunque este es un concepto bastante diferente del tema original de este hilo con implicaciones mucho más amplias. Recomiendo crear un nuevo problema para él.

La implementación propuesta es diferente del problema original, pero el problema es el mismo que es el escenario de cortar y pegar y "simplemente funciona".

Las personas pueden usar here-strings hoy o pueden escapar de todos los argumentos pasados ​​a los comandos nativos si pueden descubrir cómo hacerlo bien. La intención aquí es simplificar a los nuevos usuarios los escenarios de cortar y pegar (Stackoverflow).

Bien, pero ¿cómo es realmente más fácil? Entiendo que es subjetivo, pero estos no me parecen tan diferentes:

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

~~~

vs

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

Parecen casi lo mismo con la excepción de que este último es significativamente más claro en lo que va a suceder.

Los escenarios que se admiten aquí son un bloque de texto que se pasa a un comando nativo. Se puede admitir cualquier idioma si admite esa convención de llamada. No hay ninguna propuesta para crear un archivo fuente temporal y compilarlo.

Bueno, C # no necesitaría un archivo fuente temporal. Quizás tampoco CIL (ni idea de C ++). De cualquier manera, aunque una valla de código que sea estrictamente azúcar sintáctica para llamar a un ejecutable (por ejemplo, bash / python / cmd) me confunde. Las vallas de código implican soporte de idiomas en mi opinión .

No hay ninguna expectativa de que obtenga colores de sintaxis de lenguaje mixto. Cualquier script anidado de otro idioma simplemente se monotoneará.

Seguirá siendo una pesadilla para los analizadores basados ​​en tmLanguage. Apenas pueden manejar la sintaxis que esperan atm. Sin embargo, más que eso, si ese es el caso, no entiendo por qué es diferente de una cadena aquí.

La implementación propuesta es diferente del problema original, pero el problema es el mismo que es el escenario de cortar y pegar y "simplemente funciona".

Justo, pero ya tenemos 145 comentarios para discutir las soluciones propuestas anteriormente. Especialmente porque este hilo ya llegó a una conclusión, es probable que veas mucha más participación en un hilo nuevo y dedicado.

Bueno, C # no necesitaría un archivo fuente temporal. Quizás tampoco CIL (ni idea de C ++). De cualquier manera, aunque una valla de código que sea estrictamente azúcar sintáctica para llamar a un ejecutable (por ejemplo, bash / python / cmd) me confunde. Las vallas de código implican soporte de idiomas en mi opinión.

Solo quiero hacerme eco del comentario de ejecutar el script C # sin involucrar la compilación con la biblioteca Microsoft.CodeAnalysis.CSharp.Scripting . Eso es lo que usa dotnet-interactive en el kernel de C # Jupyter. Por lo tanto, puede que no sea irrazonable que los usuarios soliciten compatibilidad con secuencias de comandos C # o incluso F # con dicha sintaxis.

Puede decir que la sintaxis de cercado de código en PowerShell es para llamar a un comando nativo, que teóricamente permite esto:

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

pero luego es diferente de la comprensión establecida por los usuarios de la rebaja , que, como

que teóricamente permite esto

Pones el script dentro, no los argumentos, y el script es equivalente al flujo de entrada. Además, si esto está destinado a proporcionar soporte SO, no se deben permitir rutas completas.

Pones el script dentro, no los argumentos, y el script es equivalente al flujo de entrada

El script es el argumento de bash / cmd / python.

Además, si esto está destinado a proporcionar soporte SO, no se deben permitir rutas completas.

Luego cámbielo para este ejemplo:

~ powershell$ a = ipconfig /all ~

O agregue el exe arbitrario a la ruta primero.

Pones el script dentro, no los argumentos, y el script es equivalente al flujo de entrada

El script es el argumento de bash / cmd / python.

Ninguno de ellos permite pasar un script como argumento posicional. CMD ni siquiera permite un archivo por lotes.

Además, si esto está destinado a proporcionar soporte SO, no se deben permitir rutas completas.

Luego cámbielo para este ejemplo:

$ a = `` `ipconfig
/todas

Eso no funcionaría.

Parece que estaríamos haciendo una gran cantidad de casos especiales sobre una base muy arbitraria para que esta idea de rebaja funcione.

Además, la razón principal por la que esto incluso es compatible con Markdown es por el resaltado de sintaxis. Esa será una solicitud constante si agregamos algo como esto al lenguaje.

Ésta no es una solución eficaz.

Estaba discutiendo con @jpsnover sobre esto y propuso el shebang #! como sigilo.

Solo para agregar mis 2 centavos, creo que es un error reiniciar esta discusión después de que se anunció una solución. y personalmente no me gusta la sintaxis de shebang y markdown (y ambos son cambios importantes).

No me importa reiniciar las discusiones si a alguien se le ha ocurrido algo demostrablemente mejor. De hecho, es mejor hacerlo ahora que después de que se haya enviado la función. Simplemente no creo que #! o el enfoque de rebajas sean una mejor solución para suministrar una cadena de línea de comandos "no adulterada con PowerShell" a un ejecutable nativo. Tal vez solo necesito probarlo para acostumbrarme, pero mi reacción inicial es ... um, eew.

_Si nosotros (y PowerShell) sabemos que ejecutamos un ejecutable nativo, ¿por qué necesitamos "llamar al operador nativo"? _

Si PowerShell analiza una cadena de entrada y obtiene CommandAst, PowerShell realiza un descubrimiento del comando.
Si el comando es un comando nativo, PowerShell convierte el ast en NativeCommandProcessor.
Podemos implementar un nuevo (NativeCommandProcessor experimental) para el caso que volverá a analizar la extensión Ast (que es la cadena de entrada) para obtener el nombre / ruta del ejecutable nativo y el resto de la cadena como argumento o argumentos según las reglas del sistema operativo.

Si un usuario desea copiar y pegar desde el shell, debe escribir "cmd"o" bash"- eso funcionará sin editar.
Si un usuario quiere copiar y pegar una línea de comando nativafuncionará también como g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Si un usuario quiere copiar y pegar desde shell

Tenemos Get-/Set-Clipboard para eso.

para el caso que volverá a analizar la extensión Ast (que es la cadena de entrada) para obtener el nombre / ruta del ejecutable nativo y el resto de la cadena como argumento o argumentos según las reglas del sistema operativo.

Mi pensamiento aquí es que al hacer esto tú:

  • Analizar de manera ineficiente el comando AST, solo para analizar la extensión más tarde como una cadena y desechar el AST original, O
  • Toma el AST e intenta reformarlo nuevamente en una cadena (de la forma en que lo hace el NativeCommandProcessor actual), pero al hacerlo, las cosas se perderán

Entonces, en cambio, necesitamos preservar la cadena desde el principio. Ahora, creo que podría decirse que PowerShell ya tiene tres tipos de token de cadena, pero la solicitud principal en esta discusión parece no querer escapar de las cosas. Cualquier cadena debe poder escapar de su terminador, pero la solución discutida allí parece ser usar una nueva línea como terminador y no poder escapar de las nuevas líneas (siempre hay punto y coma).

No estoy totalmente casado con un terminador de nueva línea, pero parece ser la única solución para no querer escapar de nada. Básicamente, eso haría que un token de "operador de llamada nativo" se convierta en un comentario de línea similar al actual: desde el operador hasta el final de la línea, se pasa la cadena al "shell nativo".

Entonces:

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

se tokenizará como:

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

(Tenga en cuenta que enviamos incluso el espacio directamente después de --% al shell nativo, ya que la cadena comienza inmediatamente después de --% )

Entonces este token está envuelto en un AST muy simple, como:

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

Luego, después de que esto se compila y se envía a la tubería, esto usa un nuevo procesador de comandos nativo para enviar la cadena directamente como un argumento al shell nativo.

Si bien todo eso parece implementable, algunas preguntas en mi mente son:

  • ¿Es configurable el shell nativo? Si no es así, ¿cómo lo justificamos? Si es así, ¿debería ser explícito como parte de la sintaxis (complicando la sintaxis), o implementado como una variable de preferencia (lo que dificulta su descubrimiento y manejo)?
  • ¿Se podrá descubrir una implementación así y no confundirá a la gente? ¿Los usuarios entenderán lo poco que está haciendo PowerShell aquí, o lo que sucede cuando usan | o & o incluso cadenas?
  • ¿Cuántos escenarios van a ser atendidos por una cadena textual de una sola línea aquí? ¿Vamos a implementar esto solo para descubrir que mucha gente quiere invocaciones de varias líneas? ¿Es la necesidad de evitar hacer algo como escapar de una sola cita lo suficientemente grande como para agregar estas restricciones?
  • Este parece un escenario muy específico para una sintaxis de lenguaje completamente nueva. Si estamos creando una nueva sintaxis de cadena textual, ¿por qué la acoplamos directamente al concepto de invocación nativa? Si estamos creando un nuevo mecanismo de invocación, ¿por qué lo acoplamos directamente al concepto de otros shells? La buena sintaxis y las buenas construcciones de lenguaje se componen, tanto sintácticamente como funcionalmente, y tratan de no integrarse demasiado en el lenguaje en sí (en lugar de proporcionar una plataforma de elementos para que el usuario los ensamble como su programa): ¿nuestra sintaxis de llamada nativa propuesta cumple con los requisitos? para que una nueva sintaxis elemental se componga en programas? ¿Existe un enfoque alternativo mejor que podamos tomar que resuelva este problema de una manera simple, pero que aún nos permita mantener los bloques de construcción desacoplados para que se puedan componer para resolver otros problemas?
  • ¿Otros shells u otros lenguajes implementarán esto o ya lo han hecho? ¿A nivel de sintaxis, o en cualquier otro nivel? ¿Si es así, cómo?

Mi pensamiento aquí es que haciendo esto tú tampoco

@rjmholt CommandAst tiene la propiedad Extent con una cadena de entrada. Volver a analizar es tan simple como dividir por el primer espacio en Windows o dividir por espacios en Unix. Así que espero que no perdamos nada en la cuerda y obtengamos un buen rendimiento.

Independientemente de cómo se implemente esto, creo que necesitamos un conjunto de casos de uso para realizar pruebas. Permítanme "proponer" lo siguiente:

  • Caso de uso 1: quiero que una única invocación de literal exe (sin interpolación) funcione sin conocer las reglas de citación / escape, por ejemplo: g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 . La solución actual para esto es averiguar qué citar, por ejemplo: g++ timer.cpp '@conanbuildinfo.args' -o timer --std=c++11 Un ejemplo más complicado podría incluir líneas de comando que abarcan varias líneas, por ejemplo:
tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found 

Solución alternativa actual (intercambiar comillas invertidas por \ ), por ejemplo:

tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz `
    --exclude=/proc `
    --exclude=/lost+found
  • Caso de uso 1a: quiero que funcione una única invocación de exe con interpolación de PS, por ejemplo: g++ $CppFile @conanbuildinfo.args -o timer --std=c++11 . La solución actual para esto es averiguar qué citar (simple o doble) y qué escapar, por ejemplo: g++ $CppFile '@conanbuildinfo.args' -o timer --std=c++11 Un ejemplo más complicado podría incluir líneas de comando que abarcan varias líneas, por ejemplo:
tar -cvpzf "/share/Recovery/Snapshots/${ComputerName}_`$(date +%Y%m%d).tar.gz" \
    --exclude=/proc \
    --exclude=/lost+found 

Solución alternativa actual, utilice comillas dobles en el primer argumento, cambie la comilla invertida por \ y escape el $ al comienzo de `$ (fecha + ...).

  • Caso de uso 2: quiero ejecutar un comando canalizado literal en otro shell, por ejemplo: ps -o pid,args -C bash | awk '/running_script/ { print $1 }' y wsl ls $foo && echo $PWD . _No estoy seguro de si este debe ser un caso de uso separado del anterior ._ La solución actual es citar el comando bash, por ejemplo: bash -c 'ps -o pid,args -C bash | awk ''/-bash/ { print $1 }''' . Este caso de uso también puede incluir cadenas de varias líneas, por ejemplo:
ps -o pid,args -C bash | \
awk '/running_script/ { print $1 }'
  • Caso de uso 2a: quiero ejecutar un comando canalizado en otro shell con alguna interpolación de PS , ¿es este un caso de uso?

  • Caso de uso 3: necesito npm run $scriptName para seguir funcionando, es decir, interpolar variables . (esto puede estar implícito y no es necesario indicarlo).

Hay una cuestión de si debería haber casos de uso para cadenas de interpolating es decir, g++ $CppFile @conanbuildinfo.args -o timer --std=c++11 o simplemente apostamos por eso y decimos que las reglas de escape actuales cubren esto.

Es probable que estos no sean los casos de uso correctos (o todos), pero creo que sería útil tener casos de uso documentados y poder hacer referencia a ellos cuando se habla de esta función; de lo contrario, es fácil perderse en cómo la función realmente aborda (o no) ) casos de uso de clientes.

Además, el segundo caso de uso me parece que requiere "invocación de shell". Sin embargo, no estoy seguro de que el primero lo haga. Parece que el exe podría invocarse directamente.

@rkeithhill ¿Podrías agregar soluciones alternativas existentes para completar en tu publicación anterior?

@oising , yo también aprecio su

¿Qué da ins ... que & cmd --% ... no puede?

--% en su forma actual, _parámetro_ (símbolo) - cuyo comportamiento supongo que queremos retener - no se puede usar para cmd /c --% , porque no admite _multi-command_ cmd líneas de comando (ni ^ como carácter de escape), dado que el primer | sin comillas se interpretará como el operador de tubería de _PowerShell_ y que & se pasa literalmente.
Lo mismo se aplicaría en Unix, donde --% es generalmente casi inútil, y con sh -c falla por completo, porque sh espera la línea de comando como un argumento _solo_ (por ejemplo, sh -c --% echo hi produce una línea vacía, porque echo solo se ejecuta como el comando).

ins , con su requisito de delimitar la línea de comando de paso pasándola _como una sola cadena_ resuelve estos problemas y, como se indicó, también permite el uso de la interpolación de cadenas para incorporar valores de expresión y variable _PowerShell_.

Y esto último nos lleva a por qué la --% _statement_ propuesta es una _ calle sin salida_ :

  • No hay _una ruta de transición_ de ejecutar una línea de comando preexistente _exactamente como está_ a incorporar valores de expresión y variable _PowerShell_ en ella.

    • La única manera, oscura y engorrosa, de lograr esto es definir _variables de entorno auxiliares_ que almacenan los valores de PowerShell de interés y que luego puede hacer referencia (de forma apropiada en el shell con %...% o $... ) en la línea de comandos nativa.
  • Tiene la incomodidad de no poder incorporar una declaración --% en una tubería de PowerShell más grande.

    • Tenga en cuenta que existen razones legítimas _no_ cortar y pegar para llamar al shell nativo, donde puede esperar esta integración; específicamente, pasar _raw byte data_ a través de una tubería (intermedia) y enviar _strings sin una nueva línea final_ requiere el uso de la tubería nativa; consulte esta respuesta de SO para ver un ejemplo de la vida real.
  • También existe la incomodidad de que el parámetro --% _parámetro_ sea prácticamente inútil en Unix, lo que introduce una asimetría extraña: si la declaración --% _ también funciona en Unix, ¿por qué no el parámetro --% _ ? (p.ej,
    /bin/echo --% 'hi "noon"' arroja textualmente 'hi noon' (en lugar del hi "noon" esperado), debido a que echo recibe _dos_ argumentos, textualmente 'hi y noon' , con comillas simples retenidas como datos y comillas dobles eliminadas).


ins , como alternativa sugerida a la declaración --% , idealmente sería solo una _ envoltura de conveniencia_ alrededor de cmd /c y sh -c , pero en realidad es _requisito_:

  • Al menos _temporalmente_ en Unix, siempre que # 1995 no esté arreglado, porque llamar a sh -c '...' directamente actualmente no pasa argumentos con " incrustados correctamente.

  • _Invariablemente_ en Windows, porque necesita pasar la línea de comando _ como está_ a cmd través del parámetro lpCommandLine de CreateProcess (que el parámetro --% no puede hacer debido a que está limitado a un comando _un_); alternativamente, tratar de pasar la línea de comando como una _solo cadena_ encerrada en "..." general de nuevo no funciona correctamente debido a # 1995.


Los problemas con el paso de _argument_ podrían evitarse proporcionando la línea de comando para ejecutar _via stdin_ (que ins podría y también debería admitir), es decir, para _pipe_ la línea de comando / texto del script al ejecutable de shell:

  • En Unix, esto funciona bien, porque pasar código a través de _stdin_ a sh es esencialmente lo mismo que pasar un _archivo de script_ para su ejecución (como sh -c '<code>' is ):
PSonUnix> @'
echo '{ "foo": "bar" }' | cat -n
'@ | sh

     1  { "foo": "bar" }
  • Por desgracia, en Windows cmd no maneja bien dicha entrada: imprime su logotipo y repite cada comando antes de la ejecución, junto con la cadena de solicitud:
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>

Lamentablemente, PowerShell en sí mismo exhibe un comportamiento igualmente inútil: vea # 3223


Al igual que sh y todos los shells principales similares a POSIX ( dash , bash , ksh , zsh ), la mayoría de los lenguajes de programación _hacen_ correctamente admite la entrada stdin-as-script, como python , node , ruby y perl .

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

hi

Para incorporar valores de expresión y variable _PowerShell_, nuevamente tiene la opción de usar interpolación / concatenación de cadenas:

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

bar

o usando la función de paso de argumentos del intérprete:

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

bar

Espero que lo anterior deje en claro que _no_ se necesita una sintaxis especial en absoluto, ni una declaración --% , ni una declaración #! , ni bloques de código de Markdown , todos los cuales, nuevamente, carecerían del capacidad de incorporar valores del script de PowerShell que realiza la llamada.

En cuanto a la capacidad de configuración del shell nativo a utilizar: para la previsibilidad, no creo que debamos ofrecer uno; los usuarios que quieran apuntar a un shell diferente pueden simplemente canalizar la línea de comando al ejecutable específico de ese shell o, una vez que # 1995 esté arreglado, pasarlo como argumento.

  • Una variación discutible es apuntar a /bin/bash (con un (poco probable) retroceso a /bin/sh ), mientras que uno esperaría que las líneas de comando publicadas se basen solo en las características exigidas por POSIX, las líneas de comando con Bash-isms (Las características que no son de POSIX que no se puede esperar que admitan todas las implementaciones de /bin/sh ) probablemente estén disponibles, dada la prevalencia de bash .

@joeyaiello

En mi opinión, los beneficios de esta característica en particular son mucho más significativos que el impacto # 1995.

Me parece asombroso que gastemos tanto esfuerzo como tenemos en hacer esta función de cortar y pegar, principalmente para la conveniencia interactiva, un ciudadano de primera clase (incómodo, con funciones limitadas), mientras que la incapacidad fundamental de PowerShell para pasar vacío argumentos y argumentos con caracteres " incrustados.

pero no veo que la gente golpee el # 1995 en forma masiva

Sí, y por las razones que he comentado anteriormente, lo verás cada vez más.

Nuevamente, diría que un shell que hace lo siguiente, como lo hace actualmente PowerShell, tiene un problema grave :

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

{ foo: bar }

@rjmholt CommandAst tiene la propiedad Extent con una cadena de entrada. Volver a analizar es tan simple como dividir por el primer espacio en Windows o dividir por espacios en Unix. Así que espero que no perdamos nada en la cuerda y obtengamos un buen rendimiento.

Sí, entiendo el sorteo allí, y tal vez las asignaciones del AST en ese escenario no son horribles (aunque yo diría que el lenguaje no debería generar asignaciones innecesarias para una sintaxis simple). Pero todo lo que tiene que hacer es imaginar una entrada que bash / cmd ve como una cadena y PowerShell no comienza a tener problemas:

No analiza con PowerShell actual:

--% my_command "\" "

Analiza, pero dividir por espacios da un resultado incorrecto:

--% my_command "\"    "\"

(Debemos enviar 4 espacios en la cadena, pero PowerShell ve dos cadenas separadas en una matriz)

@rjmholt , todas las grandes preguntas al final de su comentario anterior , que para mí nuevamente sugieren que la respuesta correcta es: no hay necesidad de una sintaxis especial.

He abordado la pregunta sobre qué ejecutable de shell nativo usar en mi comentario anterior , pero en cuanto a:

¿Otros shells u otros lenguajes implementarán esto o ya lo han hecho? ¿A nivel de sintaxis, o en cualquier otro nivel? ¿Si es así, cómo?

No tengo conocimiento de que ningún otro lenguaje haya hecho esto a nivel de sintaxis sin emplear _delimitadores explícitos_ y permitir la _interpolación_.
Por ejemplo, perl tiene `...` para ejecutar comandos de shell, por lo que puede ejecutar el siguiente comando desde un script en Unix, por ejemplo:
my $foo='/'; print `ls $foo | cat -n`

Los aspectos clave son el uso de delimitadores explícitos y la capacidad de incorporar valores del llamador, los cuales faltan en la propuesta actual para la declaración --% .

Como se indicó anteriormente, _podríamos_ usar este enfoque para un nuevo _operador_ (o algún tipo de sintaxis de interpolación opcional basada en delimitadores), pero no creo que valga la pena, dado que
ins "ls $foo | cat -n" (o con una variante here-string) servirá, sin sobrecargar el lenguaje con complejidad adicional.

perl ofrece una solución a nivel de sintaxis (adecuada) _porque no es en sí misma un shell_ pero quiere que las llamadas al shell sean lo más convenientes posible.

Por el contrario, _ somos_ un shell (también), y ofrecemos invocaciones de estilo shell _ directamente_, sin ninguna sintaxis adicional (aunque actualmente defectuosa - ver # 1995).
Parece innecesario proporcionar una sintaxis especial para hacer que las invocaciones de _un shell diferente_ sean lo más fáciles posible.

@sazonov

_Si nosotros (y PowerShell) sabemos que ejecutamos un ejecutable nativo, ¿por qué necesitamos "llamar al operador nativo"? _

Si PowerShell analiza una cadena de entrada y obtiene CommandAst, PowerShell realiza un descubrimiento del comando.
Si el comando es un comando nativo, PowerShell convierte el ast en NativeCommandProcessor.
Podemos implementar un nuevo (NativeCommandProcessor experimental) para el caso que volverá a analizar la extensión Ast (que es la cadena de entrada) para obtener el nombre / ruta del ejecutable nativo y el resto de la cadena como argumento o argumentos según las reglas del sistema operativo.

Si un usuario desea copiar y pegar desde el shell, debe escribir "cmd" o "bash"; eso funcionará sin editar.
Si un usuario desea copiar y pegar, una línea de comando nativa también funcionará como g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Pero espere, ¿no podría nunca usar ninguna sintaxis de PowerShell con comandos nativos si hiciéramos todo eso? Entonces, ¿como sc.exe query $serviceName pasaría $serviceName como una cadena literal?

Pero espere, ¿no podría nunca usar ninguna sintaxis de PowerShell con comandos nativos si hiciéramos todo eso? Entonces, ¿como sc.exe query $serviceName pasaría $serviceName como una cadena literal?

La opción --% no está destinada a abordar esa necesidad.

sc.exe query $serviceName es manejado por &"<cmd>"

La opción --% no está destinada a abordar esa necesidad.

sc.exe query $serviceName es manejado por &"<cmd>"

Sí, seguro, no creo que eso sea a lo que se refiere @iSazonov . Estoy leyendo eso como si estuvieran sugiriendo un cambio general en cómo funciona el enlace de parámetros para los comandos nativos.

Sí, seguro, no creo que eso sea a lo que se refiere @iSazonov . Estoy leyendo eso como si estuvieran sugiriendo un cambio general en cómo funciona el enlace de parámetros para los comandos nativos.

Señor, espero que no.

Señor, espero que no.

A veces, cuando estás haciendo una lluvia de ideas sobre lo abstracto, es fácil olvidarte de lo obvio. Quiero ser justos, todos lo leímos e inmediatamente comenzamos a pensar en la implementación sin darnos cuenta de cuánto se rompería.

O me estoy perdiendo algo obvio y eso no es en absoluto lo que se propone 😁

@essentialexch , para ser claros, re:

sc.exe query $serviceName es manejado por &"<cmd>"

& "<cmd>" solo funciona si <cmd> evalúa como un simple comando _nombre o ruta_, _no incluye argumentos_ - los argumentos deben pasarse por separado, individualmente - y no necesitan per se "...." citar para ampliar las referencias de variables (por ejemplo,
$exe = 'findstr'; & "where.exe $exe" falla por diseño; debe ser & "where.exe" $exe (las comillas dobles son opcionales aquí; si se omite, & es opcional))

Sí, seguro, no creo que eso sea a lo que se refiere @iSazonov . Estoy leyendo eso como si estuvieran sugiriendo un cambio general en cómo funciona el enlace de parámetros para los comandos nativos.

Mi propuesta es arreglar # 1995 (con cambios rotundos) lo más simple posible. De causa, la interpolación no funciona. Porque no es una propuesta final, es una demostración de cómo podríamos implementar una solución en minutos. Y principalmente demuestra que no necesitamos un operador nativo de llamada en absoluto como también señaló @ mklement0 .

Analiza, pero dividir por espacios da un resultado incorrecto:

@rjmholt Sí, pero creo que
El primer hijo de CommandAst es StringConstantExpressionAst con el comando nativo como BareWord. Podemos cortar la palabra desnuda de CommandAst Extent y obtener la lista de parámetros sin cambio de ast-s. Pero también podríamos agregar un nuevo ast para mantener la lista de parámetros.
Dedico un tiempo a la explicación porque a continuación quiero resumir la discusión y creo que tendremos que implementar algunas mejoras que parecen complejas, pero creo que podrían implementarse de manera simple y __con fácil exclusión voluntaria para compatibilidad con versiones anteriores__.


Creo que la intención de @ SteveL-MSFT de la propuesta inicial era evitar un cambio radical.
Estoy de acuerdo con @TSlivede y @ mklement0 en que esta discusión mostró que la nueva sintaxis no es necesaria ya que no resuelve todos los problemas _fundamentales_ como # 1995 y # 12975. Por otro lado, agrega complejidad al lenguaje, mientras que queremos, por el contrario, _simplificar_ el trabajo con aplicaciones nativas.

__La única forma de avanzar es realizar un cambio importante o más .__ (Después de eso, un nuevo operador o cmdlet también puede ser útil en algunos escenarios).


Debemos comenzar con un resumen de los casos de uso y las expectativas de los usuarios.
Vea la gran publicación de @rkeithhill arriba

  1. Los usuarios quieren habilitar / deshabilitar la interpolación.
    PowerShell ya tiene strings / here-strings con comillas simples y dobles.
    Nuestro objetivo es comprender cómo aplicar / perfeccionar / mejorar su.
  2. Los usuarios quieren ejecutar cualquier ejecutable o un shell

    • cualquier ejecutable: el comando comienza con el nombre de la aplicación y sigue los argumentos

      • con / sin interpolación

      • en una sola línea / en varias líneas

    • shell es probablemente un caso especial porque el argumento (s) es un _script_

      • en el escenario de copiar y pegar, los usuarios no quieren interpolación y quieren una o varias líneas

      • en el escenario de la secuencia de comandos, los usuarios desean habilitar / deshabilitar la interpolación y desean una o varias líneas

    Pensamientos:
    Esto nos hace pensar que el comportamiento del escenario de copiar y pegar debería ser el predeterminado.
    Los terminadores de línea dependen del ejecutable (en bash, `en PowerShell). Esto nos hace pensar que:
    (1) un shell debe declararse explícitamente como cualquier ejecutable,
    (2) quizás cualquier ejecutable con múltiples líneas debería ser llamado por medio de un shell o tenemos que hacer un análisis especial (para eliminar el terminador y construir la lista de argumentos recurriendo al problema del escape)
    (3) podríamos abordar el escenario de copiar y pegar en PSReadline. Podría convertir un búfer en el comando correcto de PowerShell.

  3. Los usuarios quieren una sola línea y varias líneas.

    • tal vez necesitemos mejorar here-strings para una sola línea para abordar algunos escenarios como se discutió anteriormente.
    • ver 2 - (2) - siempre use un shell para multilínea o implemente un análisis especial

No quiero escribir más porque @TSlivede y @ mklement0 podrían actualizar sus pensamientos y conclusiones de antes, pero que ahora pueden basarse en la aceptación de cambios importantes. Pediría abrir un nuevo problema (y cerrar todos los anteriores), enumerar todos los casos de uso (comenzando con @

Solo quiero agregar que podríamos considerar nuevos cmdlets para simplificar las adopciones de los usuarios como Invoke-Shell (Invoke-NativeShell), Test-Arguments (como echoargs.exe para mostrar que la aplicación nativa obtiene y mostrar una ayuda para los usuarios), Convert-Arguments ( para convertir la entrada del usuario en argumentos de escape correctos).

PD: Como mostré anteriormente, el comportamiento de una sola línea podría excluirse fácilmente para la compatibilidad con versiones anteriores en tiempo de ejecución.

Los terminadores de línea dependen del ejecutable (en bash, `en PowerShell).

\ no es un terminador de línea en bash .

De causa, la interpolación no funciona. Porque no es una propuesta final, es una demostración de cómo podríamos implementar una solución en minutos. Y principalmente demuestra que no necesitamos un operador nativo de llamada en absoluto como también señaló @ mklement0 .

De la misma manera que podríamos corregir todos los errores en unos minutos si elimináramos el repositorio. Es un descanso bastante grande lo que propones. Incluso si solo es hipotético, no veo cómo es útil.

Incluso si solo es hipotético, no veo cómo es útil.

@SeeminglyScience Parece que estamos en contextos diferentes. Digo que podemos implementar una solución para la llamada nativa de una sola línea como un cambio importante pero sin romper el analizador. En otras palabras, incluso podemos cambiar el comportamiento sobre la marcha, sin importar qué comportamiento nuevo implementemos.

En otras palabras, incluso podemos cambiar el comportamiento sobre la marcha, sin importar qué comportamiento nuevo implementemos.

Supongo que lo que me falta es cómo cambiar ese comportamiento sobre la marcha sin pedirlo explícitamente (como con un operador nativo de llamada).

@sazonov

_Si nosotros (y PowerShell) sabemos que ejecutamos un ejecutable nativo, ¿por qué necesitamos "llamar al operador nativo"? _

Si PowerShell analiza una cadena de entrada y obtiene CommandAst, PowerShell realiza un descubrimiento del comando.
Si el comando es un comando nativo, PowerShell convierte el ast en NativeCommandProcessor.
Podemos implementar un nuevo (NativeCommandProcessor experimental) para el caso que volverá a analizar la extensión Ast (que es la cadena de entrada) para obtener el nombre / ruta del ejecutable nativo y el resto de la cadena como argumento o argumentos según las reglas del sistema operativo.

Si un usuario desea copiar y pegar desde el shell, debe escribir "cmd" o "bash"; eso funcionará sin editar.
Si un usuario desea copiar y pegar, una línea de comando nativa también funcionará como g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Quiero confirmar lo que quiere decir con eso: ¿Está sugiriendo ignorar esencialmente toda la sintaxis de PowerShell al llamar a ejecutables externos?

Específicamente: ¿Está sugiriendo que, por ejemplo, ejecutar

/bin/echo (1..5)

en powershell ya no saldrá 1 2 3 4 5 sino que debería generar literalmente (1..5) ?

Si eso es realmente lo que está sugiriendo, entonces tengo que decir: creo que es una idea terrible.

Eso no simplifica las cosas (los ejecutables externos se comportan ahora sintácticamente diferente a los cmdlets internos) y tampoco tiene que hacer nada con # 1995 en mi humilde opinión ...

@rjmholt
Desde arriba :

  • ¿Otros shells u otros lenguajes implementarán esto o ya lo han hecho? ¿A nivel de sintaxis, o en cualquier otro nivel? ¿Si es así, cómo?

Lo único que me viene a la mente sería ipythons ! operator :

Ejecutando !ps -'fax' || true en salidas de 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 no muestra comillas alrededor de los argumentos , pero ps -'fax' || true se da realmente como un solo argumento para bash).

Sin embargo, esta característica aún permite la interpolación de variables de Python en ese comando ( {pyvar} ).

Y similar a @ mklement0 's ejemplo , ipython sólo implementa este

porque no es en sí mismo un caparazón

Si ipython solo permitiera la sintaxis de Python, no sería muy útil como shell, por lo que permiten que esta sintaxis reenvíe comandos a bash para que se puedan usar como shell. Powershell, por otro lado, afirma ser un shell (hasta que se resuelva el # 1995, no diré que es un shell), por lo que sería bastante extraño agregar una sintaxis especial para llamar a otros shells.

Si algo como esto se implementa por cualquier razón (con suerte no), sugiero usar ! lugar de --% como el "operador de shell de llamada".

! es algo que la gente podría adivinar (porque conocen ! para ese propósito de ipython (o del modo de comando vim o de less o de ftp ) )

--% por otro lado es más difícil de escribir, es poco probable que se adivine y lo más importante: el uso actual de --% tiene, en mi opinión, muy poco en común con "pasar cosas a otro shell" comportamiento. El actual --% emula exactamente un elemento de la sintaxis de cmd: sustitución de variables (e incluso eso no completamente). No admite el carácter de escape ^ , no permite la redirección a archivos, etc. Lo único algo útil sobre el actual --% es la capacidad de pasar contenido textualmente al lpCommandLine , pero eso es algo en lo que el "operador de shell de llamada" propuesto no es bueno.

Una diferencia más (que ya se mencionó) entre el "operador de shell de llamada" propuesto y el actual --% : uno termina en tuberías, el otro no, muy confuso para los nuevos usuarios. ! por otro lado reenvía | al shell en todas las aplicaciones que probé hasta ahora (ipython, vim, less, ed, etc.).

HELP about_Logical_Operators -S

Sí, ! en sí mismo es problemático porque es un operador de negación lógico en el lenguaje.

Por cierto, una de las propuestas alternativas fue &! , una especie de operador de "llamada nativa / shell".

Ojalá @TSlivede hubiera enfatizado más la parte de "ojalá no" cuando dijo "Si algo como esto se implementa por cualquier razón (ojalá no)"

  • Ningún operador / declaración _con argumentos individuales_ puede resolver limpiamente el problema de I-want-to-use-PowerShell-values-in-the-native-command-line, dado que $ se usa como la variable sigilo en _both_ Shell de PowerShell y de tipo POSIX.

  • Ningún operador / declaración _sin delimitadores explícitos alrededor de toda la línea de comandos nativa_ puede resolver el problema de dónde termina la línea de comandos nativa (que puede que le importe o no).

@ PowerShell / powershell-Committee discutió esto hoy. En este punto, no parece haber un acuerdo sobre la declaración inicial del problema ni un diseño sobre cómo resolverlo, por lo que no creemos que podamos llevarlo a 7.1 con un diseño estable.

@ PowerShell / powershell-Committee discutió esto hoy. En este punto, no parece haber un acuerdo sobre la declaración inicial del problema ni un diseño sobre cómo resolverlo, por lo que no creemos que podamos llevarlo a 7.1 con un diseño estable.

182 comentarios!

¡Ha habido una gran discusión sobre esto! Continuaré monitoreando cualquier comentario nuevo sobre este tema. Seguimos alentando a la comunidad a implementar el cmdlet Invoke-NativeCommand type publicado en PSGallery independientemente de este problema.

Creo que es desafortunado, cuando tuvo una solución de compromiso pero muy útil en https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -662732627, eliminar esto. Sé que quiere consenso, pero a veces un compromiso es lo mejor que puede suceder. No, no es perfecto, pero una solución SO de una sola línea sería extremadamente útil. Al menos en opinión de esta persona.

@essentialexch para ser claros, ni siquiera tenemos consenso dentro del equipo de PowerShell

Dado que esto se pospone, ¿qué pasa con la implementación de ayudantes?

podríamos considerar nuevos cmdlets para simplificar las adopciones de los usuarios como Invoke-Shell (Invoke-NativeShell), Test-Arguments (como echoargs.exe para mostrar que la aplicación nativa obtiene y muestra una ayuda para los usuarios), Convert-Arguments (para convertir la entrada del usuario a argumentos escapados a la derecha).

Creo que Test-Arguments podría ser muy útil.

Acabo de publicar un módulo, Native , ( Install-Module Native -Scope CurrentUser ) que los invito a todos a probar y cuyos comandos usaré a continuación ; los casos de uso numéricos a los que se hace referencia son del comentario anterior de @rkeithhill .

Si alguna vez queremos que se aclare la niebla conceptual, creo que debemos enmarcar los casos de uso de manera diferente.
_Ninguno de ellos requiere ningún cambio en el análisis_.

Caso de uso [llamada de shell nativo]:

Quiere ejecutar un _comando individual_ (caso de uso 1) o una línea de comando completa _multi-comando_ (caso de uso 2) _escrito para el shell nativo de la plataforma_ (este problema):

  • Ya que está usando una sintaxis de shell diferente, está pasando el comando (línea) _ como una sola cadena_ al shell de destino, para que _it_ realice el análisis; La interpolación de cadenas de PowerShell ofrece la capacidad de incrustar valores de PowerShell en la cadena pasada (caso de uso 2a).
  • ins ( Invoke-NativeShell ) cubre estos casos de uso.
# 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 sintaxis here-string no es la más conveniente (de ahí la sugerencia de implementar una variante en línea - vea # 13204), pero no _ tiene_ que usarla; para los comandos textuales, puede usar '...' , que solo requiere _doubling_ embedded ' , si está presente.

Además, aquí hay un sustituto razonable del "operador StackOverflow" :

Si coloca el siguiente controlador de clave PSReadLine en su archivo $PROFILE , podrá usar Alt-v para realizar una llamada a ins con una cadena aquí textual en el que se pega el texto actual del portapapeles.Entrar envía la llamada.

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

Caso de uso [llamada ejecutable directa]:

Desea llamar a un _solo ejecutable externo_ usando la sintaxis de _PowerShell_, con _ argumentos individuales_ (casos de uso 1a y 3).

  • Este es un mandato básico de cualquier shell, y es de vital importancia que funcione con solidez.

    • Debe poder confiar en que PowerShell puede pasar a través de cualquier argumento que resulte de _su_ analizar _ como está_ al ejecutable de destino. Este no es el caso actualmente - ver # 1995 .
  • Requiere que comprenda la sintaxis de _PowerShell_ y sus metacaracteres en modo argumento.

    • Debe tener en cuenta que ` se usa como carácter de escape y para la continuación de la línea.
    • Debe tener en cuenta que PowerShell tiene metacaracteres adicionales que requieren citar / escapar para su uso literal; estos son (tenga en cuenta que @ solo es problemático como el primer carácter de un argumento):

      • para shells similares a POSIX (por ejemplo, Bash): @ { } ` (y $ , si desea evitar la expansión inicial de PowerShell)
      • por cmd.exe : ( ) @ { } # `
      • Individualmente ` escapando de dichos caracteres. es suficiente (por ejemplo, printf %s `@list.txt ).

Mientras esperamos para # 1995 que se fije, la función ie (para i nvoke (externo) e xecutable) llena el vacío, simplemente anteponiendo a una llamada directa; por ejemplo, en lugar de la siguiente llamada:

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

usarías lo siguiente:

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

El módulo Native viene con algo parecido a echoArgs.exe , el comando dbea ( Debug-ExecutableArguments ); salida de muestra en 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"

Al usar el modificador -UseIe ( -ie ), puede decirle a dbea que pase los argumentos a través de ie , lo que demuestra que soluciona los problemas:

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

Nota: Una vez que se corrige # 1995, ie ya no es necesario; en aras de la compatibilidad con versiones posteriores, ie está diseñado para detectar y diferir automáticamente una solución; es decir, una vez que la solución esté en su lugar, ie dejará de aplicar sus soluciones y actuará efectivamente como una llamada directa / & .

Caso de uso [llamada ejecutable de Windows no autorizada directa]:

Hay dos problemas principales, que surgen en _Solo Windows_:

  • Está invocando un ejecutable "deshonesto" que tiene requisitos de citas que difieren de la convención más utilizada ; p. ej., msiexec.exe y msdeploy.exe requieren que prop="<value with spaces>" se cite precisamente de esa manera - la cita alrededor de la parte _value_ - aunque "prop=<value with spaces>" - cita de argumento - _ debería_ ser equivalente (este último es lo que PowerShell - justificadamente - hace detrás de escena).

  • Está invocando _un archivo por lotes_ con _un argumento que no contiene espacios, pero que contiene cmd.exe metacaracteres_ como & , ^ o | ; p.ej:

    • .\someBatchFile.cmd 'http://example.org/a&b'
    • PowerShell - justificadamente - pasa http://example.org/a&b _unquoted_ (ya que no hay espacios en blanco incrustados), pero esto rompe la invocación del archivo por lotes, porque cmd.exe - irrazonablemente - somete los argumentos pasados ​​a un archivo por lotes al misma regla de análisis que se aplicaría dentro de cmd.exe lugar de aceptarlos como literales.

    • Nota: Si bien el uso de archivos por lotes para _directly_ implementar la funcionalidad probablemente está disminuyendo, usarlos como _ puntos de entrada de CLI_ sigue siendo muy común, como az CLI de Azure, que se implementa como archivo por lotes az.cmd .

Nota: ie maneja automáticamente estos escenarios para archivos por lotes y para msiexec.exe y msdeploy.exe , por lo que para la mayoría de las llamadas no debería ser necesario ningún esfuerzo adicional ; sin embargo, es imposible anticipar _todos_ los ejecutables "deshonestos".

Hay dos formas de resolver esto:

  • Dado que cmd.exe , después de aplicar su propia interpretación a los argumentos en una línea de comando, _does_ conserva las comillas como se especifica, puede delegar en una llamada ins en Windows:

    • ins '.\someBatchFile.cmd "http://example.org/a&b"'
    • Si usa una cadena _expandable_, esto nuevamente le permite incrustar valores de PowerShell en la cadena de comando.

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

  • Alternativamente, use la implementación actual --% , pero tenga cuidado con sus limitaciones:

    • .\someBatchFile.cmd --% "http://example.org/a&b"'
    • Dado cómo se implementa --% , la única forma de incrustar valores de PowerShell es, torpemente, definir un _aux. variable de entorno_ y haga referencia a ella con la sintaxis %...% :

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


Manejo de errores

El https://github.com/PowerShell/PowerShell-RFC/pull/88 pendiente traerá una mejor integración de manejo de errores con ejecutables externos (nativos).

Mientras tanto, el módulo Native se envía con dos funciones para la suscripción ad-hoc para tratar un código de salida distinto de cero como un error de terminación del script :

  • ins admite el modificador -e / -ErrorOnFailure , que arroja un error si $LASTEXITCODE es distinto de cero después de la llamada al shell nativo.

  • iee es una función contenedora para ie que, de forma análoga, arroja un error si $LASTEXITCODE es distinto de cero después de la llamada al ejecutable externo.

Hay muchas sutilezas en los comandos que se envían con el módulo.
Es de esperar que la ayuda bastante extensa basada en comentarios los cubra lo suficiente.

Si alguna vez queremos que se aclare la niebla conceptual, creo que debemos enmarcar los casos de uso de manera diferente.

¡Excelente! Lo que publiqué anteriormente fue solo para que la gente pensara en crear más y mejores casos de uso.

Debo aclarar algunos aspectos importantes de ins ( Invoke-NativeShell ) que difieren de lo que he propuesto anteriormente; el objetivo era dar cuenta de la ubicuidad de bash y proporcionar una CLI consistente en todas las plataformas.

  • En Unix , he decidido llamar a /bin/bash lugar de /bin/sh , dada la ubicuidad de Bash, pero puedes optar por usar /bin/sh con -UseSh ( -sh ) ( /bin/sh también sirve como respaldo en el improbable caso de que /bin/bash no esté presente).

    • En aras de una experiencia coherente en todos los formularios y plataformas de invocación, he ocultado la capacidad de establecer $0 , el nombre de la invocación; es decir, cualquier argumento de transferencia comienza con $1 , que es coherente con la forma en que se pasan los argumentos cuando se canaliza el script desde la canalización y probablemente con lo que esperan los usuarios:
# 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
  • En Windows , tuve que usar un _archivo por lotes_ temporal entre bastidores por razones técnicas, pero creo que el uso de la sintaxis de archivos por lotes es, en última instancia, la mejor opción de todos modos ( %%i lugar de %i en for variables de bucle, capacidad de escapar % como %% ).

    • El uso de un archivo por lotes también permite la compatibilidad con argumentos de paso, como en 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]
¿Fue útil esta página
0 / 5 - 0 calificaciones