Powershell: ¿Es hora de "PowerShell vZeroTechnicalDebt" y / o de un mecanismo de suscripción en funciones nuevas / fijas que rompen la compatibilidad con versiones anteriores?

Creado en 26 abr. 2018  ·  69Comentarios  ·  Fuente: PowerShell/PowerShell

Tenga en cuenta que el término _deuda técnica_ se utiliza de forma vaga aquí para significar "comportamiento roto acumulado que no se puede arreglar sin romper la compatibilidad con versiones anteriores";


_Update_: @rjmholt ha iniciado oficialmente una discusión sobre cómo implementar y gestionar cambios importantes: # 13129.


Nota: Este problema, que surgió de https://github.com/PowerShell/PowerShell/issues/5551#issuecomment -380522712, es solo para iniciar la discusión para ver si existe una voluntad fundamental de considerar tales cambios . Eventualmente, se necesitan RFC.

El firme compromiso de PowerShell con la compatibilidad con versiones anteriores ha sido muy útil para la comunidad a lo largo de los años.

Por otro lado, el subproducto inevitable fue la acumulación de deuda técnica que requiere memorizar excepciones y es una barrera para los recién llegados.

Ciertos problemas fundamentales que existen en la actualidad solo se pueden resolver a expensas de la compatibilidad con versiones anteriores, y hay dos formas, no mutuamente excluyentes, de manejar eso:

  • Implementación de una edición "PowerShell vZeroTechnicalDebt" que elimina todas las deudas técnicas, pero pierde la compatibilidad con versiones anteriores (posiblemente con versiones futuras que utilicen versiones semánticas para indicar compatibilidad)

  • Integración de funciones / correcciones que rompen la compatibilidad con versiones anteriores en la base de código existente, disponible estrictamente solo en _opt-in_.

¿Qué enfoques, si corresponde, estamos dispuestos a considerar?
Una vez que tengamos claridad sobre eso, podremos desarrollar los procesos.

Estos son algunos de los problemas fundamentales existentes que solo se pueden resolver a expensas de la compatibilidad con versiones anteriores; Estoy seguro de que otros pensarán en más:

Datos ambientales

Escrito a partir de:

PowerShell Core v6.0.2
Issue-Meta

Comentario más útil

Es caro solucionar problemas que creo que son la deuda técnica de PowerShell que no adoptó los nuevos conceptos de .NET a medida que aparecieron.

  • Sintaxis de lenguaje para parámetros de tipo para métodos genéricos # 5146

  • Soporte del motor para que Extension Methods emerja automáticamente en el sistema de tipos (como lo hacen en los lenguajes _compilados_ .Net). N.º 2226

  • Soporte de idiomas para API asíncronas # 6716 (y RFC )

Hay algunas características lamentables:

  • ¿Podríamos hacer algo basado en sugerencias de tipo (como FormatPx de @KirkMunro ) en lugar de cmdlets que

  • ¿Normalizaría el proveedor de registros para que se base en _contenido_, en lugar de sentirse como una demostración de cómo podría funcionar un proveedor de propiedades? N.º 5444

  • ¿Refactorizaría los PSProviders profundamente para hacerlos más fáciles de escribir? Es una pena que se necesiten dos capas de abstracción para llegar a "SHiPS" y, finalmente, brindar a las personas una forma de escribir proveedores que estén dispuestos a usar.

¿Qué tal una pregunta más importante?

¿Estaríamos dispuestos a reconsiderar el enfoque de "muchas formas es mejor" y trabajar en un enfoque de "pozo de éxito"? Quiero decir, ¿estaríamos dispuestos a eliminar características que se consideran "la forma fácil" pero que son fundamentalmente peores, a favor de tener sólo "la forma correcta" de hacer las cosas? P.ej:

  • Hacer CmdletBinding siempre activado
  • Hacer que la notación @ () signifique List[PSObject]
  • Hacer que @ {} signifique Dictionary[PSObject, PSObject] (o Dictionary[string, PSObject] 😲)
  • Hacer _Process_ el bloque predeterminado
  • Tal vez incluso haga que _ValueFromPipelineByPropertyName_ sea el predeterminado ;-)

Todos 69 comentarios

Interesante. Siempre me he preguntado por qué se usaría 1 en .ps1 para agregar una especie de control de versiones semántico a los scripts de PowerShell, lo que permite que un script indique si se adaptó a posibles cambios importantes.

Es caro solucionar problemas que creo que son la deuda técnica de PowerShell que no adoptó los nuevos conceptos de .NET a medida que aparecieron.

  • Sintaxis de lenguaje para parámetros de tipo para métodos genéricos # 5146

  • Soporte del motor para que Extension Methods emerja automáticamente en el sistema de tipos (como lo hacen en los lenguajes _compilados_ .Net). N.º 2226

  • Soporte de idiomas para API asíncronas # 6716 (y RFC )

Hay algunas características lamentables:

  • ¿Podríamos hacer algo basado en sugerencias de tipo (como FormatPx de @KirkMunro ) en lugar de cmdlets que

  • ¿Normalizaría el proveedor de registros para que se base en _contenido_, en lugar de sentirse como una demostración de cómo podría funcionar un proveedor de propiedades? N.º 5444

  • ¿Refactorizaría los PSProviders profundamente para hacerlos más fáciles de escribir? Es una pena que se necesiten dos capas de abstracción para llegar a "SHiPS" y, finalmente, brindar a las personas una forma de escribir proveedores que estén dispuestos a usar.

¿Qué tal una pregunta más importante?

¿Estaríamos dispuestos a reconsiderar el enfoque de "muchas formas es mejor" y trabajar en un enfoque de "pozo de éxito"? Quiero decir, ¿estaríamos dispuestos a eliminar características que se consideran "la forma fácil" pero que son fundamentalmente peores, a favor de tener sólo "la forma correcta" de hacer las cosas? P.ej:

  • Hacer CmdletBinding siempre activado
  • Hacer que la notación @ () signifique List[PSObject]
  • Hacer que @ {} signifique Dictionary[PSObject, PSObject] (o Dictionary[string, PSObject] 😲)
  • Hacer _Process_ el bloque predeterminado
  • Tal vez incluso haga que _ValueFromPipelineByPropertyName_ sea el predeterminado ;-)

Todas buenas sugerencias, @Jaykul.

Hacer que la notación @() signifique una List[PSObject] : creo que podemos llevar esto más allá y usar un tipo de colección extensible de manera eficiente como el tipo de colección fundamental de PowerShell, de modo que se use siempre que [object[]] actualmente es (y se usa internamente, así como cuando se devuelven colecciones, sin penalización de rendimiento de conversión de tipo); hay desafíos en torno al uso de + , pero @PetSerAl ha sugerido una implementación de lista personalizada para lidiar con eso .

Haciendo de Process {} el bloque predeterminado en funciones, aparte: es lo que Filter hace actualmente, pero está limitado al bloque - implícito - Process , y esta variante de función aparentemente nunca realmente se dio cuenta; unificar los dos en el contexto de Function tiene sentido para mí.

Es curioso, en la llamada de standup de la comunidad de Azure PowerShell ahora mismo, y van a pasar de AzureRm a un nuevo módulo Az que es multiplataforma (sin AzureRm.Core por separado), y con todos los prefijos de comando cambiados de AzureRm a Az. Los dos estarán uno al lado del otro por un tiempo, pero se están moviendo al nuevo conjunto de comandos.

Lástima que PowerShell aún no haya tenido la oportunidad de crear un nuevo ejecutable que se ejecute en paralelo con Windows PowerShell, pero que podría romper con algunos de los problemas heredados que lo arrastran hacia abajo.

Imagínense lo que pasaría si lo intentamos:
Hablando de manera realista, se necesitarían al menos 6 meses de esfuerzo concentrado para llegar a una versión 'sin arrepentimientos' y otros 6 meses para reiterarla. Luego, los módulos de terceros también deben adaptarse para que esta versión sea útil, lo que tomará al menos otros 6 meses para obtener una cobertura razonable (y tenga en cuenta que algunos módulos nunca lo harán) ... Entonces tenga en cuenta retrasos y problemas inesperados, etc. Entonces, no, creo que es solo una ilusión que uno puede deshacerse de toda la deuda técnica en una versión de una vez (y aún desarrollar y no solo mantener la versión anterior).

Por mucho que deseara que existiera tal versión, creo que solo será posible llegar a ella lentamente, un cambio importante a la vez. Con v6 ya se aceptaron muchos cambios importantes, pero creo que si uno incluye demasiados cambios importantes en una versión, será demasiado complejo actualizar los scripts / módulos existentes. Es bueno discutir los cambios de última hora más valiosos, pero hasta que no haya una versión LTS de pwsh, no creo que sea el momento de pensar en tener un segundo tren de pwsh con cambios más sustanciales en paralelo a la versión principal existente.

@bergmeister De acuerdo. Sin embargo, incluso cambios relativamente pequeños en una ruta principal pueden dificultar seriamente la adopción. Mire Python 3. Se necesitaron 10 _ años_ para entenderlo. Con cambios mucho más grandes, quién sabe cuánto tardará Perl 6 en dominar (y les tomó 15 años encontrar las cosas correctas, por lo que 1.5 años para PowerShell ++ parece optimista :-)) Por otro lado, PHP parece romper cosas con regularidad, posiblemente debido a la forma y para qué se utiliza.

Python 3 es sin duda el espectáculo de terror, ¿ya se ha puesto de moda? Sigo funcionando con la versión 2.7 y no planeo actualizarme pronto. Pero tampoco he escuchado mucho sobre Perl 6 recientemente ...

Creo que las lecciones para aprender de Python son separar los cambios importantes en el idioma de un cambio de versión en el motor. Hipotéticamente, un motor de PS 7 aún podría ejecutar archivos de scripts anteriores (.PS1) en un modo de cambios sin interrupciones, mientras que si el script estuviera marcado como 7 consciente (digamos con una extensión .PS7) podrían declarar que se han actualizado _y_ que requieren al menos PS 7 para funcionar. Espero que tenga sentido.

Quizás la mejor historia de éxito sea JavaScript / TypeScript / Babel. Un transpilador (con soporte para mapas de fuentes) parece ser el camino a seguir para la evolución del lenguaje.

Javascript es un caso especial. Estás bastante atascado, así que transpilar es realmente la única opción. TypeScript es "solo" Javascript con extensiones, por lo que es fácil de adoptar para las personas. Cualquier programa javascript es un programa mecanografiado, por lo que comienza con lo que tiene y simplemente agrega anotaciones desde allí. Dart, por otro lado, es su propio lenguaje pero se transpila a javascript o un tiempo de ejecución nativo en Chrome (al menos ese fue el plan en un momento). Dart no parece haber tenido mucha adopción fuera de Google, probablemente porque es su propio idioma.

@BurtHarris

Python 3 es sin duda el espectáculo de terror, ¿ya se ha puesto de moda?

Estaba leyendo un artículo la semana pasada donde el autor afirmaba que se había logrado una masa crítica para Python 3. Que todos los módulos principales estaban disponibles y que ahora la gente estaba migrando en masa. Veremos..

Interesante. Siempre me he preguntado por qué el 1 en .ps1 se usaría para agregar una especie de control de versiones semántico a los scripts de PowerShell, lo que permite que un script indique si se adaptó a posibles cambios importantes.

WRT .ps1 , nos reservamos el derecho de cambiar la extensión en caso de que el idioma sea completamente incorrecto, lo que resultará en cambios catastróficos en el tiempo de ejecución, de modo que los scripts de la versión anterior simplemente no funcionarían. Pero cambiar la extensión también conduce a una gran cantidad de trabajo porque muchas cosas en el ecosistema están vinculadas a una extensión. Entonces no es algo para hacer a la ligera. Y, por supuesto, al ser parte de Windows, si bifurcáramos la extensión, aún tendríamos que mantener ambas versiones (algo así como Python 2/3).

@bergmeister :

Preocupaciones válidas, pero con el espíritu de:

Es bueno discutir los cambios de última hora más valiosos

por favor comparta cualquiera que tenga en mente.

lentamente un cambio rotundo a la vez

Si bien un mecanismo de aceptación (de ámbito léxico) para cambios incompatibles es una solución, me preocupan dos cosas:

  • La introducción gradual puede hacer que nadie pueda recordar qué versión se requiere para qué función; Me viene a la mente Perl (v5-).

  • La base del código se hincha y es difícil de mantener, y el binario resultante se hincha igualmente, lo que perjudica (al menos) el rendimiento de inicio.

Lo he dicho antes: para mí, y esto es solo una corazonada, la versión 6 GA fue un compromiso desafortunado entre hacer que los veteranos se sientan infelices con los cambios importantes y llevar suficiente equipaje para obstaculizar la adopción en el mundo [similar a] Unix.

Dicho esto, dada la relativa juventud de PowerShell en el mundo [similar a] Unix, tal vez haya (todavía) más voluntad de solucionar los problemas mediante cambios incompatibles. Como afirma @BrucePay , Windows PowerShell tendrá que mantenerse de todos modos y puede seguir siendo el refugio seguro para la compatibilidad con versiones anteriores.

Creo que ya se ha cubierto la escala de las consecuencias negativas de estos cambios decisivos y estoy de acuerdo con la mayoría de esos puntos. Estoy escribiendo esto porque dudo que, en un contexto más amplio, estos cambios produzcan beneficios significativos en primer lugar. Al menos por la forma en que uso PowerShell.

El OP incluye la siguiente declaración:

Por otro lado, el subproducto inevitable fue la acumulación de deuda técnica que requiere memorizar excepciones y es una barrera para los recién llegados.

La premisa implícita de esta afirmación parece ser que hacer estos diversos cambios importantes aliviaría la carga de memorizar excepciones. De hecho, ese sería un gran resultado. Sin embargo, soy escéptico de que ese sea el resultado. El comportamiento de PowerShell es deliberadamente rico. Esto lo hace expresivo e impredecible. La expresividad y la previsibilidad parecen contradecirse, al menos entre los lenguajes que conozco.

Si bien estoy de acuerdo en que muchos de los cambios importantes mencionados anteriormente mejorarían un poco la previsibilidad en algunos casos, permanecerán muchos aspectos impredecibles y sorprendentes del lenguaje. A pesar de pasar años escribiendo PowerShell, todavía me sorprende con frecuencia el comportamiento de PowerShell que parece ser por diseño y probablemente no debería cambiarse. Algunos ejemplos recientes que me vienen a la mente son los siguientes:

  • enlace retardado condicional de [scriptblock] argumentos (# 6419)
  • cuando, exactamente, ocurre la enumeración implícita (# 5832)
  • conversión de [AutomationNull]::Value a $null durante el enlace de parámetros (# 6357 está relacionado)
  • el impacto de break y continue en el flujo de control en la tubería (# 5811)
  • qué sucede cuando splat $null ( SO )
  • cómo el uso de bloques de secuencia de comandos en los estados de sesión afecta $_ ( SO 1 , SO 2 )

Espero que estos ejemplos sean solo una pequeña fracción de los muchos otros matices cuidadosamente diseñados pero sorprendentes que aún no he descubierto. Seleccioné estos ejemplos porque

  1. representan un comportamiento que probablemente no se puede mejorar en PowerShell, y
  2. No tengo ninguna esperanza de detectar de manera confiable todas sus implicaciones al leer o escribir PowerShell.

Estoy bien con esto. La abrumadora mayoría del comportamiento sorprendente de PowerShell no tiene un impacto duradero en mi éxito con PowerShell. El comportamiento sorprendente casi siempre se detecta de inmediato mediante pruebas durante el desarrollo. Aprendo de las cosas que se me escapan y las utilizo para mejorar mi estrategia de prueba. Esto es cierto si esos comportamientos sorprendentes son del tipo que podría eliminarse o del tipo que debe permanecer.

Ya sea que se realicen o no los cambios importantes propuestos anteriormente, PowerShell nunca será tan predecible que pueda reducir significativamente la cobertura de la prueba. En otras palabras, por la forma en que uso PowerShell, no creo que haya muchas ventajas que sean posibles al realizar los cambios importantes propuestos anteriormente.

(Por cierto, gracias @ mklement0 por hacer directamente esta pregunta. Esto ha estado en mi mente por un tiempo. Es bueno ver la oportunidad para que todos digan su parte).

Gracias, @ alx9r.

Creo que es importante distinguir entre complejidad _intrínseca_ y complejidad _extrínseca_:

  • La complejidad _intrínseca_ proviene de la complejidad inherente de los conceptos que se están implementando, que en el caso de PowerShell tiene dos fuentes principales: la complejidad _interna_ de la introducción de un nuevo paradigma (canalización basada en objetos) y la unión de dos mundos distintos (sintaxis de shell y sintaxis de lenguaje de programación). ), y la complejidad _external_ por interactuar con mundos externos múltiples y dispares.

  • La complejidad _extrínseca_ proviene de abstracciones e inconsistencias con fugas.

    • Esta complejidad debería _eliminarse_.
    • Si esa no es una opción debido a problemas de compatibilidad con versiones anteriores, dicha complejidad debería _documentarse como problemas conocidos_.

    • El comportamiento del alcance de la variable del módulo de secuencia de comandos (al que hace referencia en el contexto de $_ ) es más que una simple peculiaridad: es la causa raíz de un problema importante, el # 4568 mencionado anteriormente.

    • Todos los demás temas que mencionas me parecen pertenecientes a la categoría extrínseca (en lugar de ser el resultado de un diseño cuidadoso), porque todos se presentan como inconsistencias que no tienen una justificación o beneficio obvio (documentado):

      • Tiene más sentido salpicar con $null para pasar argumentos _no_ en lugar de un argumento $null posicional.
      • ¿Por qué [System.Management.Automation.Internal.AutomationNull]::Value se convertiría a $null durante el _enlace de parámetro_, aunque el tipo se conserva en _asignación de variable directa_? Consulte https://github.com/PowerShell/PowerShell/issues/9150#issuecomment -473743805
      • ¿Qué beneficio tiene el alcance _dinámico_ de break y continue , a través de la pila de llamadas, con _ terminación silenciosa_ si no se encuentra ningún bucle adjunto?

Si bien la riqueza de características y la unión de mundos dispares hace que sea difícil recordar toda la complejidad _intrínseca_ requerida, minimizar la _extrínseca_ sigue siendo importante.

Tener que probar primero su enfoque previsto sin solo saber y confiar en que funcionará, o que las cosas se rompan inesperadamente debido a un comportamiento sorprendente, es un obstáculo grave para la productividad (y el disfrute) (aunque PowerShell hace que sea muy fácil probar el comportamiento de manera interactiva ).

Todo se reduce a conceptos sólidos (que no contravengan las expectativas intuitivas), nombres descriptivos y buena documentación:

Si no lo sé / no estoy seguro de recordarlo correctamente, necesito saber dónde buscarlo y tener fe en que los _problemas conocidos y casos extremos_ también están documentados (ya sea como parte de los temas de ayuda habituales o mediante enlaces de allí).

Así que incluso si decidimos que _eliminar_ la complejidad extrínseca no es una opción, al menos podemos _documentarla sistemáticamente_ - y la discusión aquí puede servir como punto de partida para compilar una "galería de trampas" (que también puede incluir casos de inevitable complejidad _intrínseca_ que puede resultar sorprendente).

al menos podemos documentarlo sistemáticamente, y la discusión aquí puede servir como punto de partida para compilar una "galería de trampas"

Roman Kuzmin ha estado recopilando una galería de este tipo durante un tiempo, aquí: https://github.com/nightroman/PowerShellTraps

Gracias, @HumanEquivalentUnit , parece una gran colección.

Junto con los problemas recopilados en este hilo, podría formar la base para una galería que forma parte de la documentación _oficial_, con cada entrada aumentada, según corresponda:

  • una justificación de diseño que explique por qué el comportamiento, aunque quizás sorprendente, está justificado después de todo (una explicación correctamente enmarcada puede hacer que el elemento sorpresa desaparezca)

  • un reconocimiento del problema, indicando que:

    • tampoco: no se solucionará en aras de la compatibilidad con versiones anteriores.
    • o: que se está considerando un cambio futuro
    • o: que el problema ya está solucionado en PS _Core_

Es extraño que haya que preguntar si _es hora de "PowerShell vZeroTechnicalDebt" _

Exactamente para esto hay un comando de versión requerido.

Si algo se rompe en una nueva versión, lo único que nos duele es que tenemos que estudiar y aprender las diferencias normalmente pequeñas.

Pero (finalmente) obtenemos una plataforma, que realmente mejora con cada iteración en el núcleo. Y mucho más fácil de mantener.

No, nunca es una cuestión de iniciar malos diseños y decisiones con nuevos conocimientos y tecnología.

Además, para sacar la mierda viva de un caballo muerto, todavía es tremendamente difícil escribir una función o cmdlet que se comporte bien que se ocupe de las diferencias mágicas entre el sistema de archivos nativo del host y el proveedor del sistema de archivos. Un código tan matizado es difícil de escribir y a menudo se equivoca. Aquí hay una publicación sobre stackoverflow que respondí hace unos siete u ocho años que sigue siendo válida:

https://stackoverflow.com/questions/8505294/how-do-i-deal-with-paths-when-writing-a-powershell-cmdlet

Ref: # 8495

Hay algunas reglas de precedencia de operadores extrañas que merecen revisiones:

PS> 1, 2, 2 + 1, 4, 4 + 1
1
2
2
1
4
4
1

En la mayoría de los idiomas, uno esperaría más comúnmente que este sea el resultado:

1
2
3
4
5

Una opción que tiene sus propios pros y contras es una nueva marca de función para habilitar todo el comportamiento "ideal" para que sea opt-in. Si tuviéramos algo de telemetría que indique que la mayor parte del uso se ha trasladado al nuevo comportamiento, podríamos cambiarlo para que sea excluido. Todo esto podría ser solo una quimera, ya que no he visto ningún caso del mundo real en el que un modelo así funcionara ...

De hecho, eso sería _buen_, pero dada la naturaleza bastante ... completa ... de algunas de estas revisiones en este hilo, se corre el riesgo de _partir_ por completo la compatibilidad de los scripts entre "normal" y "experimental", y potencialmente en varias partes, dependiendo de qué banderas haya habilitado un individuo.

Esto significa que realmente no podemos confiar en nada que sea una característica experimental de esa manera, ni escribir scripts y módulos que se basen en ellos, a menos que adjuntemos una gran advertencia a las páginas de documentos, o potencialmente evitemos que se importen a menos que ciertas banderas están habilitados.

Sin embargo, esto _podría_ ser evitable ... si podemos, a voluntad, habilitar y deshabilitar características experimentales específicas para cada módulo. Pero, dado que eso solo complica aún más las cosas, ni siquiera estoy seguro de si esa es una solución particularmente excelente.

@ vexx32 solo para ser claros, no estaba sugiriendo una bandera experimental que eventualmente se volvería no experimental. Estaba pensando en algo diferente (quizás más como Enable-PSFeature ya que sería oficialmente compatible (y, por lo tanto, protegido de cambios importantes a diferencia de las funciones experimentales)). También estaba pensando que sería una bandera única en la que optar por estos nuevos cambios de última hora como un conjunto en lugar de individualmente.

Oh, en ese caso ... Sí, absolutamente. Eso nos permitiría empaquetar cuidadosamente todo bajo un solo paraguas, y realmente usarlo. ¡Mucho mejor de lo que pensaba! 😄

La adición de @ jszabo98 a la lista de problemas compilada aquí, basada en # 8512:

-and y -or inesperadamente tienen la misma precedencia, mientras que en la mayoría de los lenguajes (incluido C #) -and tiene una precedencia mayor que -or .

Expresión de ejemplo que contraviene las expectativas:

PS> $true -or $true -and $false
False

Debido a la asociatividad a la izquierda y a que -and y -or tienen la misma precedencia, lo anterior se evalúa como ($true -or $true) -and $false lugar del $true -or ($true -and $false) esperado

Todo esto podría ser solo una quimera, ya que no he visto ningún caso del mundo real en el que un modelo así funcionara ...

De hecho, ya tenemos un modelo que funciona en todo el mundo: LTS. MSFT usa esto para desarrollar Windows 10. El modelo se usa para desarrollar distribuciones Unix.
Para nosotros, esto significa que podríamos lanzar versiones LTS PowerShell Core cada dos años e incluirlas en las versiones Windows 10 LTS y Unix LTS. (Un problema es sincronizar las fechas de lanzamiento de Windows y Unix para las versiones LTS. Supongo que MSFT puede negociar con las principales empresas de Unix).
Durante estos dos años, estamos recopilando cambios de ruptura menores, transfiriendo los cambios de ruptura más importantes al siguiente ciclo.
Para cada nueva versión de LTS, ScriptAnalyzer debería obtener un conjunto de nuevas reglas para facilitar la migración de scripts.
Con este modelo, los productos y scripts críticos solo tendrán que saltar de la versión anterior a la siguiente después de realizar pruebas y migraciones exhaustivas.

Utilicé este enfoque cuando migré paso a paso (versión por versión) un sistema antiguo de la versión PHP 4 a la siguiente versión PHP hasta que llegué a la versión PHP 5.x compatible. En cada paso, tuve que hacer cambios relativamente pequeños y rápidos, después de lo cual el sistema continuó funcionando durante algún tiempo hasta el siguiente paso.

Actualización: debe estar sincronizado con las versiones de .Net Core LTS. Espero que .Net Core 3.1 sea el próximo LTS y PowerShell Core 6.x debería haber estado en la versión.

¿Honestamente? Eso suena como una gran idea para PowerShell Core. Eso nos permitiría realizar cambios importantes como este que realmente podrían ayudarlo a avanzar, sin sobrecargarlo con problemas de compatibilidad, mientras que _también_ no sobrecargarlo con demasiados cambios en el ínterin entre lanzamientos de LTS.

Ref: https://github.com/PowerShell/PowerShell/pull/8407#issuecomment -453221566

Get-Variable -ValueOnly -Name myVar | Should -Be $var aserción falla cuando $var es una matriz.

Esto se debe a que Get-Variable -ValueOnly no usa la sobrecarga de enumeración para WriteObject() .

Con respecto a tener múltiples versiones de powershell que tienen diferentes conjuntos de características y cambios importantes: ya tenemos esto con el legado y el núcleo. Lo que parece malo a primera vista, pero puede proporcionar un modelo de trabajo para el futuro. Quédate conmigo...

Tenemos que configurar casi todos los procesos que usan powershell para llamar al núcleo pwsh.exe antes de ejecutar los scripts reales porque las tareas programadas de Windows, jenkins y muchos entornos no son conscientes del núcleo de powershell. Hacemos esto porque utilizamos funciones que solo están disponibles en una versión de PS en particular.

En los casos en los que no podamos forzar una llamada al núcleo, tenemos que hacer que cada script verifique en qué versión de PS se está ejecutando y luego se llame a sí mismo utilizando la versión de PS adecuada en el sistema host. Cualquiera sea el truco que usemos para hacer que el código correcto se ejecute en el entorno correcto, a menudo estamos pasando todo el entorno y los parámetros al mismo script que ya se estaba ejecutando.

Esa es la filosofía de UNIX. Haga que una herramienta haga una cosa y hágalo bien. Luego combine herramientas para crear un todo que sea mayor que la suma de sus partes. Este es el mundo de las secuencias de comandos en el que hacemos llamadas de subshell a otras utilidades todo el tiempo y procesamos los resultados. Históricamente eso es scripting. Powershell tiende hacia una convención extraña al tratar de mantener todo el código nativo, pero eso es solo una convención, no un requisito. PS se ejecuta con acceso al sistema operativo y todas las utilidades disponibles al igual que los scripts de shell de Unix.

Esto sería mucho más fácil si la extensión de archivo para el núcleo se hubiera actualizado a .ps6 para que el motor de PowerShell pudiera hacer el trabajo por nosotros. Cada versión binaria de PowerShell puede permanecer independiente de las demás siempre que cada llamada a una pase entornos y devuelva las salidas, errores, advertencias, excepciones a la otra, lo que tenemos que hacer manualmente ahora mismo con un gran esfuerzo. No es necesario tener indicadores compatibles con versiones anteriores en el motor siempre que el motor sepa qué versión ejecutar. El cambio de contexto involucrado ya está sucediendo, por lo que ya estamos en el peor de los casos.

Nos veremos obligados a usar hacks horribles en PowerShell para siempre A MENOS que sigamos adelante y rompamos cosas de una manera bien definida. Los cambios importantes no van a hacer que nuestras vidas sean más difíciles de lo que ya lo son. Alternativamente, si usamos la mejora continua incremental y hacemos que el motor haga el trabajo pesado de cambiar el contexto, entonces nuestro código (y vidas) será mejor.

@ jtmoree-kahalamgmt-com ¡Gracias por tus comentarios! No dude en abrir nuevos números para compartir su experiencia y comentarios y solicitar nuevas funciones útiles.

Esto sería mucho más fácil si la extensión de archivo para el núcleo se hubiera actualizado a .ps6 para que el motor de PowerShell pudiera hacer el trabajo por nosotros.

Consulte https://github.com/PowerShell/PowerShell/issues/2013#issuecomment -247987977

Y creé https://github.com/PowerShell/PowerShell/issues/9138 basado en sus comentarios.

Python 3 es sin duda el espectáculo de terror, ¿ya se ha puesto de moda? Sigo funcionando con la versión 2.7 y no planeo actualizarme pronto. Pero tampoco he escuchado mucho sobre Perl 6 recientemente ...

Creo que las lecciones para aprender de Python son separar los cambios importantes en el idioma de un cambio de versión en el motor. Hipotéticamente, un motor de PS 7 aún podría ejecutar archivos de scripts anteriores (.PS1) en un modo de cambios sin interrupciones, mientras que si el script estuviera marcado como 7 consciente (digamos con una extensión .PS7) podrían declarar que se han actualizado _y_ que requieren al menos PS 7 para funcionar. Espero que tenga sentido.

Creo que llamar a Python 3 un programa de terror es demasiado dramático. Si miro los paquetes más descargados de PyPi y tomo los metadatos del paquete individual sobre descargas v2 vs v3, veo que v3 ahora ha roto el 33% de penetración para casi todos los paquetes principales, y para bibliotecas más nuevas como la biblioteca de solicitudes de Ken Reitz, es casi 50/50 dividido: ayer hubo 685k descargas de v3, frente a 875k de v2.

Lo principal que retuvo a Python 3 durante mucho tiempo fue el apoyo de la comunidad científica a pandas y pysci. Pero, en las últimas estadísticas, la comunidad científica ahora prefiere Python 3 a Python 2: https://pypistats.org/packages/pandas 231k v3 descargas, vs 175k v2 descargas.

Como diría el famoso Dr. W. Edwards Deming: "En Dios confiamos; ¡todos los demás, traen datos!" Tal vez pueda escribir un script de PowerShell para analizar el conjunto de datos de PyPi y decirme cuán horrible es el uso de datos de Python 3: https://packaging.python.org/guides/analyzing-pypi-package-downloads/

Estaría feliz con (en este orden exacto):

  1. Sistema de módulos más fácil de usar que produce buenos seguimientos de pila.

    • Por ejemplo, el archivo psm1 permite describir reglas arbitrarias sobre cómo se cargan las funciones, pero esto provoca rastros de pila crípticos donde todas las funciones se agrupan en el archivo psm1 de forma lógica. Esto significa que los números de línea en los trazos de pila son tan útiles como una tetera de chocolate.
  2. Reemplace el formato de cadena de objetos Error predeterminado de PowerShell que absorbe el 99% de la utilidad de la programación en una arquitectura von Neumann basada en pilas.

    • C # más o menos lo hace bien. PowerShell es demasiado inteligente a la mitad.
  3. Corrija la redirección nula para que las tres formas diferentes de redirigir los flujos de salida al dispositivo nulo tengan un rendimiento equivalente.

  4. Corregir el alcance dinámico :

function a($f) {& $f}
function main() {
    $f = {write-host 'success'}
    a {& $f} # stack-overflow
    a {& $f}.getnewclosure() # okay
}
[void] (main)
  1. Corregir captura de variables :
set-strictmode -version 'latest'
$erroractionpreference = 'stop'

function main() {
    $a = 'foo'
    $f = {
        $g = {$a}.getnewclosure()
        & $g # "variable '$a' cannot be retrieved because it has not been set."
    }.getnewclosure()
    & $f
}

main
  1. La producción se acumula; return no hace lo mismo que otros idiomas; arreglalo.

  2. PowerShell no tiene azúcar sintáctico para la eliminación de recursos externos de objetos

  3. Vuelva a escribir PowerShellGet. Es terrible. Funciones con cientos de líneas de código, excepciones tragadas, sin cobertura de prueba.

  4. Deshazte de cómo funciona el almacenamiento en caché de módulos.

    • La caché no es segura para subprocesos. WTF?
    • Todo el concepto de almacenar en caché un módulo con fines de análisis es un truco / truco. Me doy cuenta de que dentro de Microsoft hay un gran soporte para esta función porque PowerShell es muy lento, pero vea mi siguiente sugerencia para una verdadera solución.
    • La ÚNICA forma de borrar su AnalysisCache es reiniciar PowerShell.exe
    • El almacenamiento en caché de módulos con fines de análisis acopla estrechamente el análisis al estado de tiempo de ejecución de los módulos.
  5. Proporcione "módulos compilados" similares a cómo los archivos Py se compilan en archivos pyc para mejorar el rendimiento.

  6. Agregar $none literal

    • Considere reemplazar $null con $none en muchos lugares
    • Reconsidere cómo funciona ParameterSetName y permita pasar $none valor especial a los parámetros, de modo que no necesite escribir declaraciones de cambio locas para llamar al código de 5 formas diferentes. La dinámica COM y C # hace esto correctamente: PowerShell lo convierte en un desastre. Un ejemplo sería cómo tengo que mapear dinámicamente una configuración de implementación para informes SSRS en el módulo ReportingServicesTools según el tipo de ocurrencia de suscripción. Preferiría simplemente pasar una bolsa de propiedades y hacer que el método se ocupe internamente de la complejidad, en lugar de forzar la complejidad externamente a los usuarios finales. ParameterSetName solo es útil como concepto como plantilla, no es bueno para programar. Confunde el polimorfismo ad-hoc con intellisense.

@jzabroski Con respecto al formato de ErrorRecord, hay un tema separado que se usa para discutir eso: https://github.com/PowerShell/PowerShell/issues/3647 , dado que el formato no se considera un cambio importante, podemos hacer mejoras allí.

Algunas de sus otras solicitudes son potencialmente aditivas y no se rompen, por lo que deberían ser problemas separados. Este problema específico trata sobre las solicitudes de cambios importantes si tuviéramos la oportunidad y pudiéramos justificar la realización de una nueva versión principal de PS.

@jzabroski Con respecto al formato de ErrorRecord, hay un tema separado que se usa para discutir eso: # 3647, dado que el formato no se considera un cambio importante, podemos hacer mejoras allí.

_¿Por qué_ no es un cambio rotundo? ¿Porque los desarrolladores no deberían hacer coincidir patrones en cadenas en mensajes de error en primer lugar? Supongo que eso es válido. Pero aún romperá con los consumidores intermedios.

Algunas de sus otras solicitudes son potencialmente aditivas y no se rompen, por lo que deberían ser problemas separados.

¿Cuáles crees que son potencialmente aditivos? Puedo crearles problemas para que no descarrilemos esta discusión, pero esta mañana preparé cuidadosamente esta lista. Odio PowerShell con una furia y pasión de truenos y relámpagos explosivos. PowerShell es una bola de boliche con un cuchillo de carnicero. Mis 5 momentos más embarazosos como ingeniero sucedieron mientras usaba PowerShell. Nunca recuperaré ese tiempo.

Actualicé mis sugerencias para dejar en claro por qué son sugerencias de cambios innovadoras (e increíbles).

La producción se acumula; return no hace lo mismo que otros idiomas; arreglalo.

return se comporta como usted espera dentro de un método class . La forma en que la "salida se acumula" en una función de PS normal / avanzada es bastante típica para entornos de scripts de shell , por ejemplo, Korn shell y Bash. Es decir, copie varios comandos de la consola y póngalos en una función. Cuando invoca esa función, sale de cada comando tal como cuando ejecutó esos comandos desde la consola.

Mi queja principal con PowerShell es que en el caso de la función regular / avanzada, return nunca debería haber tomado argumentos. Porque algo como:

return $foo

se ejecuta realmente como:

$foo
return

Esto le da a la gente la sensación de que pueden controlar la salida utilizando return y no pueden.

return se comporta como usted espera dentro de un método class . La forma en que la "salida se acumula" en una función de PS normal / avanzada es bastante típica para entornos de scripts de shell , por ejemplo, Korn shell y Bash.

Escucho a personas decir esto, incluido énfasis mío , a continuación, citando a él), pero la conclusión es:

Solo en PowerShell tenemos este ilógico "retorno no devuelve" sin sentido y la salida se acumula. Esta es una fuga de memoria glorificada, y debería pensar en ella como tal.

Para ser honesto, en todas partes de PowerShell In Action busco la frase "problemas", puedo agregar algo a la lista de vZeroTechnicalDebt. Esto es lo que Bruce escribe sobre la declaración de devolución:

7.3 DEVOLUCIÓN DE VALORES DE LAS FUNCIONES
Ahora es el momento de hablar sobre la devolución de valores de funciones. Hemos estado haciendo esto todo el tiempo, pero hay algo que debemos destacar. Debido a que PowerShell es un shell, no devuelve resultados, escribe resultados o emite objetos. Como ha visto, el resultado de cualquier expresión o canalización es emitir el objeto de resultado al llamador. En la línea de comando, si escribe tres expresiones separadas por punto y coma, se muestran los resultados de las tres declaraciones:
[...]
En el enfoque tradicional, debe inicializar una variable de resultado, $result , para contener la matriz que se está produciendo, agregar cada elemento a la matriz y luego emitir la matriz:

PS (16) > function tradnum
> {
> $result = @()
> $i=1
> while ($i -le 10)
> {
> $result += $i
> $i++
> }
> $result
> }

Este código es significativamente más complejo: debe administrar dos variables en la función ahora en lugar de una. Si estuviera escribiendo en un idioma que no extendiera automáticamente el tamaño de la matriz, sería aún más complicado, ya que tendría que agregar código para cambiar el tamaño de la matriz manualmente. Y aunque PowerShell cambiará automáticamente el tamaño de la matriz, no es eficiente en comparación con la captura de la salida transmitida. El punto es hacerle pensar en cómo puede utilizar las instalaciones que ofrece PowerShell para mejorar
tu codigo. Si se encuentra escribiendo código que construye matrices explícitamente, considere mirarlo para ver si se puede reescribir para aprovechar la transmisión.

Creo que la motivación detrás de esta función es agradable, pero hay formas mucho más limpias y fáciles de leer para lograr el mismo objetivo: promoción implícita de un solo objeto a una colección de objetos. Además, PowerShell ya tiene una forma sencilla de devolver la salida para la redirección: Write-Output .

En Bash, return sale de una función, punto.

¿Eh? Bash acumula la salida de la misma manera:

hillr@Keith-Dell8500:~$ function foo() {
> echo "Hello"
> ls ~
> date
> return
> echo "World"
> }
hillr@Keith-Dell8500:~$ foo
Hello
bar.ps1  date.txt   test.ps1
baz.ps1  dotnet    powershell-preview_6.2.0-rc.1-1.ubuntu.16.04_amd64.deb
Tue Mar 26 16:06:18 DST 2019

Al igual que PowerShell, Bash todavía genera todo desde cada comando hasta la declaración return .

Ah, creo que te refieres a lo que se devuelve en $? . Sí, eso es una diferencia. Creo que esos deben ser valores de retorno de int en Bash para indicar éxito / fracaso. En PS, $? indica éxito / fracaso como bool basado en errores generados por la función. En la práctica, no creo que $? no se use tanto. En su lugar, uso el manejo de excepciones a través de try/catch/finally .

Si. return tiene un solo propósito en esos idiomas. En PowerShell, se mezcla con la salida. Mezclar valores de retorno y flujos de salida es una mala idea. También puede provocar problemas de rendimiento similares a pérdidas de memoria. Lo que realmente desea son co-rutinas o "rendimiento de retorno", o generadores, constructores de generadores y escáneres (como en Icon). La razón es que el consumo de entrada es lento y la falla suspende el cálculo al tiempo que permite eliminar la parte del cálculo generado hasta el momento.

Puede pensar en la "redirección de salida" como en realidad dos objetos: un Processor y un ProcessorContext.

Editar: Veo por qué refutaste mi punto original. Pensaste que estaba diciendo que es malo poder producir resultados. En cambio, estoy diciendo que es malo devolver la salida de la forma en que lo hace PowerShell.

En su lugar, uso el manejo de excepciones a través de try/catch/finally .

Eso es mucho tipeo y no expresa claramente la intención en todos los casos.

Bueno, PowerShell se basa en .NET, que es recolector de basura, por lo que no debería sufrir "pérdidas" de memoria. Sin embargo, eso no significa que no puedas tener un tesoro de memoria. Por lo tanto, debe tener un poco de cuidado al manejar grandes resultados / colecciones que se asignan a una variable o se colocan en una tabla hash de algún tipo.

Creo que la declaración return PowerShell solo debería tener un único propósito y es regresar antes, nada más. Cuando alguien hace return 42 no tiene relación con $? . Es solo otro elemento de salida (cedido) al flujo de salida.

Como mencioné antes, $? es administrado por PowerShell y no tiene nada que ver con lo que genera / devuelve una función. $? se establece en función de los errores generados (o la falta de ellos). Esto significa que cuando ejecute $res = foo - $ res contendrá todos los objetos generados por la función foo. Si desea saber si la función "falló", puede inspeccionar $? en cada comando que ejecute O puede envolver un montón de comandos en un bloque try/catch . Encuentro el primero mucho menos mecanografiado (en scripts).

Escuche a la gente decir esto, incluido
En Korn Shell, return sale de una función, punto.
En Bash, return sale de una función, punto.

En PowerShell, return también sale de una función:

PS C:\> function test { "hello"; return "!"; "world"}
PS C:\> test
hello
!

Mezclar valores de retorno y flujos de salida es una mala idea. También puede provocar problemas de rendimiento similares a fugas de memoria

No hay mezcla porque no hay diferencia entre el flujo de salida y el valor de retorno en PowerShell. Estás hablando como si la función escribiera las cadenas en stdout y devolviera por separado un signo de exclamación a la persona que llama, pero no lo hace. Apuntar a write-output parece el mismo error; todos estos: write-output 'x' , 'x' , return 'x' envían algo por la misma ruta de salida, a la tubería. No hay un valor de retorno separado.

Lo que realmente desea son co-rutinas o "rendimiento de retorno", o generadores, constructores de generadores y escáneres (como en Icon). T

Eso significaría que si escribiera un comando en la línea de comandos y luego lo pusiera en un script o función, se comportaría de manera diferente:

PS C:\> robocopy /whatever
PS C:\> "robocopy /whatever" | set-content test.ps1; .\test.ps1
PS C:\> function test { robocopy /whatever }

Me gusta ver el mismo resultado en los tres casos, que no se trague el resultado en dos casos porque no yield return sin motivo. La forma en que PowerShell lo hace es coherente y conveniente para un shell y para escribir scripts de shell / lote: tomar algo que escribió en la línea de comandos y colocarlo en algún lugar donde pueda llamarlo una y otra vez. Hacer eso no debería cambiar completamente cómo se comporta el código.

La razón es que el consumo de entrada es lento y la falla suspende el cálculo al tiempo que permite eliminar la parte del cálculo generado hasta el momento.

La canalización de PowerShell está controlada por el tiempo de ejecución, si entiendo lo que está diciendo, ya puede hacerlo:

PS D:\> 1..1mb |foreach { write-verbose -Verbose "incoming number: $_"; $_ } |select -first 3
VERBOSE: incoming number: 1
1
VERBOSE: incoming number: 2
2
VERBOSE: incoming number: 3
3

select terminará la tubería tan pronto como tenga tres cosas. El resto de los números de un millón no se incluyeron en foreach . La información que desciende por la canalización es un modelo "push", pero está controlada por el motor PowerShell.

Aunque, al ser un shell, no está claro qué significaría para "falla de entrada para suspender el cálculo" si la entrada proviene de un comando nativo o algo fuera de un cmdlet de PS.

Proporcione "módulos compilados" similares a cómo los archivos Py se compilan en archivos pyc para mejorar el rendimiento.

Creo que la solución prevista para un mayor rendimiento es escribir módulos en C #. El código de PowerShell se compila JIT, en algunas situaciones, pero los miembros del equipo de PS han dicho antes que no tenían ningún plan para exponer esto a los usuarios finales para su personalización, por ejemplo, la charla de PS Conf Asia de IIRC

Corrija la redirección nula para que las tres formas diferentes de redirigir los flujos de salida al dispositivo nulo tengan un rendimiento equivalente.

Deseable, pero parece poco probable; $null = 4 es un enlace variable simple, 4 | out-null necesita la sobrecarga de iniciar una canalización y hacer la resolución de comandos: ¿qué pasaría si alguien hubiera hecho intencionalmente out-null un contenedor de proxy que agregó registro ¿código? ¿Es esa deuda técnica, que pedir una canalización y un cmdlet para ejecutar es más lento que una expresión?

Corregir el alcance dinámico:

Si te refieres a "no usar el alcance dinámico", fue una elección deliberada, por ejemplo, Bruce Payette dice

Eso puede ser molesto, pero ¿es esa "deuda técnica" para un caparazón, y sería "zeroTechnicalDebt" si se cambiara?

Bueno, PowerShell se basa en .NET, que es recolector de basura, por lo que no debería sufrir "pérdidas" de memoria. Sin embargo, eso no significa que no puedas tener un tesoro de memoria. Por lo tanto, debe tener un poco de cuidado al manejar grandes resultados / colecciones que se asignan a una variable o se colocan en una tabla hash de algún tipo.

Un administrador del sistema, que no es ingeniero, escribió algo de lógica para tratar de tabular los metadatos de todos los archivos en un recurso compartido de 8 TB. Eso no terminó bien: consumió mucha memoria y también fijó el sistema al 100% de la CPU ... ni siquiera pudimos iniciar sesión en el cuadro después de que comenzó el script, y la única solución _en producción_ para salvar el servidor fue difícil reiniciarlo.

En mi experiencia, PowerShell hace que sea fácil escribir esa basura. Puede hacer con mis comentarios y criticarlos como desee. No cambiará el hecho de que PowerShell facilita la escritura de scripts que pueden bloquear efectivamente las máquinas de producción.

@jzabroski, ¿puedes compartir algún detalle del guión?

La gente de Powershell intenta alentar la transmisión por canalización ( get-childitem | foreach-object {} ) en lugar de precargar un conjunto de datos ( $files = get-childitem; $files | foreach-object ) deliberadamente para reducir el uso de memoria. ¿Hizo esa carga por adelantado?

¿Se utilizó Group-Object para recopilar archivos similares? Recientemente, ha habido mejoras en el rendimiento de objetos de grupo que reducirían el uso de la CPU, si no el uso de la memoria.

PowerShell es de un solo núcleo de forma predeterminada, ¿había código de trabajos / espacios de ejecución allí, era un servidor de un solo núcleo? PS 6.1 tiene Start-ThreadJob que permite múltiples trabajos con mucho menos sobrecarga y estrangulamiento para ayudar a controlar el uso de la CPU.

En mi experiencia, PowerShell hace que sea fácil escribir esa basura

Todos los tutoriales y libros que le dicen que escriba $files = @(); $files += $f deben haberle costado al mundo una pequeña fortuna en ciclos de CPU y memoria. Ese es un patrón lento, costoso de procesador y memoria, y angustiosamente común. Tengo curiosidad por saber si ese patrón estaba en el guión. (¿Y si hay alguna forma de bloquearlo sin romper la compatibilidad con versiones anteriores, o hacer que + = no sea válido en matrices en futuros cambios de ruptura?).

Pero, hay una compensación aquí. Get-ChildItem -Recurse tiene 22 caracteres y consume más de 800 MB de RAM en solo mi unidad C:, el C # para hacer eso sin usar toneladas de memoria es de más de 50 líneas y no obtiene metadatos de archivos en absoluto, y será más como 80 MB. Eso no es memoria de pérdida de PS, es PS que la usa para ganar comodidad para el usuario.

¿Existe algún lenguaje en el que no tenga que preocuparse por los detalles de cómo funciona, puede escribir código útil, no puede escribir código incorrecto y no tiene que ser un programador experto? (¿Y que también es un caparazón?)

¿Alguno de los cambios propuestos anteriormente habría hecho que el guión funcionara?

Puede escribir un script Bash para enumerar archivos, generar un proceso para cada uno, hasta que su servidor se caiga y ni siquiera pueda SSH en él. Puede escribir un script de Python que os.walk() s el árbol de archivos y llame a os.stat() para cada archivo y use menos memoria y termine más rápido, pero toma 10 líneas en lugar de 1 y aún debe preocuparse por OS y se preocupan por los detalles del código (y todavía no es un shell y no funcionará con proveedores de PS).

Me gustaría que PS tuviera más "pozos de éxito" y menos tropiezos. La lista rápida de nombres de archivos es una de las que veo mucho uso. Cambiarlo radicalmente para que sea un idioma completamente diferente y obstaculizar las áreas que hace bien, no hará eso.

Usted quiere Python, Python existe, es un lenguaje hermoso, no está obstaculizado por tratar de encajar en dos mundos diferentes y mantener la semántica de retorno de alcance y función de un shell de treinta años de un sistema operativo diferente (por qué, Microsoft), o por el compromiso de Microsoft a la compatibilidad con versiones anteriores (y la obsesión con .Net), por lo que puede esquivar todas esas preguntas, en lugar de enfrentarlas.

Me gustaría que PS tuviera más "pozos de éxito" y menos tropiezos.

Si pudiera agregar veinte pulgares hacia arriba solo por eso, lo haría. Este es exactamente el patrón y el enfoque que debe adoptar PowerShell.

For-EachObject es una función de PowerShell para tomar colecciones de PowerShell y convertirlas en seguimientos de pila ilegibles.

Hoy, tomé un script escrito por Amazon para enumerar los discos de Windows EC2: https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2-windows-volumes.html#windows -list-disks

¿Adivina qué? No tenía la configuración de Initialize-AWSDefaults en esta PC, y dado que el autor del script usó ForEach-Object, mi seguimiento de la pila se veía así _garbage_:

PS C:\Windows\system32> D:\Installs\PowerShell\List-EC2Disks.ps1
Could not access the AWS API, therefore, VolumeId is not available. 
Verify that you provided your access keys.
ForEach-Object : You cannot call a method on a null-valued expression.
At D:\Installs\PowerShell\List-EC2Disks.ps1:39 char:12
+ Get-disk | ForEach-Object {
+            ~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [ForEach-Object], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull,Microsoft.PowerShell.Commands.ForEachObjectCommand

Esto sucede cada vez que la gente usa expresiones ForEach-Object en su código. Los rastros de la pila son _siempre terribles_. Reescribir esto es doloroso. Afortunadamente, es solo un script que copio y pego en un archivo aleatorio, lo guardo y ejecuto. Si es un problema en un módulo grande, tengo que obtener el código fuente del módulo, reescribir el script, espero que Dios lo refactorice. No rompí nada en el módulo al convertir la expresión ForEach-Object Y espero Puedo averiguar dónde está el error REAL para poder _hacer mi trabajo_. Y ahora tengo un código personalizado que bifurqué de un repositorio importante, solo para descubrir por qué recibo un error porque el Write-Error PowerShell ES horrible en comparación con el modismo simple de C # de throw new Exception("message", ex) .

Reescribiendo esto para usar for ($var in $list) , acumulando los resultados en una variable $results , obtengo el siguiente error:

PS C:\Windows\system32> D:\Installs\PowerShell\List-EC2Disks.ps1
Could not access the AWS API, therefore, VolumeId is not available. 
Verify that you provided your access keys.
You cannot call a method on a null-valued expression.
At D:\Installs\PowerShell\List-EC2Disks.ps1:58 char:26
+ ... lDevice = If ($VirtualDeviceMap.ContainsKey($BlockDeviceName)) { $Vir ...
+                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

Ah, y por cierto, cada vez que tengo que hacer esto no puedo recordar las estúpidas reglas de PowerShell para agregar elementos a las listas, así que tengo que buscar en Google https://www.google.com/search?q=powershell+append + para + enumerar y leer una publicación de blog sobre alguien que explica lo poco intuitivas que son las operaciones de lista de PowerShell.

Si no conoce C # o un lenguaje que lo haga bien, es fácil argumentar que este comportamiento está bien. Sin embargo, C # maneja este problema de manera inteligente al incluir la excepción en una AggregateException, de modo que obtenga tanto la excepción interna como la externa. ¿Potencia Shell? Jajajaja. Sí, eso sería demasiado útil.

@jzabroski, ¿puedes compartir algún detalle del guión?

Lamentablemente (_ y felizmente _), no lo recuerdo. Era un archivo compartido, por lo que era casi seguro que tuviera uno o dos núcleos como máximo. Si eran dos núcleos, es probable que Windows Defender se llevara la CPU restante.

Editar: lo encontré en el chat de Teams.

$big32 = Get-ChildItem D:\Data\Website\Data -recurse | Sort-Object length -descending | select-object -first 32 | measure-object -property length –sum

Como beneficio adicional, acabo de hablar con la persona que ejecutó este comando (y me sentí increíblemente mal), y dijo que el caso de uso "era tomar los 32 archivos más grandes para el requisito de tamaño del grupo de replicación inicial para DFS".

No hay mezcla porque no hay diferencia entre el flujo de salida y el valor de retorno en PowerShell.

Aún no he visto que la gente use esto correctamente en el primer intento. De nuevo, esto no es lo que hace bash ni ningún otro shell. Si bien fue un experimento interesante para unificar los dos conceptos, fue un desastre total y absoluto desde el punto de vista de la legibilidad. No ayuda que Write-Output se haya roto en PowerShell v6 durante 16 meses.

@jzabroski de su salida de For-EachObject parece que no está usando Powershell Core (para eso está dedicado este repositorio) ¿podría probar Powershell core y ver si puede reproducirlo? Asegúrese de utilizar la última versión. Tenga en cuenta que la funcionalidad específica de Windows (como Get-Disk) no es parte de Powershell Core, deberá tener esto en cuenta al realizar sus pruebas.

Aún no he visto que la gente use esto correctamente en el primer intento. De nuevo, esto no es lo que hace bash ni ningún otro shell.

$ test () { ls /tmp; ls /etc/pm; return 5; ls /v*; } ; x=$(test)
$ echo "$x"
tmux-1000
sleep.d
$ echo $?
0

Esa es la salida de dos comandos separados que se acumulan en una función Bash y el retorno de Bash no funciona como funciones de retorno en C #. ¿Qué, específicamente, "no es lo que hace Bash"?

No está capturando el valor de retorno correctamente para el ejemplo de bash. la devolución se pierde porque el $? es siempre el último comando. en tu caso 'echo'.

$ test () { ls /tmp; ls /etc/pm; return 5; ls /v*; } ; x=$(test)
$ echo $?
5
$ echo $x
tmux-1000
sleep.d

POWERSHELL hace esto de manera diferente

> function test() { ls; return 5 ; ls } ; $x=test
> echo $?
True
> echo $x
    Directory: c:\foo\bar

Mode                LastWriteTime         Length Name
----                -------------         ------ ----

Más rarezas de PowerShell:

Chocolatey agrega un RefreshEnv.cmd que es un GODSEND. ¿Por qué esto no se envía solo con PowerShell? ¿Por qué necesito reiniciar mi Shell o descargar algún script estúpido de Internet para restablecer mi $ env?

@jzabroski Esto se mencionó en https://github.com/PowerShell/PowerShell-RFC/pull/92 Es posible que desee intervenir allí.

Cree un modelo de objetos estándar para proveedores de PowerShell: https://github.com/PowerShell/SHiPS/issues/66#issuecomment -368924485

En el enlace sobre el número 66 de SHiPS, describo posibles cmdlets candidatos. No tengo claro por qué no podemos implementar algo como el comportamiento de la clase de tipo Haskell para estos cmdlets principales.

Esta API ya está en PowerShell.

@iSazonov ¿Puede editar y citar lo que está respondiendo y agregar algo de contexto, como un hipervínculo a la documentación de dichas API? TYVM

@jzabroski Buscar archivos en la carpeta srcSystem.Management.Automationnamespaces.

POWERSHELL hace esto de manera diferente

Es bash que lo hace de manera diferente: return solo puede establecer el código de salida del proceso (entero) (eso no es lo que significa "retorno" en otros lenguajes de programación, es un exit ) .

PowerShell también puede hacer eso , con exit 5

Es solo que no usamos códigos de salida con mucha frecuencia en PowerShell (prácticamente solo para tareas programadas e interoperabilidad con otros sistemas operativos), porque no es un shell basado en procesos y las funciones no deberían salir.

Al final del día, es la misma razón por la que detectar el tipo de identificador de stdout no es útil en PowerShell (y por qué "redirigir" a out-null no es lo mismo que enviar a [void] o asignar a $null )

Recomiendo a las personas que presenten nuevos problemas si realmente tienen comentarios sobre los que quieren que se actúe, ya que el equipo ha _ descartado de manera clara e inequívoca_ una versión no compatible con versiones anteriores de PowerShell, no sé por qué este hilo sigue adelante ...

ya que el equipo ha _clarado e inequívocamente descartado_ una versión no compatible con versiones anteriores de

Eso es una novedad para mi. ¿Cómo es esto claro? Proporcione enlaces a publicaciones u otra documentación.

Es un poco frustrante para algunos de nosotros escuchar "nunca romperemos la compatibilidad" mientras luchamos con alguna falla todos los días. El objetivo de este hilo es que el libre mercado de ideas pueda resolver algunos de estos problemas cuando el upstream no lo hará.

Alguien podría encontrar lo suficientemente ventajoso para bifurcar powershell y crear una versión con una deuda técnica mínima. Ese grupo se beneficiará de las ideas presentadas en este hilo. (Así es como funciona el mundo ahora. Que gane el mejor powershell).

Reiteraré que el equipo ya ha producido una versión 'no compatible con versiones anteriores' de powershell al cambiar el nombre del comando de powershell a pwsh. Power (SHELL) es un caparazón. el trabajo de un caparazón es ser el pegamento para los humanos que une los sistemas digitales. No es un binario compilado con dependencias externas mínimas. Incluso los lenguajes de programación tradicionales planifican y realizan cambios importantes.

POWERSHELL hace esto de manera diferente

Es bash que lo hace de manera diferente: return solo puede establecer el código de salida del proceso (entero) (eso no

Tengo curiosidad por otras conchas. ¿Qué hacen? korn, csh, etc.

Aquí hay un artículo que analiza la declaración de devolución en varios idiomas: https://en.wikipedia.org/wiki/Return_statement

Dice que el sistema operativo [shells] permite que se devuelvan varias cosas: código de retorno y salida.

Mi equipo tiene una variedad de scripts que solo se ejecutan en PowerShell 5 a pesar de que usamos PowerShell 6 tanto como sea posible. En mi experiencia, la premisa de que PowerShell es completamente compatible con versiones anteriores es definitivamente falsa. Hay al menos algunos casos extremos (por ejemplo: ErrorActionPreference no se comportan de manera intuitiva) que definitivamente deberían abordarse como un cambio importante: casos en los que hacer la corrección sería menos "romper" que no hacerlo.

@chriskuech, ¿hay ErrorActionPreference ?

@ SteveL-MSFT Creo que el mosaico al que @KirkMunro se vinculó directamente debajo de los comentarios de @chriskuech es el problema que está buscando. Y sí, aprieto la palabra Mosaic en una conversación tecnológica.

Dicho esto, @iSazonov cerró el número original de https://github.com/PowerShell/PowerShell/issues/7774

Parece que estas cosas siguen apareciendo, en diferentes formas, y el Comité sigue cerrando temas al respecto.

@jzabroski encontró que mi problema principal apuntaba a la causa raíz.

También presenté un problema en el pasado en torno a uno de los síntomas: Invoke-WebRequest arrojando un error de terminación. Personalmente, he presenciado a varias personas completamente estupefactas en todo el modelo try / catch para manejar solicitudes HTTP fallidas, lo que ocurre porque los métodos internos de .NET arrojan errores de terminación. En cada uno de los tres casos diferentes, el ingeniero respondió con un improperio cuando le expliqué el comportamiento subyacente y por qué el problema supuestamente nunca se solucionaría.

Para resumir , los errores de terminación terminan la secuencia de comandos porque los creadores de PowerShell creen que la secuencia de comandos no puede continuar lógicamente más allá del error, pero no creo que sea literalmente nadie más que la decisión del scripter de tomar caso por caso. Solo el programador puede decidir si quiere que el programa sea Stop , SilentlyContinue , Continue , etc.

(Tangencial al problema anterior)

Si PowerShell implementa un modo opt-in "ZeroTechDebt", definitivamente creo que $ErrorActionPreference = "Stop" debería establecerse de forma predeterminada. Obviamente, esta configuración no tiene sentido en un REPL y, por lo tanto, no debería establecerse de forma predeterminada para todos los escenarios, pero literalmente todos mis scripts tienen el prefijo $ErrorActionPreference = "Stop" para habilitar la "programación defensiva" y comportarse como un "normal". " lenguaje de programación.

@chriskuech Si aún no lo ha hecho, eche un vistazo a esta colección de RFC: https://github.com/PowerShell/PowerShell-RFC/pull/187. Hablan directamente de lo que está hablando aquí sin necesidad de una nueva versión de PowerShell sin deuda tecnológica.

También puede encontrar los cuatro RFC en números separados en la página Problemas en ese repositorio si eso los hace más fáciles de leer / digerir. Solo busque los problemas abiertos publicados por mí y los encontrará

@ SteveL-MSFT Aquí hay un problema similar que impide mi productividad. No es $ErrorActionPreference sino $ConfirmPreference :

A continuación se muestra un script feo que escribí para configurar los volúmenes de disco de SQL Server en 64kb.

Import-Module Storage;

function Format-Drives
{
    # See https://stackoverflow.com/a/42621174/1040437 (Formatting a disk using PowerShell without prompting for confirmation)
    $currentconfirm = $ConfirmPreference
    $ConfirmPreference = 'none'

    Get-Disk | Where isOffline | Set-Disk -isOffline $false
    # The next line of this script is (almost) copy-pasted verbatim from: https://blogs.technet.microsoft.com/heyscriptingguy/2013/05/29/use-powershell-to-initialize-raw-disks-and-to-partition-and-format-volumes/
    Get-Disk | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -Confirm:$false -PassThru | New-Partition -AssignDriveLetter -UseMaximumSize -IsActive | Format-Volume -FileSystem NTFS -AllocationUnitSize 64kb -Confirm:$false

    # See https://stackoverflow.com/a/42621174/1040437 (Formatting a disk using PowerShell without prompting for confirmation)
    $ConfirmPreference = $currentconfirm
}

Format-Drives

Un par de puntos laterales:

  1. La documentación de Format-Volume necesita más. ¿Solo dos ejemplos? Y la documentación oficial no está sincronizada con el sitio web: https://github.com/MicrosoftDocs/windows-powershell-docs/issues/1170
  2. ¿Por qué necesito ir a StackOverflow para aprender cómo evitar una GUI para un lenguaje de scripting?
  3. El hecho de que esto sea tan propenso a errores / estúpidamente difícil de escribir "sin pensar" solo subraya problemas relacionados como https://github.com/PowerShell/PowerShell-RFC/issues/198 : es otro ejemplo de cómo la promesa de Bruce Payette en " PowerShell In Action "que simplemente escribe lo que cree que es correcto y funciona ... es total y absolutamente falso.
  4. Consulte también el problema de @ mklement0 : https://github.com/PowerShell/PowerShell/issues/4568

Requiere -Version no tiene forma de especificar una versión máxima

Ver: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_requires?view=powershell-6

Esto es molesto cuando escribe funciones avanzadas que cargan bibliotecas de .NET Framework donde la API es completamente diferente entre .NET Framework y .NET Core, por ejemplo, cómo funciona la API de AccessControl.

@jzabroski Puede especificar la edición, sin embargo, para separar lo siguiente:

#requires -PSEdition Desktop
# versus
#requires -PSEdition Core

Solo una nota rápida de que @rjmholt ha iniciado oficialmente una discusión sobre cómo implementar y administrar cambios importantes: # 13129.

Además, # 6817 y # 10967 son más comportamientos que vale la pena revisar una vez que se permiten cambios importantes.
(Tienen la misma causa raíz, explicada en https://github.com/PowerShell/PowerShell/issues/10967#issuecomment-561843650).

El hecho de que , sea ​​más fuerte que + es lógico, ya que PowerShell se trata más de listas que de números. EN MI HUMILDE OPINIÓN.

@rjmholt ha iniciado oficialmente una discusión

Debo decir que no es más oficial que cualquier otra discusión.

Me encantaría tener un control estricto sobre las importaciones y el tiempo de edición de firmas de funciones.
¿Está pensando en introducir una nueva semántica de importación, como la que proporcionan los módulos EcmaScript?
No más contaminación global del espacio de nombres. Mecánica de importación lenta.

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