Powershell: No requiera {...} alrededor de nombres de variables para el acceso de miembros condicional nulo

Creado en 17 dic. 2019  ·  53Comentarios  ·  Fuente: PowerShell/PowerShell

Seguimiento de # 3240.

Se implementó el acceso de miembros condicional nulo ( ?. ) y estará disponible como una función _experimental_ en 7.0

Sin embargo, en aras de la compatibilidad con versiones anteriores, la implementación actualmente requiere que el nombre de la variable se incluya entre {...} , porque ? es técnicamente un carácter legal en un nombre de variable cuyo uso sorprendentemente _no_ requiere {...} .

Es decir, si desea ignorar un intento de llamar a $obj.Method() si $obj es $null (o indefinido), esperaría lo siguiente, análogo a C #:

# Does NOT work as intended.
# 'obj?' as a whole is interpreted as the variable name.
$obj?.Method()

En su lugar, debe utilizar actualmente:

# !! Must enclose 'obj' in {...} to signal that '?' is a syntactic element as part of '?.'
${obj}?.Method()

_Update_: Null-coalescing - $var??='default y $a??$b - y el operador ternario - $var?1:0 - se ven afectados de manera similar (aunque existe el uso de espacios en blanco antes de ? resuelve el problema).

Este requisito es (a) inesperado y (b) engorroso:

  • Los nuevos usuarios no esperarán la necesidad de {...} y no sabrán necesariamente que _it_ es lo que se requiere cuando lo siguiente falla (posiblemente _undetected_, a menos que Set-StrictMode -Version 2 o superior esté en vigor): $o = [pscustomobject] @{ one = 1 }; $o?.one

  • Incluso una vez que los usuarios _saben_ la necesidad de {...} :

    • Se olvidarán de usarlo en ocasiones, debido a la necesidad contraria a la intuición.
    • Cuando lo recuerden, este requisito aparentemente artificial será una fuente constante de frustración, especialmente porque {...} es difícil de escribir.

El uso de {...} no debería ser necesario y, si bien no requiere un cambio _de ruptura_, podría decirse que cae en el cubo 3: Área gris improbable .


Por qué este cambio debe considerarse aceptable:

Nota: @rjmholt analiza por qué cambiar la sintaxis de PowerShell es muy problemático _ en general_ en este comentario , pero por las razones que se presentan a continuación, creo que vale la pena hacer una excepción aquí.

?. es una característica sintáctica _nueva_; por definición, los scripts que lo usan _no pueden_ (significativamente) se ejecutan en versiones anteriores, a menos que agregue específicamente condicionales que proporcionen rutas de código compatibles con la versión heredada, pero que no parece valer la pena, entonces simplemente se quedaría con las características heredadas.

Sí, la interpretación de $var?.foo en scripts antiguos que usaban $var? como nombre de variable se rompería, pero:

  • ? nunca debería haberse permitido como parte de un identificador _ sin encerrarlo en {...} _.

  • Dado que poder hacerlo es inesperado y probablemente incluso _desconocido_ para muchos, tales nombres de variables son extremadamente raros en la naturaleza, hablando por experiencia personal, pero el análisis de @mburszley proporciona evidencia más tangible .

    • Incluso Ruby solo permite ? (y ! ) al final de los identificadores, y solo de identificadores de método, y sospecho que la mayoría de los usuarios de Ruby saben que no deberían asumir que otros idiomas admiten la misma cosa.

Entonces, pragmáticamente hablando:

  • La gran mayoría del código existente no se verá afectado: un token como $var?.foo simplemente no se encontrará.

  • Si escribe $var?.foo con la nueva semántica, entonces sí, ejecutar eso en versiones anteriores podría resultar en un comportamiento diferente (en lugar de romperse de una manera obvia), dependiendo del modo estricto que esté en efecto, pero _ siempre debería hacer cumplir la versión mínima requerida para ejecutar su código como se pretende de todos modos_ ( #requires -Version , claves de manifiesto del módulo).

En general, para mí, este es un caso claro de un cambio de cubo 3: un cambio técnicamente rompedor que rompe muy poco código existente al tiempo que ofrece beneficios reales (o, por el contrario, evita dolores de cabeza perennes debido a un comportamiento inesperado).

Committee-Reviewed Issue-Enhancement WG-Language

Comentario más útil

@rjmholt , aunque creo que todos podemos apreciar lo complicado que es un cambio como este _ en principio_, en este caso particular no importa _en la práctica_, y no creo que ni siquiera se necesite una regla de PSSA:

  • El análisis de @ThomasNieto ha demostrado que, si creemos que el corpus es representativo de todo el código de PowerShell que existe, _virtualmente nadie usa variables con nombres que terminan en ? _.

  • Si tuviera que adivinar, la mayoría de la gente ni siquiera pensaría en usar tales nombres de variables, porque no esperarían que funcionen (incluido yo), y probablemente ni siquiera notarían la excepción de que el $? automático bash ).

    • De hecho, mirando más de cerca el análisis de 8 enumerados:

      • 5 contienen solo _falsos positivos_, es decir, usos de variables dentro de _cadenas_ como "Are you sure you want to kill $vParam?" ; de hecho, estos usos están _quebrantados_ y revelan que los autores del script _no_ esperaban que ? fueran parte del nombre de la variable.

      • 2 de esos archivos ya no están disponibles públicamente.

      • Solo _1_ de ellos es un uso de buena fe de ? -variables finales - como variables _Boolean_ (p. Ej., $key1? ), que, aparte, por lo tanto, _no_ se combinan con el operador de acceso a miembros. , . / cc @ stevenayers.

Basándome en lo anterior, esto me parece un cubo de texto improbable del área gris , que por lo tanto es _aceptable_ - y creo que los _beneficios_ de este cambio se han argumentado de manera persuasiva.

_Documentar_ el cambio de comportamiento (con una justificación) debería ser suficiente.

Por supuesto:

  1. esto se basa en que (a) el análisis sea correcto y (b) el corpus disponible públicamente sea representativo.

  2. está totalmente en desacuerdo con la afirmación del comité de que "hubo bastante uso de nombres de variables que terminan con el signo de interrogación" (lo cual, por supuesto, solo sería un problema si dichos nombres de variables no se incluyen en {...} ).

Partiendo del supuesto de que 1. es verdadero (no hemos escuchado nada que respalde 2.), tenemos dos opciones:

  • Quítese la tirita y simplemente desautorice ? en los nombres de variables en el futuro, a menos que esté incluido en {...} (con la obvia excepción de $? ); esto romperá lo extremadamente pequeño proporción de scripts que actualmente se basan en eso.

    • Es decir, algo como $key1? que no va seguido de . ni de otro ? ( $key1??'else' ) ni de una expresión ternaria ( $key1?1:0 ) causaría un _ error del analizador_.

      • Como muestran los ejemplos de operadores ternarios y de coalescencia nula, ellos también se beneficiarían de este cambio; actualmente, necesita un _espacio_ - $key1 ?1:0 / $key1 ??'else' - o {...} - ${key1}?1:0 / ${key1}??'else'

    • Dentro de _cadenas expandibles_, ? ya no se consideraría parte de un nombre de variable (no {...} -enclosed), por lo que en realidad _corregiríamos_ scripts existentes que han utilizado cadenas por error como "Are you sure you want to kill $vParam?" . 😁
  • Si realmente sentimos que debemos evitar romper la pequeña proporción de scripts existentes, _podríamos_ considerar la lógica propuesta por @ ExE-Boss , pero no creo que queramos introducir esta complejidad conceptual, y mucho menos la complejidad de implementación (que yo realmente no puedo hablar).

Todos 53 comentarios

Para aclarar, el hito solo significa que es algo que el equipo debe discutir en función de los comentarios, no que estemos comprometidos a realizar este cambio.

@ mklement0

? nunca debería haberse permitido como parte de un identificador sin encerrarlo entre {...}.

Ummm: el lenguaje base inicial para PowerShell fue Posix Shell, que usa $? para el código de salida qed '?' está permitido en nombres de variables sin comillas.

no puede (significativamente) ejecutarse en versiones anteriores

Seguro que puede:

PS>  $abc?=@{a=1; b=2; c=3}
PS>  $abc?.b                                                                                        1
PS>  2                      

Seguro que puede:

No se puede ejecutar de manera significativa _con la nueva semántica_ (acceso condicional nulo); eso es todo lo que quise decir.

Sí, su ejemplo funciona actualmente, pero _con semántica diferente_ ( abc? _incluyendo el ? _ que se considera el nombre de la variable) - pero por las razones indicadas, vale la pena abandonar este caso de esquina por el bien de hacer ?. funcionan como se esperaba.

El lenguaje base inicial para PowerShell fue Posix Shell que usa $? para el código de salida

$? es una _excepción_ en shells similares a POSIX; no puede hacer lo siguiente:

# bash, ksh, zsh, dash (all common /bin/sh implementations)
foo?='bar' # BOOM! E.g. "bash: foo?=bar: command not found"

(Y, obviamente, nadie está pidiendo que se elimine $? en PowerShell).

Y, por supuesto, debe mencionarse que _todas las demás_ variables automáticas en el mismo espacio que $? (es decir, $^ y $$ ) son explícitamente especiales- encajonado en el tokenizador. Y, de hecho, también lo es $? , aunque (actualmente) no es necesario.

Me parece claro que se esperaba en el momento en que se escribió el código que ? no sería un carácter identificador válido.

Solo conozco una persona que ha usado ? al final de los nombres de las variables en sus scripts, y esa es @StartAutomating. Dado que en realidad usó esa sintaxis, pensé que valía la pena llamar su atención sobre esta discusión.

También @ vexx32 , la razón por la que $? está en mayúscula especial en el tokenizador es porque no puede crear una variable con un nombre que comience con ? . por ejemplo, $??? = 'foo' no se analizará. Los nombres de las variables deben comenzar con caracteres alfanuméricos o un guión bajo, y actualmente pueden contener caracteres alfanuméricos, guiones bajos o signos de interrogación. Estoy de acuerdo en que ?. debe analizarse como el operador de acceso de miembro condicional nulo, pero quería aclarar por qué ? tiene una caja especial en el tokenizador.

Gracias por llamar mi atención sobre este chat.

Por lo que vale, tengo algunas ideas:

  1. Si bien estas variables son raras, nadie quiere jugar al whack-a-mole cuando la retrocompatibilidad rompe sus scripts
  2. Dejando a un lado a las personas profundamente geek, esperaría que $ {obj} ?. Method () o $ obj? .Method () obtengan un pequeño repunte. Hay una década de orientación en la web sobre cómo administrar nulos, y no veo que muchas personas cambien de if ($ obj) {$ obj.Method ()} _sólo para subirse al tren de la v7_
  3. Además, la mayoría de las personas que tienen problemas con nombres de variables inesperados aprenden la sintaxis $ {} (ya la llamaría naturalmente con un guión bajo). Esperaría que la superposición del diagrama de Venn de "personas que quieran usar esta función" con "personas que ya conocen la sintaxis $ {}" esté cerca del 100%
  4. Dado que dicha sintaxis no es tan poderosa como una subexpresión completa, y las subexpresiones completas funcionan tanto en el núcleo como no, esperaría que la mayoría de la gente continúe usando esto en el futuro.

Entonces, ejecutando las matemáticas, hay varias razones concretas _no_ para hacer esto, y solo una razón vaga para hacerlo (podría_ ser más fácil, para algunos usuarios). Dada la baja prevalencia de? en los nombres de las variables, es posible que usted esté ayudando a más personas de las que daña, pero en mi opinión es un tiro al aire.

Dado el potencial de beneficio de lanzar una moneda al aire, parafrasearé a Robert McNamara "Si estamos malditos si lo hacemos y malditos si no lo hacemos, elegiré malditos si no lo hacemos".

Sugiero hacerlo de modo que $obj?.Method() funcione como $<var i="6">obj</var> ?. <var i="9">Method</var>() de forma predeterminada, y solo vuelva a $<var i="11">obj?</var> . <var i="14">Method</var>() cuando $obj no existe en el alcance, pero $obj? sí y #requires ‑Version no es v7 +.

@ ExE-Boss, parece que hay muchas excepciones para esta regla en particular.

Repito mi cuestionamiento del valor de esta regla en particular.

Más allá de eso, la pregunta es "¿qué más no sabemos acerca de la sintaxis variable que podríamos romper?"

Si el consenso es forzar la sintaxis ${...} para esto, se agradecería el soporte para OptionalFeatures aquí, de modo que las personas como yo que usarán esta sintaxis (la usaré _mucho_) puedan optar por salir del ruido .

Ya que estamos parafraseando aquí:
"La lógica dicta claramente que las necesidades de muchos superan las necesidades de unos pocos". - Spock

Entonces, ejecutando las matemáticas, hay varias razones concretas _no_ para hacer esto, y solo una razón vaga para hacerlo (podría_ ser más fácil, para algunos usuarios).

@StartAutomating : Hay varias razones, y son concretas, no vagas. Además, uno de los objetivos de esto es la simplicidad del código. Tener que ajustar los nombres de las variables con {} funciona directamente en contra de ese objetivo.

Estoy con @ mklement0 y @KirkMunro en esto. Personalmente, encuentro la sintaxis ${...}? natural e innecesariamente compleja. Ya hemos tenido cambios importantes y, en este caso, es un pequeño precio a pagar a cambio de claridad.

@InicioAutomatización

  1. Además, la mayoría de las personas que tienen problemas con nombres de variables inesperados aprenden la sintaxis $ {} ( ya la llamaría naturalmente con un guión bajo ). Esperaría que la superposición del diagrama de Venn de "personas que quieran usar esta función" con "personas que ya conocen la sintaxis $ {}" esté cerca del 100%

Esto no se sostiene. Los guiones bajos son caracteres válidos en nombres de variables.

@ vexx32

Me parece claro que se esperaba en el momento en que se escribió el código

No.

¿Cambiar los caracteres? $foo.?Property parece no chocar, porque los nombres de propiedad que comienzan con signos de interrogación ya deben citarse:

PS C:\> $foo = [pscustomobject]@{'?'=1; '?name'=2}
PS C:\> $foo.?name
At line:1 char:6
+ $foo.?name
+      ~
Missing property name after reference operator.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : MissingPropertyName
PS C:\> $foo.'?name'
2

@HumanEquivalentUnit , desafortunadamente, eso invitaría a una confusión eterna con la sintaxis ?. establecida desde hace mucho tiempo que se usa en C # y varios otros lenguajes (además, necesitaría encontrar una forma alternativa para el indexador ( ${arr}?[0] ) también, y algo como $arr[0]? invitaría a confusión con el operador ternario)

No hay una solución ordenada.

  1. ¿Un número desconocido de personas lo usan? al final de los nombres de una variable. Si bien es una función opcional, se puede decir "No la encienda si ejecuta esos scripts", pero la persona que lo activa debe verificar los scripts actuales y futuros que obtenga de cualquier fuente.
  2. La sintaxis alternativa no es una gran opción porque está importando algo que no es muy potente desde C #
  3. El cambio de comportamiento basado en #requires no funciona fuera de una secuencia de comandos guardada y fallará tan pronto como alguien copie algún código de una secuencia de comandos larga sin establecer la etiqueta.
    4. (dir *.ps2)?.count y (dir *.ps1)?.count muestran que su operando no necesita ser una variable.
    ($Host)?.toString() / ($null)?.toString() elimina la necesidad de poner llaves alrededor del nombre. Todavía son pulsaciones de teclas adicionales, pero es más lógico y menos feo.
  1. El cambio de comportamiento basado en #requires no funciona fuera de una secuencia de comandos guardada y fallará tan pronto como alguien copie algún código de una secuencia de comandos larga sin establecer la etiqueta.

Esto se aplica a _cualquier_ código que utilice funciones no admitidas en versiones anteriores.
Si esta fuera la principal preocupación, nunca se introduciría una nueva característica de idioma o un nuevo parámetro de cmdlet / cmdlet.

(Con suerte, en el código anterior tiene al menos Set-StrictMode -Version 1 en efecto, en cuyo caso $var?.foo fallaría en voz alta, debido a que no existe una variable denominada var? ).

  1. elimina la necesidad de poner llaves alrededor del nombre.

Sustituir (...) por {...} es solo una mejora marginal.

Cualquiera que sea la sintaxis adicional que se utilice, el problema aquí es la _necesidad_ de sintaxis adicional, para algo que debería funcionar tal como está, no solo para la conveniencia de escribir, sino también para cumplir con las expectativas sensibles.

  1. El cambio de comportamiento basado en #requires no funciona fuera de una secuencia de comandos guardada y fallará tan pronto como alguien copie algún código de una secuencia de comandos larga sin establecer la etiqueta.

Esto se aplica a _cualquier_ código que utilice funciones no admitidas en versiones anteriores.
Si esta fuera la principal preocupación, nunca se introduciría una nueva característica de idioma o un nuevo parámetro de cmdlet / cmdlet.

Eso no fue exactamente lo que quise decir. Más arriba, se sugirió que si #requires especifica pwsh 7, entonces el procesamiento podría ser diferente. Iluminando un nuevo comportamiento solo si pones #requiresno es bueno.

(Con suerte, en el código anterior tiene al menos Set-StrictMode -Version 1 en efecto, en cuyo caso $var?.foo fallaría en voz alta, debido a que no existe una variable denominada var? ).

En mi experiencia, no se usa mucho Set-Strictmode. Porque (a) existe la suposición de que el valor predeterminado sin él es "correcto" y (b) la frecuencia de uso de cosas que dependen de que esté apagado es demasiado alta. Es un uso común para estos operadores. En cualquier caso, el problema es el código anterior donde $ var? existe y tiene una propiedad foo, pero ahora el código busca $ var ... Posiblemente el nuevo procesamiento causaría un error si se establece el modo estricto porque $ var no existe ....

  1. elimina la necesidad de poner llaves alrededor del nombre.

Sustituir (...) por {...} es solo una mejora marginal.

Si. ($ x) es un poco mejor que $ {x} pero solo un poco. ( Get-user "Bob")?.disable() es mejor que

$u = get-user "bob" 
($u)?.disable

Cualquiera que sea la sintaxis adicional que se utilice, el problema aquí es la _necesidad_ de sintaxis adicional, para algo que debería funcionar tal como está, no solo para la conveniencia de escribir, sino también para cumplir con las expectativas sensibles.

Si.
$var. foo Con trabajos de espacios en blanco. ${foo} ?.bar() no porque? está permitido en una función o nombre de alias (?. es legal para ambos). Insertar bits de sintaxis de C # en reglas de sintaxis existentes sin romper cosas no siempre es práctico A veces, "Esto está disponible en C # ..." realmente merece la respuesta "Sí. Sabes dónde está C # si lo quieres".
:-)

Para mantener la compatibilidad de SemVer , esto debe hacerse en una versión principal (preferiblemente 7.0.0 ).

También vea mi sugerencia arriba ( https://github.com/PowerShell/PowerShell/issues/11379#issuecomment-566756120 ).

@ ExE-Boss Sí, era su comentario al que me refería cuando dije que depender de los #requisitos podría no ser la buena idea que suena al principio.
Y sí, si uno va a romper los scripts existentes, el momento de hacerlo es cuando cambie la versión principal, por lo que esto debe hacerse rápidamente o esperar a la V8

Estas siguen siendo una función experimental y están desactivadas de forma predeterminada en RC-1, que supongo que seguirá siendo la misma para un producto de envío. Eso permite que el comportamiento puede cambiar (o incluso ser función segundo experimental) - sino que también debe bloquear / advertir al intentar crear una variable que termina con ciertos caracteres. Si lo habilito y luego quiero usar scripts con nombres de variables errantes, eso me pone la responsabilidad, por supuesto, significa que los scripts que descargo y que hacen eso simplemente se ven "rotos", lo cual es más tolerable bajo el banner "experimental".

No soy un desarrollador serio de PowerShell; Vine aquí en busca de una solución a un error y me encontré con este problema. Para las secuencias de comandos de la línea de comandos, utilizo principalmente bash. Probablemente pertenezca al grupo demográfico al que Microsoft está tratando de convencer para cambiar a PowerShell Core: desarrollador multiplataforma que necesita un lenguaje de scripting rápido que sea menos molesto que bash.

A menudo, muchas de las decisiones relacionadas con la sintaxis de PowerShell me parecen poco intuitivas para las personas que no son principalmente desarrolladores de PowerShell. La última vez que usé PowerShell para algo serio, me horrorizó la falta de un operador coalescente nulo, o incluso un operador ternario que pudiera usar en su lugar, lo que me llevó a publicar esta popular respuesta de PowerShell en Stack Overflow . Eso fue en 2013, y es fácilmente una de las tres razones principales por las que he evitado principalmente PowerShell desde entonces. El objetivo de un lenguaje de scripting es que quiero algo rápido y sucio con mucho azúcar de sintaxis, no declaraciones detalladas if-else.

@StartAutomating dijo:

Dejando a un lado a las personas profundamente geek, esperaría que $ {obj} ?. Method () o $ obj? .Method () obtengan un pequeño repunte. Hay una década de orientación en la web sobre cómo administrar nulos, y no veo que muchas personas cambien de if ($ obj) {$ obj.Method ()} solo para subirse al tren de la v7

Solo soy n = 1, pero como no desarrollador de PowerShell, estoy totalmente en desacuerdo. El nuevo ? La sintaxis es algo que descubriría con bastante rapidez incluso como alguien que toca PowerShell tal vez una vez cada pocos meses, y definitivamente aumentaría las probabilidades de que use PowerShell en el futuro en lugar de alternativas. Si luego veo que se requiere alguna sintaxis {} loca para la compatibilidad con versiones anteriores, voy a poner los ojos en blanco y volver a lo que ya estaba usando.

No entiendo el énfasis en la compatibilidad con versiones anteriores aquí. PowerShell Core ya rompió la mayoría de mis scripts existentes porque usaban partes de .NET que no están en .NET Core. (No me quejo de eso, .NET Core es genial, pero si vas a romper la compatibilidad con versiones anteriores, esta es tu oportunidad).

En primer lugar, creo que mis comentarios iniciales han creado una confusión de malentendidos.

Mi suposición era que $ {} no sería _ tan extraño para los desarrolladores de PowerShell_, porque ya tendrían que conocer esta forma de sintaxis si alguna vez hubieran incrustado una variable con un? o y subrayar en una cadena.

Gracias por los numerosos recordatorios de que esta sintaxis puede resultar incómoda para los desarrolladores que no son de PowerShell.

La segunda parte de mis comentarios podría haberse perdido en la mezcla: debido a que otras alternativas a la fusión nula han existido durante más tiempo en PowerShell y se usan ampliamente, no sé cuánto aumento tendría la función _con los usuarios de PowerShell existentes_. Quieres entrar y decir "esto es lo único que necesito para comenzar a usar PowerShell", y no puedo discutir tu opinión (aunque podría decir que te estás perdiendo un bosque de características interesantes en busca de un árbol de lenguaje familiaridad).

La tercera parte de mis comentarios parece haber sido exagerada. Hablando como una de las personas que ocasionalmente ha usado variables denominadas como $ IsThisRight? = Prueba: algo, mi primera reacción al escuchar las posibles noticias sobre esta restricción variable fue "bueno, supongo que tendré que cambiar el nombre de algunas variables, de todos modos no estaba obteniendo tanto por eso".

Entonces, para intentar reafirmar y finalizar el hilo:

  1. En mi humilde opinión, $ {} probablemente no será una sintaxis tan extraña para los desarrolladores de PowerShell _existentes_
  2. Le invitamos a pensar que esta característica es genial; para mí es una droga de entrada a la familiaridad con el idioma, y ​​recomiendo encarecidamente a todos los que lean este hilo que aprendan a rockear asignando ifs / foreaches / whiles, etc.
  3. Como uno de los autores afectados, no obtengo algo tan crítico para la misión de un? al final de mis variables, me opondría a cambiar mi código.

O, para ser brevemente breve:

Haz este cambio si quieres. Tengo un pequeño caballo en esta carrera. Si decide hacer esto, difunda el cambio claramente para que cualquier otra persona que haya nombrado variables con un? puede actualizar en consecuencia.

La segunda parte de mis comentarios podría haberse perdido en la mezcla: debido a que otras alternativas a la fusión nula han existido durante más tiempo en PowerShell y se usan ampliamente, no sé cuánto aumento tendría la función con los usuarios existentes de PowerShell.

Definitivamente estoy de acuerdo con eso, y no quiero desestimar tu opinión. Siempre es difícil hacer cambios rotundos como ese, especialmente cuando la alternativa puede ser intuitiva para las personas que ya son usuarios habituales del idioma.

Quieres entrar y decir "esto es lo único que necesito para comenzar a usar PowerShell", y no puedo discutir tu opinión (aunque podría decir que te estás perdiendo un bosque de características interesantes en busca de un árbol de lenguaje familiaridad).

Bueno, tenía la sensación de que la mayoría de los desarrolladores que no son de PowerShell no participan realmente en estas conversaciones; es pura coincidencia que me topara con esto. Es probable que mi uso de PowerShell sea muy diferente al de las personas que escriben bibliotecas y herramientas de código abierto. Pensé que solo daría la perspectiva de alguien que está usando PowerShell menos como un marco elegante y más para realizar tareas extrañas lo más rápido posible. No tiene la intención de desestimar otras opiniones, es solo otra opinión desde una perspectiva que probablemente no se ve con tanta frecuencia aquí.

Un mejor manejo de nulos no es lo único que necesito para comenzar a usar más PowerShell: es lo segundo. El primero fue el soporte multiplataforma, y ​​estoy muy impresionado con lo lejos que ha llegado. Personalmente, el azúcar sintáctico es un gran problema para mí cuando elijo un idioma para hacer una tarea determinada: quiero que la sintaxis facilite lo que estoy haciendo. Si estoy escribiendo algo que necesita ser mantenido por mucha gente, probablemente querré que sea más detallado; pero si estoy escribiendo un script rápido para tareas administrativas o devops, solo quiero una forma rápida de probar cosas como null.

La tercera parte de mis comentarios parece haber sido exagerada. Hablando como una de las personas que ocasionalmente ha usado variables denominadas como $ IsThisRight? = Prueba: algo, mi primera reacción al escuchar las posibles noticias sobre esta restricción variable fue "bueno, supongo que tendré que cambiar el nombre de algunas variables, de todos modos no estaba obteniendo tanto por eso".

Favorecer la compatibilidad con versiones anteriores es siempre un argumento justo. Sin embargo, en este caso particular, parece que muchos scripts de PowerShell están a punto de romperse debido a la transición a Core de todos modos. Cuando la compatibilidad con versiones anteriores se rompe de forma regular, es frustrante, pero estaría a favor de que cualquier cambio importante se produzca de una vez, lo que significa que probablemente sea un buen momento.

En un escenario en el que intenta apuntar a un grupo demográfico completamente nuevo, es decir, desarrolladores no centrados en Windows, podría ser necesario realizar algunos cambios importantes para competir con lo que ya existe.

Si decide hacer esto, difunda el cambio claramente para que cualquier otra persona que haya nombrado variables con un? puede actualizar en consecuencia.

Para ser claros, estoy de acuerdo con la necesidad de optar por la función, eso no me disuadiría de usar PowerShell. Ya estoy acostumbrado a necesitar un montón de comandos set en la parte superior de mis scripts de bash. No lo consideraría ideal, pero tendría poco impacto en mi decisión de usar PowerShell siempre que esté bien documentado.

Personalmente, el azúcar sintáctico es un gran problema para mí cuando elijo un idioma para realizar una tarea determinada.

¡Entonces has venido al idioma correcto! PowerShell es positivamente saccrino con azúcar sintáctico.

Para educar a aquellos que no saben, puede "fusionar nulos" de varias formas en PowerShell:
~$ ListWhereNotNull = $ null, 1, 2, $ null, 3 -ne $ null # Esto comprobará que cada elemento no es nulo y devolverá una lista de 1,2,3$ FirstNonNull = @ ($ null, 1, $ null, 2 -ne $ null) [0] # Esto devolverá el primer no nulo$ lastNonNull = @ ($ null, 1, 2, $ null -ne $ null) [- 1] # Esto devolverá el último no nulo, usando un índice negativo$ TheObjectPipelineWay = $ null, 1, $ null, 2 | Seleccionar objeto: primero 1$ TheAssigningForeachWay = foreach ($ elemento en $ null, 1, 2, $ null, 3, 4) {if ($ elemento -ne $ nulo) {$ elemento;



Eso es suficiente por ahora, pero debería demostrar que la sintaxis del lenguaje tiene mucha flexibilidad. Todas esas demostraciones funcionarán desde PowerShell 1.0

Sin embargo, en este caso particular, parece que muchos scripts de PowerShell están a punto de romperse debido a la transición a Core de todos modos.

En esto estás equivocado. Core en realidad tiene muy pocos cambios de ruptura de sintaxis, y más módulos de los que cree que funcionan bien en el núcleo (+ - algunos problemas con Linux vs Windows pathing / newlines). En términos más prácticos, es menos probable que su módulo funcione en Core si tiene algún tipo de dependencia API específica de la plataforma Windows (por ejemplo, no voy a contener la respiración para que mis envoltorios WPF funcionen en Linux).

Todavía estoy bien de cualquier manera con el cambio de sintaxis: solo sentí que dos de las afirmaciones en la última respuesta debían abordarse y llamarse.

Re: Azúcar sintáctico, mi mente sarcástica cita la película "Snatch": "Ya soy lo suficientemente dulce"
Re: romper los potenciales de cambio en Core: Tenemos pocos ahora, y mantengamos las cosas así tanto como podamos.

@StartAutomating : ¿Estás diciendo que no puedo disparar? porque todavía hay lugar para más azúcar, creo 😜

Para educar a aquellos que no saben, puede "fusionar nulos" de varias formas en PowerShell:

Me di cuenta de eso (esa es mi propia respuesta que escribí), pero me sentí como una torpeza. Es una idea genial con mucho potencial, pero como señaló un comentarista en esa respuesta de Stack Overflow, la evaluación no se corta. El operador ?. significa que, por lo general, esto ya no es un problema al encadenar llamadas / propiedades. Sin embargo, en 2013, no estaba disponible e, incluso entonces, están sucediendo muchas cosas en estos ejemplos, y puede ver en los comentarios cómo las personas encontraron eso confuso. Tuve que desglosar exactamente lo que hacen estos ejemplos, símbolo por símbolo.

En esto estás equivocado. Core en realidad tiene muy pocos cambios de ruptura de sintaxis, y más módulos de los que cree que funcionan bien en el núcleo (+ - algunos problemas con Linux vs Windows pathing / newlines). En términos más prácticos, es menos probable que su módulo funcione en Core si tiene algún tipo de dependencia API específica de la plataforma Windows (por ejemplo, no voy a contener la respiración para que mis envoltorios WPF funcionen en Linux).

Solo hablo por experiencia a este respecto, en lugar de analizar y recopilar estadísticas adecuadas. Por ejemplo, antes de Core, podría generar una contraseña con [System.Web.Security.Membership]::GeneratePassword(32, 6) , pero System.Web no está disponible en Core. (Sé que probablemente podría cargar el antiguo System.Web.dll, pero prefiero no hacerlo si no tengo que hacerlo). Los cambios de sintaxis son en realidad más fáciles de resolver para mí que las API que faltan. Generar un montón de secretos de Docker para un devkit parece un gran lugar para usar un script rápido, pero sorprendentemente es más detallado en PowerShell Core que en bash. Por supuesto, las soluciones de bash a menudo se ven bastante feas.

Re: Azúcar sintáctico, mi mente sarcástica cita la película "Snatch": "Ya soy lo suficientemente dulce"

Eso depende. A veces siento que PowerShell es extraordinariamente agradable. Tratar con null es una de esas áreas clave en las que generalmente no me siento así, aunque diría lo mismo para muchos idiomas. También encuentro que la instrucción if-else de PowerShell en línea tradicional es engorrosa en comparación con un operador ternario o el encadenamiento && y || bash. Dado que este es uno de los problemas clave que hizo que me alejara de PowerShell hace años, pensé que valía la pena mencionarlo.

Por otro lado, trato con una amplia variedad de idiomas a diario, y no importa lo que esté escribiendo, siempre pienso, "¡esto sería mucho más fácil en el idioma X!", Y luego cuando Estoy usando el lenguaje X, estoy pensando, "¡esto sería mucho más fácil en el lenguaje Y!" Pero bueno, si tengo la oportunidad de decir eso en voz alta y tal vez influir en la dirección del idioma, lo tomaré.

No estoy realmente interesado en cambiar de opinión aquí, solo en agregar otra perspectiva.

@StartAutomating : ¿Estás diciendo que no puedo disparar? porque todavía hay lugar para más azúcar, creo 😜

¡Siempre me ha gustado un poco de azúcar de sintaxis nueva! Selecciono lenguajes de programación como un niño que va a pedir dulces en Halloween.

@Zenexer : Me gusta mucho Powershell . Soy lo suficientemente afortunado de hacer PS de manera extensiva y diaria en el trabajo. Me encanta la flexibilidad, especialmente la simbiosis con .NET, ¡oh hombre! Por eso me importa su dirección y por eso la sintaxis ${...}? es tan difícil de aceptar para mí.

Buenos puntos, pero permítanme aclarar algunas cosas.

Para bien o para mal, PowerShell hasta la fecha no ha utilizado versiones semánticas.
Una nueva versión principal solo trajo _nuevas_ características, con un firme compromiso de compatibilidad con versiones anteriores.
Los cambios importantes comenzaron a aparecer en Core, en parte por necesidad debido a la multiplataforma.
Se eligió la versión principal v7.0 para señalar un _ aumento_ en la compatibilidad con versiones anteriores: un intento de proporcionar un reemplazo viable para Windows PowerShell.

Si bien muchos problemas históricos no se pueden solucionar sin romper seriamente el código existente y, por lo tanto, solo se pueden ofrecer como características opcionales (opt-in),
Los cambios de ruptura del

Como se argumentó en el OP, el caso que nos ocupa me parece un cambio de cubo 3; Si bien sabemos que _algunos_ scripts se romperán ( @StartAutomating sabrá cómo arreglar sus scripts), los datos sugieren que tales casos son bastante raros.

¿Por qué es raro?

Porque el uso de ? en nombres de variables no se le ocurre a la mayoría de las personas, y si lo hace, no deberían esperar que funcione _sin {...} _: Si todo lo siguiente funciona solo con {...} , ¿por qué esperaría que fuera diferente para ? ?: . , ; ( : no se puede usar en absoluto, porque siempre se interpreta como un separador de accionamiento)

Permitir nombres como $var? o $va?r sin requerir también {...} era una permisividad que pedía problemas, y el problema ha llegado: se interpone en el camino de la evolución del idioma, como en este caso.

Hay 3 nuevas características relacionadas (al menos relacionadas en forma sintáctica) inspiradas en C #:

  • Condicionales ternarios: será una característica de buena fe en 7.0

    • (Get-Date).Second % 2 ? 'odd' : 'even'

  • Operador de coalescencia nula: será una característica de buena fe en 7.0

    • $var ?? 'default' y $var ??= 'default'

  • El operador condicional nulo, el problema en cuestión, será una característica _experimental_ en 7.0

    • $possiblyNull?.ToString()

Todos ellos tienen ? como parte de su sintaxis, y todos ellos se ven afectados por la carga del análisis heredado $var? , aunque ?. más obvio es:

  • Si bien podría argumentar que algo como $var?1:0 solo es de interés para los golfistas de código, funcionaría bien si ? tuviera una función sintáctica; actualmente debe usar un _space_ antes de ?

  • Por el contrario, me parece razonable que alguien intente $var??='bar' (quizás menos $baz = $foo??$bar ), que actualmente también requiere un espacio.

Es decir, si nos atenemos al análisis actual de $var? , estamos cargando _tres_ características nuevas (aunque en diversos grados), 2 de las cuales ya están disponibles en general (no son experimentales).

El problema de requerir {...} _no_ es solo el inconveniente de escribir: se trata tanto de _no esperar la necesidad_ en primer lugar, y luego _ olvidar la necesidad_ (con un mal funcionamiento potencialmente sutil) - porque es tan contrario a la intuición.

@ ExE-Boss, estoy de acuerdo con @jhoneill en que implementar un comportamiento condicional de versión sería problemático. No creo que sea necesario sobrecargar el motor con una carcasa especial de compatibilidad con versiones anteriores en este caso.

@ mklement0: No es natural / contra-intuitivo / te-nombre-it. Los corchetes son duros, los corchetes rizados son aún más duros. No hay necesidad de ellos en este caso.

@ mklement0 ya que parece que se está rompiendo el acuerdo ... Creo que es un buen resumen.

Todavía no estoy completamente convencido al agregar construcciones C # a PowerShell. Ya tenemos problemas en muchos lugares que simplemente no hay suficientes símbolos en el teclado. ¿Es ">" "mayor que" o "redirigir a"? Es "=" asignación o prueba-igualdad (diferentes idiomas usan: = para asignar o == para igualdad). Las personas que saltan de un idioma a otro se olvidarán de usar -eq o -gt y estamos atrapados con eso ... Is + suma aritmética, o concatenación de cadenas / matrices. * es tanto una multiplicación como un comodín. % es Modulo y un alias para forEach-object.

Pobre ? Es el alias de Where-Object. Y es el nombre de una variable automática. Pero intente hacer Get-Alias ? o Get-Variable ? ... y luego hace el trabajo de un comodín.

Haga Get-PSdrive y verá "variable", etc. no tiene un ":" al final del nombre, agregar uno: a un nombre hace que se trate como una unidad. A menos que, por supuesto, refiera haber escrito "$drive="C" y seguirlo con "$drive:temp" Cualquier persona nueva en PowerShell (y algunos expertos, cuando no están pensando en ello) se desconcertará al saber que ' Acabo de inventar una "unidad" de alcance con nombre.

Entonces, en contra de eso ... uno podría decir 'Crear una nueva sintaxis con más usos para ":" y "?" ¿estas loco?'
IIRC, en twitter @BrucePay describió al operador ternario como "No muy PowerShelly" y yo tiendo a estar de acuerdo. Pero las voces "Make it more like C #" ganaron el día ...

La mayoría de la gente escribiría $PSEdition.length -gt 2
pero $PSEdition. length-gt2 también funciona (incluso puede tener un salto de línea entre las dos mitades) "." también está sobrecargado sirviendo como "directorio actual", "ejecutar en este ámbito" y "miembro". Sin embargo, en PS V1-V5 no puedo pensar en ningún operador que requiera un espacio inicial; "." es extraño en romperse si hay uno presente. Pero, para analizar, el operador ternario _podría_ necesitar un espacio inicial
$host.debuggerEnabled?0:1 está bien porque "?" asumido _no_ a parte de una propiedad
$IsCoreCLR?0:1 no es porque "?" se supone que es parte del nombre de la variable.

Lo mismo es cierto para los operadores coalescentes nulos.
$null?? "default" necesita un espacio $true.length?? "default" no lo hace.

Deje el caso de uso original en la parte superior del problema a un lado por un momento. Los casos anteriores muestran que algo no funciona del todo. Se solucionaría si los nombres de las variables que contienen "?" necesitaba estar entre corchetes como nombres que contienen "." o "+" o ":", y @StartAutomating, ¿ quién es el usuario principal de "?" en nombres piensa que es un _break que podría manejarse_. Incluso antes de que lleguemos a los miembros nulos, parece que 'encuentra una manera de hacerlo ya'

Creo que hay un breve período de tiempo para poner en una función experimental que requiere? para estar entre corchetes en los nombres, y si esa función está ACTIVADA, ¿cuál debería ser la predeterminada? ¿Todas las novedades? los operadores deben analizar en consecuencia. Solo si esa función está DESACTIVADA, el analizador requeriría espacios o llaves con estos operadores. Ese es un cambio importante, algunos scripts (un pequeño número no cuantificable) se romperían a menos que se estableciera una opción (y no permitir el nombre de la variable debería evitar $ IsIt ? .tostring () es el primer error en un script, por lo que los scripts deberían fallar, no ejecutarse peligrosamente). e idealmente eso no debería suceder con una versión puntual (tiene razón sobre que PowerShell no usa realmente sem-ver, pero sigue siendo un buen principio)

El problema de requerir {...} no es solo el inconveniente de escribir: se trata tanto de no esperar la necesidad de hacerlo en primer lugar, y luego olvidar la necesidad de hacerlo (con un mal funcionamiento potencialmente sutil), porque tan contrario a la intuición.

Este es un buen punto y es en gran parte lo que me molestaría de ${...?} en la práctica. Incluso si sé usar {} , existe una gran posibilidad de que eventualmente lo olvide, y eso podría resultar en errores sutiles que no son obvios al principio, ya que probablemente seguirá siendo sintácticamente válido. $var??='bar' es un buen ejemplo de eso, aunque normalmente soy bastante cuidadoso con el espaciado.

Gracias, @jhoneill , esa es una buena ilustración de cuántos símbolos hacen una gran cantidad de funciones dobles (triples, ...) en PowerShell.
En su mayor parte, una vez que conoce todos los usos, eso no es un problema, por las siguientes razones:

  • diferentes contextos (por ejemplo, * como operador frente a * como carácter comodín).

    • Los diferentes contextos para ? realidad tienen algo en común, en un cierto nivel de abstracción: puede concebir el ? en Get-ChildItem | ? Target , $var?.Target , $? , $var ?? 'none' ya que todo implica una especie de _pregunta_ y, por lo tanto, _prueba_ o _comportamiento_condicional_.

  • comportamiento polimórfico sensible (por ejemplo, + realizando concatenación con cadena).

Un ejemplo problemático es & (operador de llamada frente a operador en segundo plano), que se usa en el mismo contexto con semántica diferente, dependiendo solo de su _posición_.

Se solucionaría si los nombres de las variables que contienen "?" necesitaba estar entre corchetes como nombres que contienen "." o "+" o ":"

De hecho, y eso vale la pena repetirlo: la capacidad de usar ? en nombres de variables no desaparecerá, pero tendrá que usar {...} en el futuro: ${var?} = @{ foo=1 }; ${var?}?.foo

Como demuestra, desde la perspectiva de alguien que razonablemente _no_ espera que ? sea ​​un carácter válido. . para dot-sourcing es la excepción justificable; ? como Where-Object alias que requiere un espacio después puede ser sorprendente (por ejemplo, gci|?target no funciona), pero los nombres de comando _do_ deben separarse de sus argumentos con espacios en blanco).

Ser capaz de poner espacios en blanco entre . y el nombre de un miembro ( $obj. foo ) tiene sentido principalmente en declaraciones de varias líneas, aunque la forma posiblemente más legible, familiar de otros idiomas, es (también) permitir espacios en blanco _antes_ del . :

# This does NOT work - the . must be *immediately after* the expression / member
(Get-Date)
  .ToUniversalTime()  
  .TimeOfDay

Ser capaz de hacerlo reflejaría la capacidad recientemente agregada de colocar | en la línea _siguiente_:

 # Already works in PS Core
Get-Date
  | % ToUniversalTime
  | % TimeOfDay

Sin embargo, esto introduciría ambigüedades irresolubles con . como operador de origen de puntos y nombres de comando que comienzan con . , aunque sea poco probable. (por ejemplo, . foo en una nueva línea podría ser un acceso a la propiedad o un comando que proporciona una función o script llamado foo (que debería estar en $env:PATH ) ; .foo podría ser el nombre de un _comando_).

RFC múltiples continuación de línea @KirkMunro 's, mientras que se centró en lo que _commands_ a ocupar varias líneas, nos daría la mejor solución - ver https://github.com/PowerShell/PowerShell-RFC/pull/179#issuecomment -498734875.

En cuanto a la función experimental que está siendo activada por defecto: Afortunadamente, _preview_ lanzamientos - pero no liberan candidatos - se dirige ahora _todas_ características experimentales de forma predeterminada; sin embargo, tenga en cuenta que esto se anula persistentemente si alguna vez ha ejecutado Enable-ExperimentalFeature ya sea _without_ -Scope o con -Scope CurrentUser con las funciones que haya habilitado allí.

@ mklement0 Si bien estoy de acuerdo básicamente con todo eso, me temo que podríamos descarrilar un poco esta discusión en particular con los detalles adicionales que tienes allí; Si desea abordar la continuación de línea alrededor del operador de propiedad de puntos, es posible que desee abrir un nuevo problema para eso si aún no hay uno. 🙂

Estoy de acuerdo en que continuar permitiendo ? como un carácter de nombre de variable normal es, en el mejor de los casos, extremadamente confuso para los usuarios, ya que el uso del personaje de repente hace que las cosas sean sensibles a los espacios en blanco, e históricamente al menos, no creo que los nombres de variable tengan _ever_ ha sido sensible al espacio en blanco en el pasado.

Introducir esa ambigüedad es, creo, más un problema que hacer un cambio rotundo y no permitir que ? use en un nombre de variable. Con el cambio radical, podemos crear fácilmente una regla PSSA para alertar a los autores de que la sintaxis ya no está permitida. Sin embargo, lo contrario no es tan fácilmente cierto y, a diferencia del cambio radical, no necesariamente se producirá un error; muy fácilmente puede terminar con usuarios haciendo referencia accidentalmente a la variable incorrecta en silencio, sin saber que eso es lo que están haciendo.

@ PowerShell / powershell-Committee discutió esto, habíamos analizado el corpus de scripts de PowerShell y hubo bastante uso de nombres de variables que terminaban con el signo de interrogación. Como tal, no podemos dividir esas personas y llaves alrededor de los nombres de las variables de PowerShell es una característica del lenguaje existente para distinguir el nombre de la variable de otros caracteres (como dentro de una cadena).

habíamos analizado el corpus de scripts de PowerShell y hubo bastante uso de nombres de variables que terminaban con el signo de interrogación

Gracias, @ SteveL-MSFT. ¿Puede compartir los resultados de este análisis?

Sí, tendría curiosidad por ver algo de eso, porque sé que algunos miembros de la comunidad hicieron su propio análisis y no obtuvieron nada parecido a ese tipo de resultado.

También me gustaría ver los resultados y el método utilizado. Hice mi propio análisis del PowerShell Corpus anoche usando el AST que tardó casi 6 horas en completarse en mi máquina. Hubo una pequeña cantidad de archivos que no se pudieron analizar debido a que Windows Defender lo puso en cuarentena.

Encontré 11 nombres de variables únicos en 8 archivos que terminan en un signo de interrogación (excluyendo $? Que apareció 8846 veces). Hubo un total de 1,895,983, variables únicas en 408,423 archivos. Eso significa que el 0,0006% de todas las variables únicas de PowerShell en el corpus tiene un nombre de variable que termina con un signo de interrogación.

Aquí están los 8 archivos y las 11 variables únicas:

File                 : C:\Temp\PowerShellCorpus\Github\alanrenouf_PowerActions\Kill-VM.ps1
QuestionMarkVars     : vParam?
QuestionMarkVarCount : 1

File                 : C:\Temp\PowerShellCorpus\Github\anthonywlee_PowerShell\Send_Notification.ps1
QuestionMarkVars     : To?
QuestionMarkVarCount : 1

File                 : C:\Temp\PowerShellCorpus\Github\aspear_My-PowerCLI-scripts\my-labotomize-vms.ps1
QuestionMarkVars     : vParam?
QuestionMarkVarCount : 1

File                 : C:\Temp\PowerShellCorpus\Github\jlundstrom_Scripts\Powershell\7Zip SFX Creator\Build.ps1
QuestionMarkVars     : Title?
QuestionMarkVarCount : 1

File                 : C:\Temp\PowerShellCorpus\Github\pslaughter_Working\Setup-Host.ps1
QuestionMarkVars     : HostIp?
QuestionMarkVarCount : 1

File                 : C:\Temp\PowerShellCorpus\Github\stevenayers_PowerShell-Scripts\Random Functions\Add-OutlookConferenceRegionNumber.ps1
QuestionMarkVars     : {key1?, key2?, key3?, key4?}
QuestionMarkVarCount : 4

File                 : C:\Temp\PowerShellCorpus\Github\unixboy_powershell-stufff\stopvm.ps1
QuestionMarkVars     : vParam?
QuestionMarkVarCount : 1

File                 : C:\Temp\PowerShellCorpus\PowerShellGallery\PsHg\0.6.2\PsHg.psm1
QuestionMarkVars     : PsHg?
QuestionMarkVarCount : 1

Este es el script utilizado para generar el informe:

Get-ChildItem -Path C:\Temp\PowerShellCorpus -Include *.ps1, *.psm1 -File -Recurse | ForEach-Object -Parallel {
    try {
        $content = $PSItem | Get-Content

        $ast = [System.Management.Automation.Language.Parser]::ParseInput($content, [ref]$null, [ref]$null)
        $variables = $ast.FindAll({$args[0] -is [System.Management.Automation.Language.VariableExpressionAst ]}, $true)

        # First query because I forgot to exclude $? variables
        # $nonQuestionMark = $variables | Where-Object VariablePath -notmatch '\?$' | Select-Object -Unique
        # $QuestionMark = $variables | Where-Object VariablePath -match '\?$' | Select-Object -Unique

        $nonQuestionMark = $variables |
        Where-Object VariablePath -notmatch '\?$' |
        ForEach-Object { $_.VariablePath.UserPath.ToString() } |
        Select-Object -Unique

        $QuestionMark = $variables |
        Where-Object { $_.VariablePath -match '\?$' -and $_.VariablePath.UserPath -ne '?' } |
        ForEach-Object { $_.VariablePath.UserPath.ToString() } |
        Select-Object -Unique

        $output = [pscustomobject]@{
            File = $PSItem
            QuestionMarkVars = $QuestionMark
            QuestionMarkVarCount = $QuestionMark.Count
            NonQuestionMarkVars = $nonQuestionMark
            NonQuestionMarkVarCount = $nonQuestionMark.Count
        }

        $output
    }
    catch {
        throw
    }
}

Avíseme si hay algún problema con el método utilizado para generar este informe. A continuación, se muestra un script de ejemplo que se utiliza para probar la auditoría.

$abc = 'test'
$isAwesome? = $true
$?

$test = {
    $?
    $isabc? = { $adce? = 'test' }
    ${def?} = 123
    ${is a bad var name?} = $isabc?
}

Resultados en:

File                    : C:\temp\variable.ps1
QuestionMarkVars        : {isAwesome?, isabc?, adce?, def?, is a bad var name?}
QuestionMarkVarCount    : 5
NonQuestionMarkVars     : {abc, true, test}
NonQuestionMarkVarCount : 3

Como dicen en la clase de matemáticas, "por favor muestra tu trabajo" 😄

Excelente investigación, @ThomasNieto , gracias.

Creo que potencialmente puede eliminar incluso más casos, dado que el formulario ${...} continuará funcionando con el cambio propuesto; por ejemplo, solo debemos preocuparnos por $isAwesome? , no por ${isAwesome?}

Con su archivo de ejemplo:

$variables.Extent.Text.Where({ $_ -match '(?<!\$)\?$' }) | Select-Object -Unique

solo obtenemos:

$isAwesome?
$isabc?
$adce?

Es decir, ${def?} y ${is a bad var name?} no necesitan ser considerados.

PD: Probablemente también sea mejor usar $PSItem | Get-Content -Raw para leer todo el script como una sola cadena.

@ mklement0 Eso es correcto, agregué esos casos justo antes de la publicación para asegurarme de que el script no excluyera las variables con la notación de llaves todas juntas.

Después de actualizar el script con sus recomendaciones, el archivo de prueba produce el siguiente resultado como esperamos.

File                    : C:\temp\variable.ps1
QuestionMarkVars        : {isAwesome?, isabc?, adce?}
QuestionMarkVarCount    : 3
NonQuestionMarkVars     : {abc, true, test}
NonQuestionMarkVarCount : 3

Ejecuté el script actualizado en esos 8 archivos y ninguno de ellos usa la notación de llaves, por lo que el resultado sigue siendo que 11 variables únicas se verían afectadas con el cambio propuesto.

Guión actualizado:

Get-ChildItem -Path C:\Temp\PowerShellCorpus -Include *.ps1, *.psm1 -File -Recurse | ForEach-Object -Parallel {
    try {
        $content = $PSItem | Get-Content -Raw

        $ast = [System.Management.Automation.Language.Parser]::ParseInput($content, [ref]$null, [ref]$null)
        $variables = $ast.FindAll({$args[0] -is [System.Management.Automation.Language.VariableExpressionAst ]}, $true)

        $nonQuestionMark = $variables |
        Where-Object VariablePath -notmatch '\?$' |
        ForEach-Object { $_.VariablePath.UserPath.ToString() } |
        Select-Object -Unique

        $QuestionMark = $variables |
        Where-Object { $_.VariablePath -match '\?$' -and $_.VariablePath.UserPath -ne '?' -and $_.Extent.Text -match '(?<!\$)\?$' } |
        ForEach-Object { $_.VariablePath.UserPath.ToString() } |
        Select-Object -Unique

        $output = [pscustomobject]@{
            File = $PSItem
            QuestionMarkVars = $QuestionMark
            QuestionMarkVarCount = $QuestionMark.Count
            NonQuestionMarkVars = $nonQuestionMark
            NonQuestionMarkVarCount = $nonQuestionMark.Count
        }

        $output
    }
    catch {
        throw
    }
}

Gracias, @ThomasNieto . Esperaba que pudiéramos reducir el porcentaje al 0.0005% ... (es broma).

Entonces, el comité debe haber usado un - ¿privado? - corpus.

Entonces, además de decidir el tema en cuestión, surge la pregunta: ¿es necesario actualizar el corpus disponible públicamente ? ¿Se mantiene oficialmente (actualmente con fecha de 26 de julio de 2017)?

@ SteveL-MSFT, ¿podría aclararlo?

@ mklement0 Lo

En https://github.com/PowerShell/PowerShell-RFC/pull/223#discussion_r318340339 @adityapatwardhan hizo un análisis utilizando el mismo corpus público que usé y obtuve un 62% con variables que terminaban con un signo de interrogación. No vi una respuesta sobre cómo se le ocurrió ese porcentaje. ¿El comité de PS utilizó ese análisis para determinar esta solicitud?

Mirando el comentario vinculado, creo que lo que @adityapatwardhan está diciendo es que _entre todas las variables que tienen ? en su nombre_ el 62% tiene un ? como el último carácter del nombre.

Por el contrario, su análisis de la prevalencia real de las variables (sin llaves) que terminan en ? (como porcentaje del uso de variables en general) parece mucho más relevante.

En este punto, parece que el comité solo está diciendo "no queremos" y, si ese es el caso, ¿por qué incluso tener esta función si, como resultado, va a ser torpe y no se usará?

@ThomasNieto también hice un análisis en algún lugar sobre estos temas y

@mburszley Gracias por hacer su análisis. Quería usar otro método (ast vs regex) para confirmar los resultados que obtuviste el año pasado.

Te escucho, @mburszley , pero re:

el comité solo dice "no queremos"

Si esta fue realmente una decisión _fiat_, una que no se basara en el análisis del uso del mundo real, no sería un buen augurio para la comunidad en general, especialmente dado que la razón _declarada_ fue una base en tal análisis.

Por lo tanto, debemos darle al comité la oportunidad de mostrar el análisis de uso del mundo real en el que se basó la decisión, o reconocer que el análisis se quedó corto y reconsiderar la decisión.

¿Qué pasa con mi sugerencia en https://github.com/PowerShell/PowerShell/issues/11379#issuecomment-566756120 ?

Sugiero hacerlo de modo que $obj?.Method() funcione como $<var i="9">obj</var> ?. <var i="12">Method</var>() de forma predeterminada, y solo vuelva a $<var i="14">obj?</var> . <var i="17">Method</var>() cuando $<var i="19">obj</var> no existe en el alcance, pero $<var i="21">obj?</var> sí y #requires ‑Version no es v7 +.

Espero que aborde la mayoría de los problemas de compatibilidad con versiones anteriores.

@ThomasNieto

@ mklement0 es correcto, quise decir que entre las variables que usan ? , el 62% lo usa al final.

De los resultados que se muestran en el comentario aquí, parece que todas las variables que usan ? usan al final. De ahí la propuesta de hacer un cambio rotundo.

Si se elimina el requisito de {..} , el script seguirá analizando, pero la semántica será diferente, lo que en mi opinión es muy peligroso.

Claro ... pero el análisis muestra que literalmente casi nadie los está usando.

Al igual que cualquier otro cambio importante, puede anunciarse, introducirse como una función experimental durante un tiempo y luego documentarse y establecerse.

No veo ninguna razón para recomendar una sintaxis esotérica cuando ? como carácter identificador no se utiliza en realidad en un número de casos remotamente significativo.

Si se elimina el requisito de {..}, el script seguirá analizando, pero la semántica será diferente, lo que en mi opinión es muy peligroso.

¿No le preocupa también que las personas usen ?. la forma en que lo hacen en C / C ++ / C # y obtengan el resultado incorrecto sin indicios de que hayan hecho algo mal? Por ejemplo:

PS> $res = "My length is 15"
PS> $res?.Length
0

Claro, el modo estricto encontrará esto, pero no sé si mucha gente usa el modo estricto. Entonces, para evitar que un porcentaje muy pequeño de scripts se rompa, parece que estamos preparando a mucha gente para fallar usando ?. . No estoy loco por la compensación. Dicho esto, definitivamente entiendo el deseo de evitar romper a la gente, incluso si se trata de un número muy pequeño de personas.

Tal vez PowerShell necesite un equivalente de Enable-ExperimentalFeature llámelo Enable-PSFeature , para funciones que ya no están en el estado experimental pero que no están listas para activarse de forma predeterminada . Para scripts que se ejecutan con código como este $?.GetType() , podría comenzar a emitir advertencias de que en el futuro esto se romperá y que debe usar ${?}.GetType() lugar. Los scripts individuales podrían habilitar la función con #requires -version 7.1 -feature PSNullConditionalOperators . Entonces, tal vez cada vez que llegue PS 8, la función podría estar habilitada de forma predeterminada.

El ejemplo de @rkeithhill va directo al grano.
Olvídese por un momento de lo que sabe sobre el funcionamiento actual de PowerShell. Cuando veas

PS> $res?.Length

Parece _nombre, operador, nombre_, que por supuesto lo es.
¿Qué tendría más sentido: _ "?" es parte del operador_, o _ "?" es parte del primer nombre-elemento_?
Si se necesitan estos operadores (y creo que "no son PowerShelly") es una elección entre algo que se utilizará incorrectamente y una pequeña cantidad de rotura.

Comenzaría a poner reglas en psScriptAnalyzer _ahora_ para decir que es de mal estilo usar ciertos caracteres [legales] en los nombres de las variables.

No me gusta Enable-feature . Alguien lo pone en un script para hacer que algo que quiere usar funcione, y un script antiguo se rompe si se ejecuta después del nuevo (pero funciona el resto del tiempo). He visto que _ el script X de terceros falla después de ejecutar el script Y_ porque Y activa el modo estricto y X depende de que esté desactivado; los autores de ambos guiones están en mis libros malos.

Empezaría a poner reglas en psScriptAnalyzer ahora para decir que es de mal estilo usar ciertos caracteres [legales] en los nombres de las variables.

Aquí está el problema: si cambia la forma en que PowerShell tokeniza las variables, PSScriptAnalyzer también las verá de manera diferente. Si tiene una variable como $res? , busque algo como esto:

{
$res?.Length
}.Ast.EndBlock.Statements[0].PipelineElements[0].Expression.Expression.VariablePath.UserPath

te dio

res?

PERO, si cambia la forma en que se analiza PowerShell en, digamos, 7.2, obtendrá


Ahora PSScriptAnalyzer no puede ver el ? , porque ya no es parte del nombre de la variable (por definición, ya que es el cambio que estamos tratando de probar). Esto se debe a que PSScriptAnalyzer es un módulo de PowerShell y reutiliza el mismo analizador que se incluye en PowerShell. (Y sería totalmente insostenible y una receta para el desastre si intentara reimplementarlo).

Hay formas de evitar esto, como compilar condicionalmente PSScriptAnalyzer (y enviar un ensamblaje completamente nuevo) para probar la nueva lógica 7.2 para proporcionar el mismo diagnóstico. Pero luego ha aumentado la complejidad en todas partes (o, alternativamente, ha disminuido su superficie de prueba) para dar servicio a un escenario algo cosmético.

Peor aún, todas las demás herramientas que también dependen del AST no tendrán la capacidad de soportar este tipo de complejidad, incluso si PSScriptAnalyzer lo hace. La mayoría de las personas que crean herramientas de orientación de AST no compilarán con versiones menores individuales de PowerShell para una corrección perfecta. Probablemente ni siquiera se enteren de un cambio tan pequeño, especialmente porque no es un error en ninguna versión; es la forma más insidiosa de romper el cambio, donde un comportamiento silenciosamente se convierte en otro.

Eso es básicamente lo que hace que los cambios a nivel de analizador / tokenizador como este sean más peligrosos. Incluso PSScriptAnalyzer debe hacer todo lo posible para repararlos correctamente, lo que significa que deben sopesarse frente a cambios tan onerosos.

@rjmholt , aunque creo que todos podemos apreciar lo complicado que es un cambio como este _ en principio_, en este caso particular no importa _en la práctica_, y no creo que ni siquiera se necesite una regla de PSSA:

  • El análisis de @ThomasNieto ha demostrado que, si creemos que el corpus es representativo de todo el código de PowerShell que existe, _virtualmente nadie usa variables con nombres que terminan en ? _.

  • Si tuviera que adivinar, la mayoría de la gente ni siquiera pensaría en usar tales nombres de variables, porque no esperarían que funcionen (incluido yo), y probablemente ni siquiera notarían la excepción de que el $? automático bash ).

    • De hecho, mirando más de cerca el análisis de 8 enumerados:

      • 5 contienen solo _falsos positivos_, es decir, usos de variables dentro de _cadenas_ como "Are you sure you want to kill $vParam?" ; de hecho, estos usos están _quebrantados_ y revelan que los autores del script _no_ esperaban que ? fueran parte del nombre de la variable.

      • 2 de esos archivos ya no están disponibles públicamente.

      • Solo _1_ de ellos es un uso de buena fe de ? -variables finales - como variables _Boolean_ (p. Ej., $key1? ), que, aparte, por lo tanto, _no_ se combinan con el operador de acceso a miembros. , . / cc @ stevenayers.

Basándome en lo anterior, esto me parece un cubo de texto improbable del área gris , que por lo tanto es _aceptable_ - y creo que los _beneficios_ de este cambio se han argumentado de manera persuasiva.

_Documentar_ el cambio de comportamiento (con una justificación) debería ser suficiente.

Por supuesto:

  1. esto se basa en que (a) el análisis sea correcto y (b) el corpus disponible públicamente sea representativo.

  2. está totalmente en desacuerdo con la afirmación del comité de que "hubo bastante uso de nombres de variables que terminan con el signo de interrogación" (lo cual, por supuesto, solo sería un problema si dichos nombres de variables no se incluyen en {...} ).

Partiendo del supuesto de que 1. es verdadero (no hemos escuchado nada que respalde 2.), tenemos dos opciones:

  • Quítese la tirita y simplemente desautorice ? en los nombres de variables en el futuro, a menos que esté incluido en {...} (con la obvia excepción de $? ); esto romperá lo extremadamente pequeño proporción de scripts que actualmente se basan en eso.

    • Es decir, algo como $key1? que no va seguido de . ni de otro ? ( $key1??'else' ) ni de una expresión ternaria ( $key1?1:0 ) causaría un _ error del analizador_.

      • Como muestran los ejemplos de operadores ternarios y de coalescencia nula, ellos también se beneficiarían de este cambio; actualmente, necesita un _espacio_ - $key1 ?1:0 / $key1 ??'else' - o {...} - ${key1}?1:0 / ${key1}??'else'

    • Dentro de _cadenas expandibles_, ? ya no se consideraría parte de un nombre de variable (no {...} -enclosed), por lo que en realidad _corregiríamos_ scripts existentes que han utilizado cadenas por error como "Are you sure you want to kill $vParam?" . 😁
  • Si realmente sentimos que debemos evitar romper la pequeña proporción de scripts existentes, _podríamos_ considerar la lógica propuesta por @ ExE-Boss , pero no creo que queramos introducir esta complejidad conceptual, y mucho menos la complejidad de implementación (que yo realmente no puedo hablar).

@ SteveL-MSFT

Gracias por discutir este tema en la llamada de la comunidad de septiembre (https://aka.ms/PSCommunityCall; al momento de escribir este artículo, la grabación de la llamada de septiembre no se ha publicado allí, pero se puede acceder a ella en https://aka.ms/JoinPSCall, hasta la próxima llamada), a partir de las 46:00, según la sugerencia de @ThomasNieto en la solicitud previa para temas de discusión en https://github.com/PowerShell/PowerShell-RFC/issues/260 :

(Esperaba proporcionar un enlace directo a la parte relevante de la grabación, pero eso tendrá que esperar hasta que se publique en YouTube y se enlace en https://aka.ms/PSCommunityCall).

Basado en sus comentarios en la grabación:

  • Dado que indicó que estaba dispuesto a revisar esto, vuelva a abrir este problema y vuelva a etiquetarlo como Review - Committee .

  • En cuanto a sus comentarios de que incluir nombres de variables entre llaves es una característica preexistente y que las personas se oponen a tener que usarlos en este caso por motivos _estéticos_:

    • No creo que nadie aquí _ no_ sepa que {...} _ puede_ y, a veces, _debe usarse para eliminar la ambigüedad de los nombres de las variables.
    • Tampoco se trata de _estética_, se trata principalmente de _no obtener el comportamiento esperado_ de manera que _falla silenciosamente_, debido a _no esperar tener que usar {...} en este caso_; una preocupación secundaria es la inconveniencia de escribir.
GitHub
Documentos RFC (Solicitud de comentarios) para obtener comentarios de la comunidad sobre cambios de diseño y mejoras en el ecosistema de PowerShell: PowerShell / PowerShell-RFC
Equipos de Microsoft

Me preocupa que este problema, ahora aparentemente indefinidamente en un estado cerrado, se pierda, así que abrí el # 14025 como un recordatorio para volver a visitarlo, e invito a todos los que estén interesados ​​a mostrar su apoyo allí.

¿Fue útil esta página
0 / 5 - 0 calificaciones