Powershell: É hora de "PowerShell vZeroTechnicalDebt" e / ou de um mecanismo de aceitação em recursos novos / fixos que quebram a compatibilidade com versões anteriores?

Criado em 26 abr. 2018  ·  69Comentários  ·  Fonte: PowerShell/PowerShell

Observe que o termo _dívida técnica_ é usado livremente aqui para significar "comportamento quebrado acumulado que não pode ser corrigido sem quebrar a compatibilidade com versões anteriores";


_Update_: @rjmholt iniciou oficialmente uma discussão sobre como implementar e gerenciar mudanças importantes: # 13129.


Observação: esse problema, que surgiu de https://github.com/PowerShell/PowerShell/issues/5551#issuecomment -380522712, é apenas para iniciar a discussão para ver se há uma disposição fundamental para entreter tais mudanças . Eventualmente, RFC (s) são necessários.

O compromisso constante do PowerShell com a compatibilidade com versões anteriores tem servido muito bem a comunidade ao longo dos anos.

Por outro lado, o subproduto inevitável foi o acúmulo de dívida técnica que exige a memorização de exceções e é uma barreira para os recém-chegados.

Certos problemas fundamentais que existem hoje só podem ser resolvidos à custa da compatibilidade com versões anteriores, e existem duas maneiras - não mutuamente exclusivas - de lidar com isso:

  • Implementação de uma edição "PowerShell vZeroTechnicalDebt" que elimina todas as dívidas técnicas, mas perde a compatibilidade com versões anteriores (possivelmente com versões futuras usando versões semânticas para indicar compatibilidade)

  • Integração de recursos / correções de quebra de compatibilidade com versões anteriores na base de código existente, disponível estritamente em uma base _opt-in_ apenas.

Quais abordagens, se houver, estamos dispostos a considerar?
Assim que tivermos clareza sobre isso, podemos dar corpo aos processos.

Aqui estão alguns dos problemas fundamentais existentes que só podem ser resolvidos à custa da compatibilidade com versões anteriores; Tenho certeza que outros pensarão em mais:

Dados ambientais

Escrito a partir de:

PowerShell Core v6.0.2
Issue-Meta

Comentários muito úteis

São caros para consertar problemas que considero como a dívida técnica do PowerShell por não adotar novos conceitos .NET à medida que surgiram

  • Sintaxe de linguagem para parâmetros de tipo para métodos genéricos # 5146

  • Suporte de mecanismo para Extension Methods para aparecer automaticamente no sistema de tipos (como eles fazem em linguagens _compiled_ .Net). # 2226

  • Suporte de idioma para APIs assíncronas # 6716 (e RFC )

Existem alguns recursos lamentáveis:

  • Poderíamos fazer algo com base em dicas de tipo (como FormatPx de @KirkMunro ) em vez de cmdlets que

  • Você normalizaria o provedor de Registro para ser baseado em _conteúdo_ - em vez de parecer uma demonstração de como um provedor de propriedade poderia funcionar? # 5444

  • Você refatoraria PSProviders profundamente para torná-los mais fáceis de escrever? É uma pena que sejam necessárias duas camadas de abstração para chegar a "SHiPS" e, finalmente, dar às pessoas uma maneira de escrever provedores que estão dispostas a usar

Que tal uma pergunta maior:

Estaríamos dispostos a reconsiderar a abordagem "de várias maneiras é melhor" e trabalhar em uma abordagem do "poço do sucesso"? Quer dizer, estaríamos dispostos a remover recursos que são considerados "o jeito fácil", mas que são fundamentalmente piores, em favor de ter apenas "o jeito certo" de fazer as coisas? Por exemplo:

  • Deixe o CmdletBinding sempre ativado
  • A notação Make @ () significa List[PSObject]
  • Make @ {} significa Dictionary[PSObject, PSObject] (ou Dictionary[string, PSObject] 😲)
  • Tornar _Process_ o bloco padrão
  • Talvez até mesmo torne _ValueFromPipelineByPropertyName_ o padrão ;-)

Todos 69 comentários

Interessante. Sempre me perguntei por que 1 em .ps1 seria usado para adicionar uma espécie de versionamento semântico aos scripts do PowerShell, permitindo que um script indique se ele foi adaptado para possíveis alterações significativas.

São caros para consertar problemas que considero como a dívida técnica do PowerShell por não adotar novos conceitos .NET à medida que surgiram

  • Sintaxe de linguagem para parâmetros de tipo para métodos genéricos # 5146

  • Suporte de mecanismo para Extension Methods para aparecer automaticamente no sistema de tipos (como eles fazem em linguagens _compiled_ .Net). # 2226

  • Suporte de idioma para APIs assíncronas # 6716 (e RFC )

Existem alguns recursos lamentáveis:

  • Poderíamos fazer algo com base em dicas de tipo (como FormatPx de @KirkMunro ) em vez de cmdlets que

  • Você normalizaria o provedor de Registro para ser baseado em _conteúdo_ - em vez de parecer uma demonstração de como um provedor de propriedade poderia funcionar? # 5444

  • Você refatoraria PSProviders profundamente para torná-los mais fáceis de escrever? É uma pena que sejam necessárias duas camadas de abstração para chegar a "SHiPS" e, finalmente, dar às pessoas uma maneira de escrever provedores que estão dispostas a usar

Que tal uma pergunta maior:

Estaríamos dispostos a reconsiderar a abordagem "de várias maneiras é melhor" e trabalhar em uma abordagem do "poço do sucesso"? Quer dizer, estaríamos dispostos a remover recursos que são considerados "o jeito fácil", mas que são fundamentalmente piores, em favor de ter apenas "o jeito certo" de fazer as coisas? Por exemplo:

  • Deixe o CmdletBinding sempre ativado
  • A notação Make @ () significa List[PSObject]
  • Make @ {} significa Dictionary[PSObject, PSObject] (ou Dictionary[string, PSObject] 😲)
  • Tornar _Process_ o bloco padrão
  • Talvez até mesmo torne _ValueFromPipelineByPropertyName_ o padrão ;-)

Todas ótimas sugestões, @Jaykul.

Tornar @() notação significa uma List[PSObject] : acho que podemos levar isso adiante e usar um tipo de coleção extensível com eficiência como o tipo de coleção fundamental do PowerShell, para que seja usado sempre que [object[]] atualmente é (e é usado internamente, bem como ao retornar coleções, sem penalidade de desempenho de conversão de tipo); existem desafios em torno do uso de + , mas @PetSerAl sugeriu uma implementação de lista personalizada para lidar com isso .

Tornando Process {} o bloco padrão em funções, como um aparte: é o que Filter faz atualmente, mas está limitado ao bloco - implícito - Process e esta variante de função aparentemente nunca realmente pegou; unificar os dois no contexto de Function faz sentido para mim.

Engraçado, na chamada standup da comunidade do Azure PowerShell agora, eles vão mudar de AzureRm para um novo módulo Az que é plataforma cruzada (sem AzureRm.Core separado) e com todos os prefixos de comando alterados de AzureRm para Az. Os dois ficarão lado a lado por um tempo, mas eles estão se movendo para o novo conjunto de comandos.

Uma pena que o PowerShell ainda não teve a oportunidade de criar um novo executável que seria executado lado a lado com o Windows PowerShell, mas que poderia romper com alguns dos crudes legados que o arrastam para baixo.

Imagine o que aconteceria se tentássemos:
Falando realisticamente, levaria pelo menos 6 meses de esforço concentrado para chegar a uma versão 'sem arrependimentos' e outros 6 meses para reiterá-la. Em seguida, os módulos de terceiros também precisam se adaptar a ele para que esta versão seja útil, o que levará pelo menos mais 6 meses para obter uma cobertura razoável (e tenha em mente que alguns módulos nunca o farão) ... Então, considere demora e problemas inesperados, etc ... Então, não, eu acho que é apenas uma ilusão que se pode se livrar de todas as dívidas técnicas em uma versão de uma vez (e ainda desenvolver e não apenas manter a versão antiga).

Por mais que eu desejasse que tal versão existisse, acho que só será possível chegar a ela lentamente, uma mudança significativa de cada vez. Com a v6, muitas alterações de interrupção já foram aceitas, mas acho que se incluirmos muitas alterações de interrupção em uma versão, será muito complexo atualizar scripts / módulos existentes. É bom discutir as alterações de interrupção mais valiosas, mas até que não haja uma versão LTS de pwsh, não acho que seja hora de pensar em ter um segundo trem de pwsh com alterações mais substanciais em paralelo à versão principal existente.

@bergmeister Concordo. No entanto, mesmo mudanças relativamente pequenas em um caminho central podem atrapalhar seriamente a adoção. Veja o Python 3. Demorou 10 _anos_ para realmente pegar. Com mudanças muito maiores, quem sabe quanto tempo levará para o Perl 6 ser dominante (e eles levaram 15 anos para criar o material certo, então 1,5 anos para o PowerShell ++ parece otimista :-)) Por outro lado, o PHP parece quebrar as coisas regularmente, possivelmente devido à forma e para que é usado.

Python 3 é certamente o show de terror, ele realmente pegou ainda? Ainda estou executando o 2.7 e não pretendo atualizar tão cedo. Mas também não ouvi muito sobre Perl 6 recentemente ...

Acho que as lições a aprender com o Python são para separar as mudanças significativas na linguagem de uma mudança de versão no mecanismo. Hipoteticamente, um mecanismo PS 7 ainda poderia executar arquivos de scripts anteriores (.PS1) em um modo de alterações sem interrupções, enquanto se o script fosse marcado como 7 compatível (digamos com uma extensão .PS7), eles poderiam declarar que foram atualizados _e_ o requer pelo menos PS 7 para funcionar. Espero que isso faça sentido.

Talvez a melhor história de sucesso seja JavaScript / TypeScript / Babel. Um transpiler (com suporte a mapa de origem) parece ser o caminho a percorrer para a evolução da linguagem.

Javascript é um caso especial. Você está praticamente preso a ele, então transpilar é realmente a única opção. O Typescript é "apenas" Javascript com extensões, portanto, é fácil para as pessoas adotarem. Qualquer programa javascript é um programa typescript, então você começa com o que tem e apenas adiciona anotações a partir daí. O Dart, por outro lado, é sua própria linguagem, mas transpila para javascript ou um runtime nativo no Chrome (pelo menos esse era o plano em um ponto). O Dart não parece ter sido muito adotado fora do Google, provavelmente porque é sua própria linguagem.

@BurtHarris

Python 3 é certamente o show de terror, ele realmente pegou ainda?

Eu estava lendo um artigo na semana passada em que o autor afirmava que a massa crítica foi alcançada para o Python 3. Que todos os módulos principais estavam disponíveis e agora as pessoas estavam migrando em massa. Veremos..

Interessante. Sempre me perguntei por que o 1 em .ps1 seria usado para adicionar uma espécie de controle de versão semântica aos scripts do PowerShell, permitindo que um script indique se ele foi adaptado para possíveis alterações significativas.

WRT .ps1 , nos reservamos o direito de alterar a extensão no caso de errarmos completamente a linguagem, resultando em alterações catastróficas no tempo de execução de forma que os scripts da versão anterior simplesmente não funcionassem. Mas mudar a extensão também leva a uma enorme pilha de trabalho porque muitas coisas no ecossistema estão vinculadas a uma extensão. Portanto, não é algo para se fazer de ânimo leve. E é claro, sendo parte do Windows, se bifurcarmos a extensão, ainda teremos que manter as duas versões (como o Python 2/3).

@bergmeister :

Preocupações válidas, mas no espírito de:

É bom discutir as mudanças importantes mais valiosas

por favor, compartilhe qualquer um que você possa ter em mente.

lentamente, uma mudança significativa de cada vez

Embora um mecanismo de aceitação (com escopo léxico) para alterações incompatíveis seja uma solução, estou preocupado com duas coisas:

  • A introdução fragmentada pode fazer com que ninguém seja capaz de lembrar qual versão é necessária para qual recurso; Perl (v5-) vem à mente.

  • A base de código se tornando inchada e difícil de manter, e o binário resultante sendo igualmente inchado, o que prejudica (pelo menos) o desempenho de inicialização.

Já disse isso antes: para mim - e isso é apenas um palpite - o v6 GA foi um infeliz compromisso entre deixar os veteranos insatisfeitos com as mudanças interrompidas enquanto carregam bagagem suficiente para impedir a adoção no mundo Unix.

Dito isso, dada a relativa juventude do PowerShell no mundo Unix, talvez haja (ainda) mais disposição para resolver os problemas por meio de mudanças incompatíveis. Como afirma @BrucePay , o Windows PowerShell terá que ser mantido de qualquer maneira e pode permanecer um porto seguro para compatibilidade com versões anteriores.

Acho que a escala das consequências negativas de tais mudanças significativas já foi abordada, e concordo com a maioria desses pontos. Estou escrevendo isso porque tenho dúvidas de que, em um contexto mais amplo, essas mudanças produziriam benefícios significativos em primeiro lugar. Pelo menos na forma como uso o PowerShell.

O OP inclui a seguinte declaração:

Por outro lado, o subproduto inevitável foi o acúmulo de dívida técnica que exige a memorização de exceções e é uma barreira para os recém-chegados.

A premissa implícita desta declaração parece ser que fazer essas várias alterações de interrupção aliviaria o fardo de memorizar exceções. Na verdade, esse seria um ótimo resultado. No entanto, não acredito que esse seja o resultado. O comportamento do PowerShell é deliberadamente rico. Isso o torna expressivo e imprevisível. Expressividade e previsibilidade parecem funcionar uma contra a outra, pelo menos entre as línguas que conheço.

Embora eu concorde que muitas das mudanças significativas mencionadas acima melhorariam um pouco a previsibilidade em alguns casos, muitos aspectos imprevisíveis e surpreendentes da linguagem permanecerão. Apesar de passar anos escrevendo PowerShell, ainda fico frequentemente surpreso com o comportamento do PowerShell que parece ser intencional e provavelmente não deveria ser alterado. Alguns exemplos recentes que vêm à mente são os seguintes:

  • vinculação retardada condicional de [scriptblock] argumentos (# 6419)
  • quando, exatamente, a enumeração implícita ocorre (# 5832)
  • conversão de [AutomationNull]::Value em $null durante a vinculação do parâmetro (# 6357 está relacionado)
  • o impacto de break e continue no fluxo de controle no pipeline (# 5811)
  • o que acontece quando você splat $null ( SO )
  • como o uso de scriptblocks em estados de sessão afeta $_ ( SO 1 , SO 2 )

Espero que esses exemplos sejam apenas uma pequena fração de muitas outras nuances cuidadosamente projetadas, mas surpreendentes, que ainda não descobri. Eu selecionei esses exemplos porque

  1. eles representam um comportamento que provavelmente não pode ser melhorado no PowerShell, e
  2. Não tenho esperança de identificar com segurança todas as suas implicações ao ler ou escrever PowerShell.

Estou bem com isso. A maioria esmagadora do comportamento surpreendente do PowerShell não tem impacto duradouro no meu sucesso com o PowerShell. Comportamentos surpreendentes quase sempre são detectados imediatamente por testes durante o desenvolvimento. Aprendo com as coisas que passam despercebidas e uso isso para melhorar minha estratégia de teste. Isso é verdade quer esses comportamentos surpreendentes sejam do tipo que poderia ser eliminado ou do tipo que deve permanecer.

Quer as alterações de interrupção propostas acima sejam feitas ou não, o PowerShell nunca se tornará tão previsível a ponto de reduzir significativamente a cobertura do teste. Em outras palavras, da maneira como eu uso o PowerShell, não acho que haja muita vantagem que seja possível fazer as alterações significativas propostas acima.

(Aliás, obrigado @ mklement0 por fazer essa pergunta diretamente. Isso está na minha mente há algum tempo. É bom ver a oportunidade para todos dizerem o que pensam.)

Obrigado, @ alx9r.

Acho que é importante distinguir entre complexidade _intrínseca_ e complexidade _extrínseca_:

  • A complexidade _Intrínseca_ deriva da complexidade inerente dos conceitos que estão sendo implementados, que no caso do PowerShell tem duas fontes principais: complexidade _internal_ da introdução de um novo paradigma (pipeline baseado em objeto) e da união de dois mundos distintos (sintaxe shell e sintaxe da linguagem de programação ) e complexidade _external_ da interface com mundos externos múltiplos e díspares.

  • A complexidade _extrínseca_ decorre de abstrações vazadas e inconsistências.

    • Essa complexidade deve ser _eliminada_.
    • Se isso não for uma opção devido a questões de compatibilidade com versões anteriores, tal complexidade deve ser _documentada como problemas conhecidos_.

    • O comportamento do escopo da variável do módulo de script (que você faz referência no contexto de $_ ) é mais do que apenas uma peculiaridade: é a causa raiz de um grande problema, o # 4568 mencionado anteriormente.

    • Todas as outras questões que você menciona me parecem cair na categoria extrínseca (em vez de ser o resultado de um design cuidadoso), porque todas se apresentam como inconsistências que não têm nenhuma razão ou benefício óbvio (documentado):

      • Faz mais sentido splatting com $null para passar _nenhum_ argumentos do que um argumento $null posicional.
      • Por que [System.Management.Automation.Internal.AutomationNull]::Value seria convertido em $null durante o _parameter binding_, mesmo que o tipo seja preservado em _direct variable assign_? Consulte https://github.com/PowerShell/PowerShell/issues/9150#issuecomment -473743805
      • Qual é o benefício do escopo _dinâmico_ de break e continue , em toda a pilha de chamadas, com _exclusão silenciosa_ se nenhum loop delimitador for encontrado?

Embora a riqueza de recursos e a junção de mundos díspares por si só tornem difícil lembrar de toda a complexidade _intrínseca_ necessária, minimizar a complexidade _extrínseca_ ainda é importante.

Ter que testar a abordagem pretendida primeiro sem apenas saber e confiar que funcionará - ou que as coisas quebrem inesperadamente devido a um comportamento surpreendente - é um sério obstáculo de produtividade (e prazer) (embora o PowerShell louvável torne muito fácil testar o comportamento de forma interativa )

Tudo se resume a conceitos sólidos (que não violam as expectativas intuitivas), nomenclatura descritiva e boa documentação:

Se eu não sei / não tenho certeza se me lembro corretamente, preciso saber onde procurar e ter fé que _problemas conhecidos e casos extremos_ também estão documentados (seja como parte dos tópicos de ajuda regulares ou por meio de links de há).

Portanto, mesmo se decidirmos que _eliminar_ a complexidade extrínseca não é uma opção, podemos pelo menos _documentá-la sistematicamente_ - e a discussão aqui pode servir como ponto de partida para compilar uma "galeria de armadilhas" (que também pode incluir casos de complexidade _intrínseca_ inevitável que pode ser surpreendente).

podemos pelo menos documentar sistematicamente - e a discussão aqui pode servir como ponto de partida para a compilação de uma "galeria de armadilhas"

Roman Kuzmin coleciona essa galeria há algum tempo, aqui: https://github.com/nightroman/PowerShellTraps

Obrigado, @HumanEquivalentUnit - parece uma ótima coleção.

Junto com os problemas coletados neste tópico, ele pode formar a base para uma galeria que faz parte da documentação _oficial_, com cada entrada aumentada, conforme apropriado:

  • um raciocínio de design que explica por que o comportamento, embora talvez surpreendente, é justificado afinal (uma explicação devidamente estruturada pode fazer o elemento surpresa ir embora)

  • um reconhecimento do problema, afirmando que:

    • ou: não será corrigido no interesse da compatibilidade com versões anteriores.
    • ou: que uma mudança futura está sendo considerada
    • ou: que o problema já foi corrigido no PS _Core_

É estranho que seja necessário perguntar se _é hora de "PowerShell vZeroTechnicalDebt" _

Exatamente para isso, há um comando de versão necessário.

Se algo quebra em uma nova versão, a única dor que sentimos é que temos que estudar e aprender as diferenças geralmente pequenas.

Mas nós (finalmente) conseguimos uma plataforma, que realmente fica melhor a cada iteração no núcleo. E muito mais fácil de manter.

Não, nunca é uma questão de iniciar projetos e decisões ruins por meio de novos conhecimentos e tecnologias.

Além disso, para açoitar o merda vivo de um cavalo morto, ainda é estupendamente difícil escrever uma função ou cmdlet bem comportado que lida com as diferenças mágicas entre o sistema de arquivos nativo do host e o provedor do sistema de arquivos. Esse código com nuances é difícil de escrever e, muitas vezes, erroneamente. Aqui está uma postagem sobre stackoverflow que respondi há cerca de sete ou oito anos que ainda é válida:

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

Ref: # 8495

Existem algumas regras de precedência de operador estranhas que merecem revisões:

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

Na maioria das línguas, seria mais comum esperar que esta fosse a saída:

1
2
3
4
5

Uma opção que tem seus próprios prós e contras é um novo sinalizador de recurso para habilitar todo o comportamento "ideal" para que seja opcional. Se tivéssemos alguma telemetria indicando que a maior parte do uso mudou para o novo comportamento, poderíamos inverter para desativá-lo. Isso tudo pode ser apenas um sonho, pois eu não vi nenhum caso do mundo real em que tal modelo funcionasse ...

Na verdade, isso seria _agradável_, mas dada a natureza bastante ... completa ... de algumas dessas revisões neste tópico, corre o risco de _dividir_ compatibilidade de scripts entre "normal" e "experimental", e potencialmente em várias partes, dependendo de quais sinalizadores um indivíduo ativou.

Isso significa que não podemos realmente confiar em nada que seja um recurso experimental dessa forma, nem escrever scripts e módulos que dependem deles, a menos que anexemos um grande aviso às páginas de documentos, ou potencialmente evitemos que sejam importados a menos que certas sinalizações estão habilitados.

Isso _pode_ ser evitável, entretanto ... se pudermos, à vontade, habilitar e desabilitar recursos experimentais específicos com base no escopo de cada módulo. Mas, visto que isso só complica ainda mais as coisas, nem tenho certeza se essa é uma solução particularmente boa.

@ vexx32 só para ficar claro, eu não estava sugerindo um sinalizador experimental que eventualmente se tornaria não experimental. Eu estava pensando em algo diferente (talvez mais como Enable-PSFeature , pois seria oficialmente suportado (e, portanto, protegido de alterações significativas, ao contrário dos recursos experimentais)). Eu também estava pensando que seria um único sinalizador em que você optar por essas novas alterações importantes como um conjunto, em vez de individualmente.

Oh, nesse caso ... Sim, absolutamente. Isso nos permitiria empacotar tudo sob um único guarda-chuva e realmente usá-lo. Muito melhor do que eu estava pensando! 😄

Adição de @ jszabo98 à lista de problemas compilada aqui, com base no # 8512:

-and e -or inesperadamente têm a _same_ precedência, enquanto na maioria dos idiomas (incluindo C #) -and tem precedência maior do que -or .

Expressão de exemplo que contraria as expectativas:

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

Devido à associatividade à esquerda e -and e -or terem a mesma precedência, o acima é avaliado como ($true -or $true) -and $false em vez do esperado $true -or ($true -and $false)

Isso tudo pode ser apenas um sonho, pois eu não vi nenhum caso do mundo real em que tal modelo funcionasse ...

Na verdade, já temos um modelo que funciona em todo o mundo - LTS. A MSFT usa isso para desenvolver o Windows 10. O modelo é usado para desenvolver distribuições Unix.
Para nós, isso significa que poderíamos lançar versões LTS PowerShell Core a cada dois anos e incluí-las nas versões LTS do Windows 10 e Unix LTS. (Um problema é sincronizar as datas de lançamento do Windows e do Unix para as versões LTS. Acho que a MSFT pode negociar com as principais empresas do Unix.)
Durante esses dois anos, estamos coletando alterações de interrupção menores, transferindo alterações de interrupção mais significativas para o próximo ciclo.
Para cada nova versão do LTS, o ScriptAnalyzer deve obter um conjunto de novas regras para facilitar a migração do script.
Com este modelo, os produtos e scripts críticos só terão que pular da versão anterior para a próxima após testes e migração completos.

Usei essa abordagem quando migrei passo a passo (versão por versão) um sistema antigo da versão do PHP 4 para a próxima versão do PHP até alcançar a versão compatível do PHP 5.x. Em cada etapa, tive que fazer mudanças relativamente pequenas e rápidas, após as quais o sistema continuou a funcionar por algum tempo até a próxima etapa.

Atualização: Deve estar sincronizado com as versões .Net Core LTS. Espero que o .Net Core 3.1 seja o próximo LTS e o PowerShell Core 6.x deveria estar na versão.

Honestamente? Parece uma ideia realmente _ótima_ para PowerShell Core. Isso nos permitiria fazer mudanças importantes como essa que realmente poderiam ajudá-lo a seguir em frente, sem sobrecarregá-lo com questões de compatibilidade, enquanto _também_ não sobrecarregar com muitas mudanças importantes no intervalo entre os lançamentos LTS.

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

Get-Variable -ValueOnly -Name myVar | Should -Be $var assertion falha quando $var é um array.

Isso ocorre porque Get-Variable -ValueOnly não usa a sobrecarga de enumeração para WriteObject() .

Em relação a ter várias versões do PowerShell com diferentes conjuntos de recursos e alterações importantes: Já temos isso com o legado e o núcleo. O que parece ruim à primeira vista, mas pode fornecer um modelo funcional para o futuro. Fique comigo...

Temos que definir quase todos os processos que usam o PowerShell para chamar o núcleo pwsh.exe antes de executar os scripts reais porque as tarefas agendadas do Windows, jenkins e muitos ambientes não reconhecem o núcleo do PowerShell. Fazemos isso porque estamos usando recursos que estão disponíveis apenas em uma determinada versão do PS.

Nos casos em que não podemos forçar uma chamada para o núcleo, temos que fazer com que cada script verifique em qual versão do PS ele está sendo executado e, em seguida, ligue a si mesmo usando a versão apropriada do PS no sistema host. Qualquer que seja o truque que usamos para fazer com que o código certo seja executado no ambiente certo, frequentemente estamos passando todo o ambiente e parâmetros para o mesmo script que já estava em execução.

Essa é a filosofia UNIX. Faça uma ferramenta fazer uma coisa e fazê-la bem. Em seguida, combine as ferramentas para criar um todo maior do que a soma das partes. Este é o mundo dos scripts, onde fazemos chamadas subshell para outros utilitários o tempo todo e processamos os resultados. Historicamente, isso é script. O Powershell tende para uma convenção estranha ao tentar manter todo o código nativo, mas isso é apenas uma convenção - não um requisito. PS roda com acesso ao sistema operacional e todos os utilitários disponíveis, assim como scripts de shell do Unix.

Isso seria muito mais fácil se a extensão do arquivo core tivesse sido atualizada para .ps6, para que o mecanismo do PowerShell pudesse fazer o trabalho por nós. Cada versão binária do PowerShell pode permanecer independente das outras, desde que cada chamada para um passe ambientes e retorne as saídas, erros, avisos, exceções para o outro - o que temos que fazer manualmente agora com grande esforço. Não é necessário ter sinalizadores compatíveis com versões anteriores no motor, desde que o motor saiba qual versão executar. A mudança de contexto envolvida já está acontecendo, então já estamos no pior cenário.

Seremos forçados a usar hacks terríveis no PowerShell para sempre, A MENOS que avancemos e quebremos as coisas de uma maneira bem definida. Quebrar mudanças não tornará nossas vidas mais difíceis do que já são. Alternativamente, se usarmos a melhoria contínua incremental e fizermos com que o mecanismo faça o trabalho pesado de alternar o contexto, nosso código (e vidas) será melhor.

@ jtmoree-kahalamgmt-com Obrigado pelo seu feedback! Sinta-se à vontade para abrir novas edições para compartilhar sua experiência e feedback e pedir novos recursos úteis.

Isso seria muito mais fácil se a extensão do arquivo core tivesse sido atualizada para .ps6, para que o mecanismo do PowerShell pudesse fazer o trabalho por nós.

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

E eu criei https://github.com/PowerShell/PowerShell/issues/9138 com base em seus comentários.

Python 3 é certamente o show de terror, ele realmente pegou ainda? Ainda estou executando o 2.7 e não pretendo atualizar tão cedo. Mas também não ouvi muito sobre Perl 6 recentemente ...

Acho que as lições a aprender com o Python são para separar as mudanças significativas na linguagem de uma mudança de versão no mecanismo. Hipoteticamente, um mecanismo PS 7 ainda poderia executar arquivos de scripts anteriores (.PS1) em um modo de alterações sem interrupções, enquanto se o script fosse marcado como 7 compatível (digamos com uma extensão .PS7), eles poderiam declarar que foram atualizados _e_ o requer pelo menos PS 7 para funcionar. Espero que isso faça sentido.

Acho que chamar Python 3 de show de terror é dramático demais. Se eu olhar para os principais pacotes baixados do PyPi e pegar os metadados de pacotes individuais sobre downloads v2 vs v3, vejo que a v3 quebrou 33% de penetração para praticamente todos os principais pacotes e para bibliotecas mais recentes como a biblioteca de solicitações de Ken Reitz divisão de quase 50/50: ontem houve 685k downloads de v3, vs 875k de v2.

A principal coisa que atrasou o Python 3 por muito tempo foi o apoio da comunidade científica aos pandas e pysci. Mas, nas estatísticas mais recentes, a comunidade científica agora prefere o Python 3 ao Python 2: https://pypistats.org/packages/pandas 231 mil downloads v3, contra 175 mil downloads v2.

Como diria o famoso Dr. W. Edwards Deming: "Em Deus confiamos; todos os outros, tragam dados!" Talvez você possa escrever um script PowerShell para analisar o conjunto de dados PyPi e me dizer como o Python 3 está usando dados horríveis: https://packaging.python.org/guides/analyzing-pypi-package-downloads/

Eu ficaria feliz com (nesta ordem exata):

  1. Sistema de módulo mais fácil de usar que produz bons rastreamentos de pilha.

    • Por exemplo, o arquivo psm1 permite descrever regras arbitrárias de como as funções são carregadas, mas isso causa rastreamentos de pilha crípticos onde todas as funções são agrupadas no arquivo psm1 logicamente. Isso significa que os números de linha nos rastreamentos de pilha são tão úteis quanto um bule de chocolate.
  2. Substitua a formatação da string do objeto Error padrão do PowerShell que engole 99% da utilidade da programação em uma arquitetura de von Neumann baseada em pilha.

    • C # mais ou menos acerta isso. PowerShell é muito inteligente pela metade.
  3. Corrija o redirecionamento nulo para que as três maneiras diferentes de redirecionar fluxos de saída para o dispositivo nulo sejam equivalentes em desempenho.

  4. Corrija o escopo dinâmico :

function a($f) {& $f}
function main() {
    $f = {write-host 'success'}
    a {& $f} # stack-overflow
    a {& $f}.getnewclosure() # okay
}
[void] (main)
  1. Corrigir captura de variável :
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. A produção se acumula; return não faz a mesma coisa que outras línguas; consertá-lo.

  2. PowerShell não tem açúcar sintático para descarte de recursos externos de objetos

  3. Reescreva PowerShellGet. É TERRÍVEL. Funções com centenas de linhas de código, exceções engolidas, sem cobertura de teste.

  4. Livre-se de como funciona o cache de módulo.

    • O cache não é seguro para threads. WTF?
    • Todo o conceito de cache de um módulo para fins de análise é um kludge / hack. Sei que na Microsoft há um grande suporte para esse recurso porque o PowerShell é muito lento, mas veja minha próxima sugestão para uma correção verdadeira.
    • A ÚNICA maneira de limpar seu AnalysisCache é reiniciar o PowerShell.exe
    • O armazenamento em cache de módulos para fins de análise acopla fortemente a análise ao estado de tempo de execução dos módulos.
  5. Fornece "módulos compilados" semelhantes a como os arquivos Py são compilados em arquivos pyc para melhorar o desempenho.

  6. Adicionar $none literal

    • Considere substituir $null por $none em muitos lugares
    • Reconsidere como ParameterSetName funciona e permita a passagem de $none valor especial para parâmetros, para que eu não precise escrever instruções de switch malucas para chamar o código de 5 maneiras diferentes. COM e C # dynamic fazem isso corretamente - o PowerShell torna isso um desastre. Um exemplo seria como eu tenho que mapear dinamicamente uma configuração de implantação para relatórios SSRS no módulo ReportingServicesTools com base no tipo de ocorrência de inscrição. Eu preferiria apenas passar um pacote de propriedades e fazer com que o método lidasse internamente com a complexidade, em vez de forçar a complexidade externa aos usuários finais. ParameterSetName é útil apenas como um conceito como um modelo, não é bom para programação. Ele confunde o polimorfismo ad-hoc com o intellisense.

@jzabroski Com relação à formatação ErrorRecord, há um problema separado usado para discutir isso: https://github.com/PowerShell/PowerShell/issues/3647 , uma vez que a formatação não é considerada uma alteração importante, podemos fazer melhorias lá.

Algumas de suas outras solicitações são potencialmente aditivas e não interrompem, portanto, essas devem ser questões separadas. Esta edição específica está discutindo solicitações de alterações significativas se tivéssemos a oportunidade e pudéssemos justificar fazer uma nova versão principal do PS.

@jzabroski Com relação à formatação ErrorRecord, há um problema separado usado para discutir isso: # 3647, uma vez que a formatação não é considerada uma alteração importante, podemos fazer melhorias lá.

_Por que_ não é uma mudança significativa? Porque os desenvolvedores não deveriam combinar padrões em strings em mensagens de erro em primeiro lugar? Eu acho que isso é válido. Mas ainda vai quebrar os consumidores a jusante.

Algumas de suas outras solicitações são potencialmente aditivas e não interrompem, portanto, essas devem ser questões separadas.

Quais você acha que são potencialmente aditivos? Posso criar problemas para eles, para não atrapalhar essa discussão, mas fiz essa lista com cuidado esta manhã. Eu odeio o PowerShell com uma fúria e paixão por trovões e raios explodindo. PowerShell é uma bola de boliche com uma faca de açougueiro. Meus 5 momentos mais embaraçosos como engenheiro aconteceram durante o uso do PowerShell. Nunca vou recuperar esse tempo.

Atualizei minhas sugestões para deixar claro por que essas sugestões de mudança são importantes (e impressionantes).

A produção se acumula; return não faz a mesma coisa que outras línguas; consertá-lo.

return se comporta como você espera dentro de um método class . O caminho "acumula saída" em uma função regular / avançado PS é bastante típico para ambientes shell script por exemplo, Korn Shell e Bash. Ou seja, copie vários comandos do console e coloque-os em uma função. Quando você invoca essa função, ela sai de cada comando da mesma forma que quando você os executa no console.

Minha principal reclamação com o PowerShell é que, no caso de função regular / avançada, return nunca deveria ter recebido argumentos. Porque algo como:

return $foo

é realmente executado como:

$foo
return

Isso dá às pessoas a sensação de que podem controlar a saída usando return e não podem.

return se comporta como você espera dentro de um método class . O caminho "acumula saída" em uma função regular / avançado PS é bastante típico para ambientes shell script por exemplo, Korn Shell e Bash.

Eu ouço pessoas dizerem isso, incluindo @BrucePay (veja ênfase minha , abaixo, citando-o), mas o resultado final é:

Apenas no PowerShell temos essa ilógica "retorno não retorna" sem sentido e a saída se acumula. Este é um vazamento de memória glorificado e você deve pensar nisso como tal.

Para ser honesto, em todos os lugares do PowerShell em ação eu procuro a frase "problemas", posso adicionar algo à lista vZeroTechnicalDebt. Aqui está o que Bruce escreve sobre a declaração de retorno:

7.3 VALORES DE RETORNO DAS FUNÇÕES
Agora é hora de falar sobre o retorno de valores de funções. Temos feito isso o tempo todo, mas há algo que precisamos destacar. Como o PowerShell é um shell, ele não retorna resultados - ele grava a saída ou emite objetos. Como você viu, o resultado de qualquer expressão ou pipeline é emitir o objeto de resultado para o chamador. Na linha de comando, se você digitar três expressões separadas por ponto-e-vírgula, os resultados de todas as três instruções serão exibidos:
[...]
Na abordagem tradicional, você deve inicializar uma variável de resultado, $result , para manter a matriz que está sendo produzida, adicionar cada elemento à matriz e, em seguida, emitir a matriz:

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

Este código é significativamente mais complexo: você deve gerenciar duas variáveis ​​na função agora, em vez de uma. Se você estivesse escrevendo em uma linguagem que não estendesse automaticamente o tamanho do array, seria ainda mais complicado, pois você teria que adicionar código para redimensionar o array manualmente. E embora o PowerShell redimensione automaticamente o array, ele não é eficiente em comparação com a captura da saída transmitida. O objetivo é fazer você pensar sobre como você pode usar os recursos que o PowerShell oferece para melhorar
seu código. Se você estiver escrevendo um código que constrói matrizes explicitamente, considere examiná-lo para ver se pode ser reescrito para aproveitar as vantagens do streaming.

Acho que a motivação por trás desse recurso é boa, mas existem maneiras muito mais limpas e mais fáceis de ler para atingir o mesmo objetivo: Promoção implícita de um único objeto para uma coleção de objetos. Além disso, o PowerShell já tem uma maneira simples de retornar a saída para redirecionamento: Write-Output .

No Bash, return sai de uma função, ponto final.

Hã? O Bash acumula saída da mesma forma:

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

Assim como o PowerShell, o Bash ainda exibe tudo de cada comando até a instrução return .

Ah, acho que você está se referindo ao que é retornado em $? . Sim, isso é uma diferença. Acho que esses devem ser int valores de retorno no Bash para indicar sucesso / falha. No PS, $? indica sucesso / falha como um bool com base nos erros gerados pela função. Na prática, não acho que $? não seja muito usado. Em vez disso, uso o tratamento de exceções por meio de try/catch/finally .

Sim. return tem apenas um propósito nesses idiomas. No PowerShell, ele é misturado com a saída. Misturar valores de retorno e fluxos de saída é uma má ideia. Também pode levar a problemas de desempenho semelhantes a vazamento de memória. O que você realmente quer são co-rotinas ou "retorno de rendimento", ou geradores, construtores de geradores e scanners (como no ícone). O motivo é que o consumo de entrada é lento e a falha suspende a computação, permitindo que a parte da computação gerada até agora seja descartada.

Você pode pensar em "redirecionamento de saída" como realmente dois objetos: um Processor e um ProcessorContext.

Edit: Eu vejo porque você refutou meu ponto original. Você pensou que eu estava dizendo que é ruim ser capaz de produzir resultados. Em vez disso, estou dizendo que é ruim retornar a saída da maneira que o PowerShell faz.

Em vez disso, uso o tratamento de exceções por meio de try/catch/finally .

Isso é muita digitação e não expressa claramente a intenção em todos os casos.

Bem, o PowerShell é baseado em .NET que é coletado como lixo, então não deve sofrer "vazamentos" de memória. Isso não significa que você não pode ter um tesouro de memória. Portanto, você deve ter um pouco de cuidado ao lidar com grandes saídas / coleções atribuídas a uma variável ou colocadas em uma tabela de hash de algum tipo.

Acho que a instrução return do PowerShell deve ter apenas um único propósito, que é retornar mais cedo - nada mais. Quando alguém faz return 42 isso não tem relação com $? . É apenas mais uma saída de item (produzida) para o fluxo de saída.

Como mencionei antes, $? é gerenciado pelo PowerShell e não tem nada a ver com o que uma função produz / retorna. $? é definido com base nos erros gerados (ou na falta deles). Isso significa que quando você executa $res = foo - $ res conterá cada saída de objeto pela função foo. Se você quiser saber se a função "falhou", você pode inspecionar $? em cada comando que executar OU você pode agrupar vários comandos em um bloco try/catch . Acho o primeiro muito menos digitação (em scripts).

ouvir as pessoas dizerem isso, incluindo @BrucePay (veja ênfase minha, abaixo, citando-o), mas o resultado final é:
No Korn Shell, return sai de uma função, ponto final.
No Bash, return sai de uma função, ponto final.

No PowerShell, o retorno também sai de uma função:

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

Misturar valores de retorno e fluxos de saída é uma má ideia. Também pode causar problemas de desempenho como vazamento de memória

Não há mistura porque não há diferença entre o fluxo de saída e o valor de retorno no PowerShell. Você está falando como se a função gravasse as strings em stdout e retornasse separadamente um ponto de exclamação para o chamador, mas não retorna. Apontar para write-output parece o mesmo erro; todos estes: write-output 'x' , 'x' , return 'x' enviam algo pelo mesmo caminho de saída, para o pipeline. Não há valor de retorno separado.

O que você realmente quer são co-rotinas ou "retorno de rendimento", ou geradores, construtores de geradores e scanners (como no ícone). T

Isso significaria que se você escrevesse um comando na linha de comando e o colocasse em um script ou função, ele se comportaria de maneira diferente:

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

Eu gosto de ver a mesma saída em todos os três casos, não ter a saída engolida em dois casos porque eu não fiz yield return sem motivo. A maneira como o PowerShell faz isso é consistente e conveniente para um shell e escrever scripts de shell / lote - pegando algo que você escreveu na linha de comando e colocando em algum lugar onde possa chamá-lo continuamente. Fazer isso não deve mudar completamente o comportamento do código.

O motivo é que o consumo de entrada é lento e a falha suspende a computação, permitindo que a parte da computação gerada até agora seja descartada.

O pipeline do PowerShell é controlado pelo tempo de execução, se bem entendi o que você está dizendo, ele já é capaz de fazer isso:

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

o select encerrará o pipeline assim que ele tiver três coisas. O restante dos milhões de números não foram inseridos em foreach . As informações que se movem pelo pipeline são um modelo "push", mas são controladas pelo mecanismo PowerShell.

Embora, por ser um shell, não esteja claro o que significaria para "falha de entrada ao suspender a computação" se a entrada vier de um comando nativo ou algo fora de um cmdlet PS.

Fornece "módulos compilados" semelhantes a como os arquivos Py são compilados em arquivos pyc para melhorar o desempenho.

Acho que a solução pretendida para obter mais desempenho é escrever módulos em C #. O código do PowerShell é compilado JIT, em algumas situações, mas os membros da equipe PS disseram antes que não tinham nenhum plano de expor isso aos usuários finais para personalização, por exemplo , PS Conf Asia do IIRC Dongbo Wang palestra sobre os internos do PS Engine em novembro de 2018. (Eu acho isso pode mudar).

Corrija o redirecionamento nulo para que as três maneiras diferentes de redirecionar fluxos de saída para o dispositivo nulo sejam equivalentes em desempenho.

Desejável, mas parece improvável; $null = 4 é um vínculo de variável simples, 4 | out-null precisa da sobrecarga de iniciar um pipeline e fazer a resolução de comandos - e se alguém tivesse intencionalmente feito out-null um proxy-wrapper que adicionou o registro código? É um problema técnico, que solicitar a execução de um pipeline e cmdlet é mais lento do que uma expressão?

Corrija o escopo dinâmico:

Se você quer dizer "não use escopo dinâmico", foi uma escolha deliberada, por exemplo, Bruce Payette aqui dizendo "escopo dinâmico, isso quase nunca é usado; antes, como há 30 anos, o LISP tinha escopo dinâmico, mas é muito raramente usado agora. A razão de estarmos usando é: é isso que os shells usam. Os shells usam o equivalente ao escopo dinâmico, até mesmo o Bash, e os shells Unix usam o equivalente ao escopo dinâmico, onde as variáveis ​​do contexto do chamador - no caso as variáveis ​​do contexto do chamador são copiadas para o contexto filho. [..] Com uma modificação, normalmente no escopo dinâmico você procura a cadeia de chamadas e define a variável onde ela existe, não fazemos isso, definimos a variável no escopo atual o tempo todo, e novamente isso emula o comportamento dos shells ".

Isso pode ser irritante, mas isso é "dívida técnica" para um shell e seria "zeroTechnicalDebt" se fosse alterado?

Bem, o PowerShell é baseado em .NET que é coletado como lixo, então não deve sofrer "vazamentos" de memória. Isso não significa que você não pode ter um tesouro de memória. Portanto, você deve ter um pouco de cuidado ao lidar com grandes saídas / coleções atribuídas a uma variável ou colocadas em uma tabela de hash de algum tipo.

Eu pedi a um administrador de sistema, que não é um engenheiro, que escreveu alguma lógica para tentar tabular metadados em todos os arquivos em um compartilhamento de arquivo de 8 TB. Isso não acabou bem: consumiu muita memória e também atrelou o sistema a 100% da CPU ... não conseguimos nem fazer login na caixa depois que o script começou, e a única solução _na produção_ para salvar o servidor foi reiniciá-lo forçosamente.

Na minha experiência, o PowerShell torna mais fácil escrever esse tipo de lixo. Você pode fazer e criticar meu feedback da maneira que desejar. Isso não mudará o fato de que o PowerShell facilita a escrita de scripts que podem travar as máquinas de produção com eficácia.

@jzabroski você pode compartilhar alguns detalhes do script?

O pessoal do Powershell tenta encorajar o streaming de pipeline ( get-childitem | foreach-object {} ) em vez de pré-carregar um conjunto de dados ( $files = get-childitem; $files | foreach-object ) deliberadamente para reduzir o uso de memória. Fez esse carregamento antecipado?

Houve uso de Group-Object para coletar os arquivos semelhantes? Recentemente, houve melhorias no desempenho do Grupo-Objeto que reduziriam o uso da CPU, se não o uso da memória.

O PowerShell é de núcleo único por padrão, havia código de jobs / espaços de execução nele, era um servidor de núcleo único? PS 6.1 tem Start-ThreadJob que permite vários trabalhos com muito menos overhead e throttling para ajudar a controlar o uso da CPU.

Na minha experiência, o PowerShell torna mais fácil escrever esse tipo de lixo

Todos os tutoriais e livros que dizem a você para escrever $files = @(); $files += $f devem ter custado ao mundo uma pequena fortuna em ciclos de CPU e memória. Esse é um padrão lento, caro de processador e memória e extremamente comum. Estou curioso para saber se esse padrão estava no script? (E se houver alguma maneira de bloqueá-lo sem quebrar a compatibilidade com versões anteriores, ou tornar + = inválido em arrays em futuras alterações significativas?).

Mas, há uma troca aqui. Get-ChildItem -Recurse tem 22 caracteres e consome mais de 800 MB de RAM apenas no meu drive C:, o C # para fazer isso sem usar toneladas de memória tem mais de 50 linhas e não obtém metadados de arquivo, e será mais como 80 MB. Isso não é PS com vazamento de memória, é PS usando para obter conveniência do usuário.

Existe alguma linguagem em que você não precisa se preocupar com os detalhes de como funciona, pode escrever códigos úteis, não consegue escrever códigos ruins e não precisa ser um programador habilidoso? (E qual também é uma concha?)

Alguma das alterações propostas anteriormente fez com que o script funcionasse?

Você pode escrever um script Bash para listar arquivos, gerar um processo para cada um, até que o servidor caia e você não consiga usar o SSH nele. Você pode escrever um script Python que os.walk() s a árvore de arquivos e chama os.stat() para cada arquivo e usa menos memória e termina mais rápido - mas ocupa 10 linhas em vez de 1 e você ainda precisa se preocupar com OS e se preocupam com os detalhes do código (e ainda não é um shell e não funcionará com provedores PS).

Eu gostaria que o PS tivesse mais "poços de sucesso" e menos riscos de tropeçar. Lista de nomes de arquivo rápida sendo um deles para o qual vejo muito uso. Mudá-lo radicalmente para um idioma completamente diferente e atrapalhar as áreas em que funciona bem não é o suficiente.

Você quer Python, Python existe, é uma linguagem linda, não é prejudicada por tentar se encaixar em dois mundos diferentes e manter o escopo e a semântica de retorno de função de um shell de trinta anos de um sistema operacional diferente (por que, Microsoft), ou pelo compromisso da Microsoft para compatibilidade com versões anteriores (e obsessão com .Net), então ele consegue se esquivar de todas essas questões, ao invés de enfrentá-las.

Eu gostaria que o PS tivesse mais "poços de sucesso" e menos riscos de tropeçar.

Se eu pudesse adicionar vinte polegares para cima apenas para isso, eu o faria. Este é exatamente o padrão e a abordagem que o PowerShell precisa adotar.

For-EachObject é uma função do PowerShell para pegar coleções do PowerShell e convertê-las em rastreamentos de pilha ilegíveis.

Hoje, peguei um script escrito pela Amazon para listar discos EC2 do Windows: https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2-windows-volumes.html#windows -list-disks

Adivinha? Eu não tinha Initialize-AWSDefaults configurado neste PC e, como o autor do script usou ForEach-Object, meu rastreamento de pilha parecia com este _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

Isso acontece _ toda vez que as pessoas usam expressões ForEach-Object em seu código_. Os rastreamentos de pilha são _sempre terríveis_. Reescrever isso é doloroso. Felizmente, é apenas um script que copio e colo em um arquivo aleatório, salvo e executo. Se for um problema em um módulo grande, tenho que obter o código-fonte do módulo, reescrever o script, espero que Deus o refaça. Não quebrei nada no módulo convertendo a expressão ForEach-Object E espero Posso descobrir onde está o erro REAL apenas para que eu possa _fazer meu trabalho_. E agora eu tenho um código personalizado que fiz bifurcação de algum repositório principal, apenas para descobrir por que estou recebendo algum erro porque Write-Error do PowerShell É HORRÍVEL em comparação com o idioma simples do C # de throw new Exception("message", ex) .

Reescrevendo para usar for ($var in $list) , acumulando os resultados em uma variável $results , recebo o seguinte erro:

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, e a propósito, toda vez que tenho que fazer isso, não consigo me lembrar das regras estúpidas do PowerShell para adicionar itens a listas, então tenho que https://www.google.com/search?q=powershell+append no Google

Se você não conhece C # ou uma linguagem que faz isso bem, é fácil argumentar que esse comportamento está OK. No entanto, C # lida com esse problema de forma inteligente, envolvendo a exceção em uma AggregateException, de modo que você obtenha a exceção interna e externa. PowerShell? Hahahaha. Sim, isso seria muito útil.

@jzabroski você pode compartilhar alguns detalhes do script?

Infelizmente (_ e felizmente _), não me lembro disso. Era um compartilhamento de arquivos, então quase certamente tinha um ou dois núcleos, no máximo. Se fossem dois núcleos, a CPU restante provavelmente foi usada pelo Windows Defender.

Editar: Encontrado no chat do Teams.

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

Como um bônus adicional, acabei de falar com a pessoa que executou este comando (e me senti incrivelmente mal), e ele disse que o caso de uso "era pegar os 32 maiores arquivos para o requisito de dimensionamento do grupo de replicação inicial para DFS".

Não há mistura porque não há diferença entre o fluxo de saída e o valor de retorno no PowerShell.

Ainda não vi pessoas usarem isso corretamente na primeira tentativa. Novamente, não é isso que o bash ou qualquer outro shell faz. Embora tenha sido uma experiência interessante unificar os dois conceitos, foi um desastre completo e absoluto do ponto de vista da legibilidade. Não ajuda que Write-Output não funcionou no PowerShell v6 por 16 meses.

@jzabroski de sua saída de For-EachObject parece que você não está usando o Powershell Core (é para isso que este repositório é dedicado), poderia experimentar o Powershell core e ver se consegue reproduzi-lo. Certifique-se de usar a versão mais recente. Esteja ciente de que a funcionalidade específica do Windows (como Get-Disk) não faz parte do Powershell Core, você precisará levar isso em consideração ao fazer seus testes.

Ainda não vi pessoas usarem isso corretamente na primeira tentativa. Novamente, não é isso que o bash ou qualquer outro shell faz.

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

Isso é a saída de dois comandos separados acumulados em uma função Bash e o retorno Bash não funciona como funções de retorno em C #. O que, especificamente, "não é o que o Bash faz"?

Você não está capturando o valor de retorno corretamente para o exemplo do bash. o retorno é perdido porque o $? é sempre o último comando. no seu caso, 'echo'.

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

POWERSHELL faz isso de maneira diferente

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

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

Mais esquisitices do PowerShell:

Chocolatey adiciona um RefreshEnv.cmd que é um GODSEND. Por que isso não é fornecido apenas com o PowerShell? Por que preciso reiniciar meu Shell ou baixar algum script idiota da Internet para redefinir meu $ env?

@jzabroski Isso foi trazido em https://github.com/PowerShell/PowerShell-RFC/pull/92 Você pode querer falar aqui

Crie um modelo de objeto padrão para provedores de PowerShell: https://github.com/PowerShell/SHiPS/issues/66#issuecomment -368924485

No link sobre o SHiPS questão 66, descrevo os possíveis cmdlets candidatos. Não está claro para mim por que não podemos implementar algo como o comportamento de typeclass Haskell para esses cmdlets principais.

Estas APIs já estão no PowerShell.

@iSazonov Você pode editar e citar o que está respondendo e adicionar algum contexto, como um hiperlink para a documentação das referidas APIs? TYVM

@jzabroski Procure os arquivos na pasta srcSystem.Management.Automationnamespaces.

POWERSHELL faz isso de maneira diferente

É bash que está fazendo isso de maneira diferente - return só pode definir o código de saída do processo (inteiro) (não é isso que "return" significa em outras linguagens de programação, é exit ) .

O PowerShell também pode fazer isso , com exit 5

Acontece que não usamos códigos de saída com muita frequência no PowerShell (basicamente apenas para tarefas agendadas e interoperabilidade com outros sistemas operacionais), porque não é um shell baseado em processo e as funções não devem sair.

No final do dia, é o mesmo motivo pelo qual detectar o tipo de identificador de stdout não é útil no PowerShell (e por que "redirecionar" para out-null não é o mesmo que lançar para [void] ou atribuir a $null )

Recomendo que as pessoas apresentem novos problemas se realmente tiverem feedback que desejam resolver - uma vez que a equipe _clara e inequivocamente descartou_ uma versão não compatível com versões anteriores do PowerShell, não sei por que este tópico continua ...

uma vez que a equipe _clara e inequivocamente descartou_ uma versão não compatível com versões anteriores de

Isso é novidade para mim. Como isso fica claro? Por favor, forneça links para postagens ou outra documentação.

É um pouco frustrante para alguns de nós ouvir "nunca quebraremos a compatibilidade" enquanto lutamos com alguma quebra todos os dias. O objetivo deste tópico é que o mercado livre de ideias possa resolver alguns desses problemas, ao contrário do upstream.

Alguém pode achar vantajoso o suficiente para fazer fork do PowerShell e criar uma versão com débito técnico mínimo. Esse grupo se beneficiará com as idéias apresentadas neste tópico. (É assim que o mundo funciona agora. Que vença o melhor PowerShell.)

Vou reiterar que a equipe já produziu uma versão 'compatível com versões anteriores' do powershell renomeando o comando de powershell para pwsh. O poder (SHELL) é uma casca. o trabalho de uma concha é ser a cola para os humanos que une os sistemas digitais. Não é um binário compilado com dependências externas mínimas. Mesmo as linguagens de programação tradicionais planejam e fazem alterações significativas.

POWERSHELL faz isso de maneira diferente

É bash que está fazendo isso de forma diferente - return só pode definir o código de saída do processo (inteiro) (que não

Estou curioso sobre outras conchas. O que eles fazem? korn, csh, etc.

Aqui está um artigo que discute a declaração de retorno em vários idiomas: https://en.wikipedia.org/wiki/Return_statement

Ele afirma que o sistema operacional [shells] permite que várias coisas sejam retornadas: código de retorno e saída.

Minha equipe tem uma variedade de scripts que são executados apenas no PowerShell 5, embora usemos o PowerShell 6 o máximo possível. Na minha experiência, a premissa de que o PowerShell é totalmente compatível com versões anteriores é definitivamente falsa. Existem pelo menos alguns casos extremos (por exemplo: ErrorActionPreference não se comportando intuitivamente) que definitivamente devem ser tratados como uma alteração significativa - casos em que fazer a correção seria menos "interrompido" do que não fazê-lo.

@chriskuech existe um problema detalhando seu problema com ErrorActionPreference ?

@ SteveL-MSFT Acredito que o Mosaic ao qual @KirkMunro vinculou diretamente abaixo dos comentários de

Dito isso, @iSazonov encerrou a edição original do https://github.com/PowerShell/PowerShell/issues/7774

Parece que essas coisas continuam surgindo, de diferentes formas, e o Comitê continua fechando questões em torno disso.

@jzabroski encontrou meu principal problema visando a causa raiz.

Também apresentei um problema no passado em torno de um dos sintomas: Invoke-WebRequest lançando um erro de encerramento. Eu pessoalmente testemunhei várias pessoas completamente pasmadas em torno do boilerplate try / catch inteiro para lidar com solicitações HTTP com falha, o que ocorre porque os métodos .NET internos lançam erros de finalização. Em cada um dos três casos diferentes, o engenheiro respondeu com um palavrão quando expliquei o comportamento subjacente e por que o problema supostamente nunca seria corrigido.

Para resumir , os erros de encerramento encerram o script porque os criadores do PowerShell acreditam que o script não pode prosseguir logicamente além do erro, mas não acho que isso seja literalmente qualquer um, exceto a decisão do criador de scripts a tomar caso a caso. Apenas o criador de scripts pode decidir se deseja que o script seja Stop , SilentlyContinue , Continue , etc.

(Tangencial ao problema acima)

Se o PowerShell implementar um modo opcional "ZeroTechDebt", definitivamente acho que $ErrorActionPreference = "Stop" deve ser definido por padrão. Obviamente, esta configuração não faz sentido em um REPL e, portanto, não deve ser definida por padrão para todos os cenários, mas literalmente todos os meus scripts são prefixados com $ErrorActionPreference = "Stop" para ativar a "programação defensiva" e se comportar como um "normal " linguagem de programação.

@chriskuech Se ainda não o fez, dê uma olhada nesta coleção de RFCs: https://github.com/PowerShell/PowerShell-RFC/pull/187. Eles falam diretamente sobre o que você está falando aqui, sem a necessidade de uma nova versão do PowerShell com débito zero de tecnologia.

Você também pode encontrar os quatro RFCs em edições separadas na página de Problemas desse repositório, se isso os tornar mais fáceis de ler / resumir. Basta procurar os problemas em aberto postados por mim e você os encontrará

@ SteveL-MSFT Aqui está um problema semelhante que impede minha produtividade. Não é $ErrorActionPreference mas $ConfirmPreference :

Abaixo está um script feio que escrevi para definir os volumes de disco do SQL Server para 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

Alguns pontos colaterais:

  1. A documentação de Format-Volume precisa de mais. Apenas dois exemplos? E a documentação oficial não está em sincronia com o site: https://github.com/MicrosoftDocs/windows-powershell-docs/issues/1170
  2. Por que preciso ir para StackOverflow para aprender como evitar uma GUI para uma linguagem de script?
  3. O fato de ser tão sujeito a erros / estupidamente difícil de conseguir escrever "sem pensar" apenas destaca questões relacionadas como https://github.com/PowerShell/PowerShell-RFC/issues/198 - é outro exemplo de como a promessa de Bruce Payette em " PowerShell em ação "que você apenas digita o que acha que é certo e funciona ... é completa e totalmente falso.
  4. Consulte também o problema de @ mklement0 : https://github.com/PowerShell/PowerShell/issues/4568

Requer -Version não tem como especificar uma versão máxima

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

Isso é irritante quando você está escrevendo funções avançadas que carregam bibliotecas do .NET Framework em que a API é completamente diferente entre o .NET Framework e o .NET Core, como funciona a API AccessControl.

@jzabroski Você pode especificar a edição, no entanto, para separar isso:

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

Apenas uma nota rápida que @rjmholt iniciou oficialmente uma discussão sobre como implementar e gerenciar mudanças importantes: # 13129.

Além disso, # 6817 e # 10967 são mais comportamentos que valem a pena revisitar, uma vez que alterações importantes são permitidas.
(Eles têm a mesma causa raiz, explicada em https://github.com/PowerShell/PowerShell/issues/10967#issuecomment-561843650).

O fato de , ser mais forte do que + é lógico, pois o PowerShell trata mais de listas do que de números. NA MINHA HUMILDE OPINIÃO.

@rjmholt iniciou oficialmente uma discussão

Devo dizer que não é mais oficial do que qualquer outra discussão

Eu adoraria ter uma verificação rigorosa das importações e do tempo de edição das assinaturas de funções.
Você está pensando em introduzir uma nova semântica de importação, como os módulos EcmaScript fornecem?
Não há mais poluição de namespace global. Mecânica de importação lenta.

Esta página foi útil?
0 / 5 - 0 avaliações