Powershell: Não requer {...} em torno de nomes de variáveis ​​para acesso de membro nulo-condicional

Criado em 17 dez. 2019  ·  53Comentários  ·  Fonte: PowerShell/PowerShell

Acompanhamento de # 3240.

O acesso de membro nulo-condicional ( ?. ) foi implementado e estará disponível como um recurso _experimental_ em 7.0

No entanto, no interesse da compatibilidade com versões anteriores, a implementação atualmente requer que o nome da variável seja colocado entre {...} , porque ? é tecnicamente um caractere legal em um nome de variável cujo uso surpreendentemente _não_ requer {...} .

Ou seja, se você deseja ignorar uma tentativa de chamar $obj.Method() se $obj for $null (ou indefinido), você esperaria o seguinte, análogo a C #:

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

Em vez disso, você deve usar atualmente:

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

_Update_: Null-coalescing - $var??='default and $a??$b - e o operador ternário - $var?1:0 - são afetados de forma semelhante (embora haja o uso de espaços em branco antes de ? resolve o problema).

Este requisito é (a) inesperado e (b) complicado:

  • Novos usuários não esperam a necessidade de {...} e não necessariamente sabem que _it_ é o que é necessário quando o seguinte falha (possivelmente _não detectado_, a menos que Set-StrictMode -Version 2 ou superior esteja em vigor): $o = [pscustomobject] @{ one = 1 }; $o?.one

  • Mesmo depois de os usuários _do_ saberem da necessidade de {...} :

    • Eles se esquecerão de usá-lo ocasionalmente, por causa da necessidade contraintuitiva dele.
    • Quando eles se lembrarem, esse requisito aparentemente artificial será uma fonte contínua de frustração, especialmente porque {...} é difícil de digitar.

O uso de {...} não deve ser necessário e, embora não exija uma alteração _breativa_, pode-se dizer que se enquadra no balde 3: improvável área cinzenta .


Por que essa mudança deve ser considerada aceitável:

Observação: @rjmholt discute por que alterar a sintaxe do PowerShell é altamente problemático _ em geral_ neste comentário , mas pelos motivos apresentados a seguir, acho que vale a pena abrir uma exceção aqui.

?. é um _novo_ recurso sintático; por definição, os scripts que o usam _não_ (significativamente) são executados em versões mais antigas - a menos que você especificamente adicione condicionais que forneçam caminhos de código compatíveis com a versão legada, mas isso parece dificilmente valer a pena - você, então, apenas ficaria com os recursos legados.

Sim, a interpretação de $var?.foo em scripts antigos que usavam $var? como nome de variável seria interrompida, mas:

  • ? nunca deveria ter sido permitido como parte de um identificador _sem incluí-lo em {...} _.

  • Já que ser capaz de fazer isso é inesperado e provavelmente _desconhecido_ para muitos, tais nomes de variáveis ​​são extremamente raros na natureza, falando por experiência pessoal, mas a análise de @mburszley fornece evidências mais tangíveis .

    • Até mesmo Ruby permite apenas ? (e ! ) no _end_ dos identificadores, e lá apenas de identificadores de _método_, e eu suspeito que a maioria dos usuários Ruby estão cientes de que não devem _não_ assumir que outras linguagens suportam a mesma coisa.

Então, pragmaticamente falando:

  • A grande maioria do código existente não será afetada - um token como $var?.foo simplesmente não será encontrado.

  • Se você escrever $var?.foo com a nova semântica, então sim, executá-lo em versões mais antigas pode resultar em um comportamento diferente (em vez de interromper de maneira óbvia), dependendo de qual modo estrito está em vigor - mas _você deve sempre aplique a versão mínima necessária para executar seu código conforme pretendido de qualquer maneira_ ( #requires -Version , chaves de manifesto do módulo).

Em suma, para mim, este é um caso claro de mudança no balde 3: uma mudança tecnicamente inovadora que quebra muito pouco código existente enquanto oferece benefícios reais (ou, inversamente, evita dores de cabeça perenes devido a comportamento inesperado).

Committee-Reviewed Issue-Enhancement WG-Language

Comentários muito úteis

@rjmholt , embora eu ache que todos podemos apreciar como uma mudança como essa é complicada _em princípio_, neste caso específico não importa _na prática_, e eu não acho que uma regra PSSA seja sequer necessária:

  • A análise de @ThomasNieto mostrou que - se acreditarmos que o corpus representa todo o código do PowerShell por aí - que _virtualmente ninguém usa variáveis ​​com nomes que terminam em ? _.

  • Se eu fosse adivinhar, a maioria das pessoas nem mesmo _pensaria_ em usar esses nomes de variáveis, porque não esperariam que funcionassem (eu incluído) e provavelmente nem notariam a exceção de que o $? automático bash ).

    • Na verdade, olhando mais de perto a análise de @ThomasNieto revela que, entre os 8 arquivos listados:

      • 5 contém apenas _falsos positivos_, ou seja, variáveis ​​usa dentro de _strings_, como "Are you sure you want to kill $vParam?" - na verdade, esses usos são _interrompidos_ e revelam que os autores do script _não_ esperavam que ? fossem parte do nome da variável.

      • 2 desses arquivos não estão mais disponíveis publicamente.

      • Apenas _1_ deles é um uso genuíno de ? variáveis ​​finais - como variáveis ​​_Booleanas (por exemplo, $key1? ), que, à parte, são, portanto, _não_ combinadas com o operador de acesso de membro , . / cc @stevenayers.

Com base no que precede, isso me parece um livro didático Balde 3: Mudança

_Documentar_ a mudança de comportamento (com uma justificativa) deve ser suficiente.

Claro:

  1. isto é baseado em (a) a análise ser correta e (b) o corpus publicamente disponível ser representativo.

  2. está em total desacordo com a afirmação do comitê de que "havia bastante uso de nomes de variáveis ​​terminando com o ponto de interrogação" (o que, é claro, só seria um problema se esses nomes de variáveis ​​não estivessem entre {...} ).

Partindo do pressuposto de que 1. é verdadeiro (não ouvimos nada para apoiar 2.), temos duas opções:

  • Retire o band-aid e simplesmente desabilite ? em nomes de variáveis ​​daqui para frente, a menos que estejam incluídos em {...} (com a exceção óbvia de $? ) - isso quebrará o pequeno proporção de scripts que atualmente dependem disso.

    • Ou seja, algo como $key1? que não é seguido por . nem por outro ? ( $key1??'else' ) nem por uma expressão ternária ( $key1?1:0 ) causaria um _erro do analisador_.

      • Como mostram os exemplos de operador ternário e coalescência nula, eles também se beneficiariam com essa mudança - atualmente, você precisa de um _space_ - $key1 ?1:0 / $key1 ??'else' - ou {...} - ${key1}?1:0 / ${key1}??'else'

    • Dentro de _cadeias expansíveis_, ? seria mais considerado parte de um nome de variável (não {...} -enclosed), então, na verdade, _fixaríamos_ scripts existentes que usaram por engano strings como "Are you sure you want to kill $vParam?" . 😁
  • Se realmente sentimos que precisamos evitar quebrar a proporção cada vez menor de scripts existentes, nós _pudemos_ considerar a lógica proposta por @ ExE-Boss , mas não acho que queremos introduzir essa complexidade conceitual - muito menos complexidade de implementação (que eu realmente não posso falar).

Todos 53 comentários

Para esclarecer, o marco significa apenas que é algo que a equipe deve discutir com base no feedback, não que estejamos comprometidos em fazer essa mudança.

@ mklement0

? nunca deveria ter sido permitido como parte de um identificador sem incluí-lo entre {...}.

Ummm - a linguagem base inicial para PowerShell era o Posix Shell que usa $? para o código de saída qed '?' é permitido em nomes de variáveis ​​sem aspas.

não pode (significativamente) ser executado em versões mais antigas

Claro que pode:

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

Claro que pode:

Ele não pode ser executado de forma significativa _com a nova semântica_ (acesso nulo-condicional) - isso é tudo que eu quis dizer.

Sim, o seu exemplo atualmente funciona, mas _com semântica diferente_ ( abc? _incluindo ? _ sendo considerado o nome da variável) - mas pelas razões declaradas, vale a pena abandonar este caso esquivo por uma questão de ?. trabalhe conforme o esperado.

A linguagem base inicial para PowerShell era o Posix Shell que usa $? para o código de saída

$? é uma _exceção_ em shells do tipo POSIX; você _não_ pode fazer o seguinte:

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

(E, obviamente, ninguém está pedindo que $? seja abolido no PowerShell.)

E, obviamente, deve ser mencionado que _todas as outras_ variáveis ​​automáticas no mesmo espaço que $? (ou seja, $^ e $$ ) são explicitamente especiais- embalado no tokenizer. E, de fato, $? , embora (atualmente) não precise ser.

Parece claro para mim que era esperado no momento em que o código foi escrito que ? não seria um caractere identificador válido.

Só conheço uma pessoa que usou ? no final dos nomes de variáveis ​​em seus scripts, @StartAutomating. Como ele realmente usou essa sintaxe, achei que valeria a pena chamar sua atenção para esta discussão.

Também @ vexx32 , o motivo pelo qual $? tem uma caixa especial no tokenizer é porque você não pode criar uma variável com um nome que comece com ? . por exemplo, $??? = 'foo' não analisará. Os nomes das variáveis ​​devem começar com caracteres alfanuméricos ou um sublinhado e, atualmente, podem conter caracteres alfanuméricos, sublinhados ou pontos de interrogação. Estou de acordo que ?. deve ser analisado como o operador de acesso de membro condicional nulo, mas gostaria de esclarecer por que ? é especial no tokenizer.

Obrigado por chamar minha atenção para este chat.

Se vale a pena, tenho algumas idéias:

  1. Embora essas variáveis ​​sejam raras, ninguém quer brincar de whack-a-mole quando o back-compat quebra seus scripts
  2. Pessoal profundamente geek à parte, eu esperaria que $ {obj} ?. Method () ou $ obj? .Method () receba um pequeno aumento. Há uma década de orientação na web sobre como gerenciar nulos por aí, e não vejo muitas pessoas mudando de if ($ obj) {$ obj.Method ()} _apenas para entrar no movimento v7_
  3. Além disso, a maioria das pessoas que têm problemas com nomes de variáveis ​​inesperados aprendem a sintaxe $ {} (você já teria acertado naturalmente com um sublinhado). Eu esperaria que a sobreposição do diagrama de Venn de "pessoas que desejam usar este recurso" para "pessoas que já conhecem a sintaxe $ {}" seja quase 100%
  4. Uma vez que a referida sintaxe não é tão poderosa quanto uma subexpressão completa, e as subexpressões completas funcionam tanto no núcleo quanto não, eu espero que a maioria das pessoas continue a usar isso daqui para frente.

Portanto, executando a matemática, existem várias razões concretas para _não_ fazer isso, e apenas uma vaga razão para fazer isso (_pode_ ser mais fácil, para alguns usuários). Dada a baixa prevalência de? em nomes de variáveis, você _pode_ estar ajudando mais pessoas do que prejudicando, mas na minha opinião é um cara-ou-coroa.

Dado o potencial de benefício da moeda ao ar, vou parafrasear Robert McNamara "Se formos malditos se fizermos e malditos se não fizermos, vou escolher malditos se não fizermos".

Eu sugiro fazer isso de forma que $obj?.Method() funcione como $<var i="6">obj</var> ?. <var i="9">Method</var>() por padrão, e apenas volte para $<var i="11">obj?</var> . <var i="14">Method</var>() quando $obj não existe no escopo, mas $obj? sim e #requires ‑Version não é v7 +.

@ ExE-Boss, isso parece uma série de exceções a esta regra em particular.

Repito meu questionamento sobre o valor desta regra particular.

Depois disso, a questão é "o que mais não sabemos sobre a sintaxe da variável que podemos quebrar?"

Se o consenso for forçar a sintaxe ${...} para isso, o suporte para OptionalFeatures seria apreciado aqui, para que pessoas como eu, que usarão essa sintaxe (eu a usarei _muito_), possam cancelar o ruído .

Já que estamos parafraseando aqui:
"A lógica dita claramente que as necessidades de muitos superam as necessidades de poucos." - Spock

Portanto, executando a matemática, existem várias razões concretas para _não_ fazer isso, e apenas uma vaga razão para fazer isso (_pode_ ser mais fácil, para alguns usuários).

@StartAutomating : Existem vários motivos e são concretos, não vagos. Além disso, um dos objetivos disso é a simplicidade do código. Ter que agrupar nomes de variáveis ​​com {} funciona diretamente contra esse objetivo.

Estou com @ mklement0 e @KirkMunro nisso. Pessoalmente, considero a sintaxe ${...}? complexa e não natural. Já tivemos mudanças significativas e, neste caso, é um preço pequeno a pagar em troca de clareza.

@StartAutomating

  1. Além disso, a maioria das pessoas que têm problemas com nomes de variáveis ​​inesperados aprendem a sintaxe $ {} ( você já teria acertado naturalmente com um sublinhado ). Eu esperaria que a sobreposição do diagrama de Venn de "pessoas que desejam usar este recurso" para "pessoas que já conhecem a sintaxe $ {}" seja quase 100%

Isso não se sustenta. Os sublinhados são caracteres válidos em nomes de variáveis.

@ vexx32

Parece claro para mim que era esperado no momento em que o código foi escrito

Não.

Trocar os caracteres? $foo.?Property parece não conflitante, porque nomes de propriedades começando com pontos de interrogação já precisam ser citados:

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

@HumanEquivalentUnit , infelizmente, isso ?. usada em C # e várias outras linguagens (também, você precisaria encontrar uma forma alternativa para o indexador ( ${arr}?[0] ) também, e algo como $arr[0]? poderia causar confusão com o operador ternário)

Não existe uma solução legal.

  1. Um número desconhecido de pessoas usa? no final de nomes de variáveis. Embora seja um recurso opcional, pode-se dizer "Não ative se você executar esses scripts", mas a pessoa que o ativar deve verificar os scripts atuais e futuros obtidos de qualquer fonte.
  2. A sintaxe alternativa não é uma ótima opção porque importa algo não muito poderoso do C #
  3. Alterar o comportamento com base em #requires não funciona fora de um script salvo e falhará assim que alguém copiar algum código de um script longo sem definir a tag.
    4. (dir *.ps2)?.count e (dir *.ps1)?.count mostram que seu operando não precisa ser uma variável.
    ($Host)?.toString() / ($null)?.toString() elimina a necessidade de colocar colchetes ao redor do nome. Ainda são pressionamentos de tecla extras, mas é mais lógico e menos feio.
  1. Alterar o comportamento com base em #requires não funciona fora de um script salvo e falhará assim que alguém copiar algum código de um script longo sem definir a tag.

Isso se aplica a _qualquer_ código que usa recursos não suportados em versões anteriores.
Se essa fosse a preocupação principal, nenhum novo recurso de linguagem ou novo parâmetro cmdlet / cmdlet seria introduzido.

(Esperançosamente, no código mais antigo você tem pelo menos Set-StrictMode -Version 1 em vigor, caso em que $var?.foo falharia muito, devido a nenhuma variável chamada var? existente).

  1. elimina a necessidade de colocar colchetes ao redor do nome.

Substituir (...) por {...} é apenas uma melhoria marginal.

Qualquer que seja a sintaxe extra usada, o problema aqui é a _necessidade_ de sintaxe extra - para algo que deve funcionar _como está_ - não apenas para conveniência de digitação, mas também para atender às _expectativas razoáveis_.

  1. Alterar o comportamento com base em #requires não funciona fora de um script salvo e falhará assim que alguém copiar algum código de um script longo sem definir a tag.

Isso se aplica a _qualquer_ código que usa recursos não suportados em versões anteriores.
Se essa fosse a preocupação principal, nenhum novo recurso de linguagem ou novo parâmetro cmdlet / cmdlet seria introduzido.

Não foi bem isso que eu quis dizer. Mais acima, houve a sugestão de que se #requer o pwsh 7 especificado, o processamento pode ser diferente. Iluminar um novo comportamento apenas se você colocar #requiresnão é bom.

(Esperançosamente, no código mais antigo você tem pelo menos Set-StrictMode -Version 1 em vigor, caso em que $var?.foo falharia muito, devido a nenhuma variável chamada var? existente).

Na minha experiência, não usa muito Set-Strictmode. Porque (a) existe uma suposição de que o padrão sem ele é "certo" e (b) a frequência de uso de coisas que dependem de estar desativado é muito alta. É um uso comum para esses operadores. Em qualquer caso, o problema é um código mais antigo, onde $ var? existe e tem uma propriedade foo, mas agora o código procura $ var ... Possivelmente o novo processamento causaria um erro IF strictmode está definido porque $ var não existe ....

  1. elimina a necessidade de colocar colchetes ao redor do nome.

Substituir (...) por {...} é apenas uma melhoria marginal.

sim. ($ x) é ligeiramente melhor do que $ {x}, mas apenas ligeiramente. ( Get-user "Bob")?.disable() é melhor do que

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

Qualquer que seja a sintaxe extra usada, o problema aqui é a _necessidade_ de sintaxe extra - para algo que deve funcionar _como está_ - não apenas para conveniência de digitação, mas também para atender às _expectativas razoáveis_.

sim.
$var. foo Com trabalhos de espaço em branco. ${foo} ?.bar() não porque? é permitido em um nome de função ou alias (?. é válido para ambos). Amarrar partes da sintaxe C # nas regras de sintaxe existentes sem quebrar as coisas nem sempre é prático Às vezes, "Isso está disponível em C # ..." realmente merece a resposta "Sim. Você sabe onde está o C #, se quiser".
:-)

Para manter a compatibilidade do SemVer , isso deve ser feito em uma versão principal (de preferência 7.0.0 ).

Veja também minha sugestão acima ( https://github.com/PowerShell/PowerShell/issues/11379#issuecomment-566756120 ).

@ ExE-Boss Sim, era ao seu comentário que eu estava me referindo quando disse que depender de #requires pode não ser a boa ideia que pareceu a princípio.
E sim, se for para quebrar qualquer script existente, a hora de fazer é quando a versão principal mudar, então isso precisa ser feito rapidamente ou esperar pelo V8

Esses ainda são um recurso experimental e estão desativados por padrão no RC-1, que presumo que permanecerá o mesmo para um produto de remessa. Isso permite que o comportamento seja alterado (ou até mesmo ser um segundo recurso experimental) - ele também deve bloquear / avisar ao tentar criar uma variável que termina com certos caracteres. Se eu habilitá-lo e depois quiser usar scripts com nomes de variáveis ​​errados, isso coloca a responsabilidade sobre mim - é claro que isso significa que os scripts que eu baixei e que fazem isso parecem "quebrados", o que é mais tolerável sob o banner "experimental".

Não sou um desenvolvedor PowerShell sério; Vim aqui em busca de uma solução para um bug e me deparei com esse problema. Para scripts de linha de comando, eu uso principalmente o bash. Provavelmente estou no grupo demográfico que a Microsoft está tentando convencer a mudar para o PowerShell Core: desenvolvedor de plataforma cruzada que precisa de uma linguagem de script rápida que seja menos chata do que bash.

Freqüentemente, descobri que muitas das decisões relacionadas à sintaxe do PowerShell não são intuitivas para pessoas que não são principalmente desenvolvedores do PowerShell. A última vez que usei o PowerShell para algo sério, fiquei chocado com a falta de um operador de coalescência nulo, ou mesmo um operador ternário que eu pudesse usar no lugar - o que resultou em eu postar esta resposta popular do PowerShell no Stack Overflow . Isso foi em 2013 e é facilmente um dos três principais motivos pelos quais evitei o PowerShell desde então. O ponto principal de uma linguagem de script é que eu quero algo rápido e sujo com muito açúcar de sintaxe, e não declarações if-else prolixas.

@StartAutomating disse:

Pessoal profundamente geek à parte, eu esperaria que $ {obj} ?. Method () ou $ obj? .Method () receba um pequeno aumento. Há uma década de orientação na web sobre como gerenciar nulos por aí, e não vejo muitas pessoas mudando de if ($ obj) {$ obj.Method ()} apenas para entrar no movimento v7

Eu sou apenas n = 1, mas como um desenvolvedor não PowerShell, discordo totalmente. O novo ? A sintaxe é algo que eu descobriria muito rapidamente, mesmo como alguém que usa o PowerShell talvez uma vez a cada poucos meses, e isso definitivamente aumentaria as chances de eu usar o PowerShell no futuro no lugar de alternativas. Se eu perceber que alguma sintaxe {} maluca é necessária para compatibilidade com versões anteriores, vou revirar os olhos e voltar para o que já estava usando.

Não entendo a ênfase na compatibilidade com versões anteriores aqui. O PowerShell Core já quebrou a maioria dos meus scripts existentes porque eles usaram partes do .NET que não estão no .NET Core. (Não estou reclamando disso - o .NET Core é ótimo, mas se você vai quebrar a compatibilidade com versões anteriores, agora é sua chance.)

Em primeiro lugar, acredito que meu feedback inicial criou uma confusão de mal-entendidos.

Minha suposição era que $ {} não seria _tão estranho para os desenvolvedores do PowerShell_, porque eles já teriam que conhecer essa forma de sintaxe se já tivessem incorporado uma variável com um? ou e sublinhado em uma string.

Obrigado pelos muitos lembretes de que essa sintaxe pode ser estranha para desenvolvedores não PowerShell.

A segunda parte do meu feedback pode ter se perdido na mistura: Como outras alternativas à coalescência de nulos existem há mais tempo no PowerShell e são amplamente usadas, não sei quanto aumento o recurso teria _com os usuários existentes do PowerShell_. Você quer entrar e dizer "esta é a única coisa que preciso para começar a usar o PowerShell" e não posso contestar sua opinião (embora possa dizer que está faltando uma floresta de recursos interessantes em busca de uma árvore da linguagem familiaridade).

A terceira parte do meu feedback parece ter saído de proporção. Falando como uma das pessoas que ocasionalmente usou variáveis ​​nomeadas como $ IsThisRight? = Test-Something, minha primeira reação ao ouvir notícias em potencial sobre essa restrição de variável foi "ah, bem, acho que terei que renomear algumas variáveis, não estava conseguindo muito por isso de qualquer maneira."

Portanto, para tentar reformular e encerrar a discussão:

  1. IMHO, $ {} provavelmente não será uma sintaxe tão estranha para _existing_ desenvolvedores do PowerShell
  2. Você pode achar que esse recurso é legal; para mim, é uma droga de portal de familiaridade com a linguagem, e eu recomendo fortemente que todos que estejam lendo este tópico aprendam como definir ifs / foreaches / whiles etc.
  3. Como um dos autores impactados, não estou entendendo algo tão crítico de missão de um? no final das minhas variáveis, eu me oporia a mudar meu código.

Ou, para ser muito curto:

Faça esta mudança se quiser. Eu tenho um pequeno cavalo nesta corrida. Se você decidir fazer isso, transmita a mudança claramente para que qualquer outra pessoa que tenha nomeado variáveis ​​com um? pode atualizar em conformidade.

A segunda parte do meu feedback pode ter se perdido na mistura: Como outras alternativas à coalescência de nulos existem há mais tempo no PowerShell e são amplamente usadas, não sei quanto aumento o recurso teria com os usuários existentes do PowerShell.

Eu definitivamente concordo com isso e não quero desprezar sua opinião. É sempre difícil fazer mudanças bruscas como essa, especialmente quando a alternativa pode ser intuitiva para pessoas que já são grandes usuários da linguagem.

Você quer entrar e dizer "esta é a única coisa que preciso para começar a usar o PowerShell" e não posso contestar sua opinião (embora possa dizer que está faltando uma floresta de recursos interessantes em busca de uma árvore da linguagem familiaridade).

Bem, eu tive a sensação de que a maioria dos desenvolvedores não PowerShell não estão realmente participando dessas conversas - é pura coincidência que eu tropecei nisso. Meu uso do PowerShell provavelmente será muito diferente das pessoas que estão escrevendo bibliotecas e ferramentas de código aberto. Achei que deveria apenas dar a perspectiva de alguém que está usando o PowerShell menos como uma estrutura sofisticada e mais para realizar tarefas estranhas o mais rápido possível. Não tem a intenção de ignorar nenhuma outra opinião - é apenas outra opinião de uma perspectiva que provavelmente não é vista com tanta frequência aqui.

Melhor tratamento de nulos não é a única coisa que preciso para começar a usar mais o PowerShell: é a segunda coisa. O primeiro foi o suporte multiplataforma, e estou muito impressionado com o quão longe isso aconteceu. Pessoalmente, a sintaxe de açúcar é um grande negócio para mim quando estou escolhendo uma linguagem para fazer uma determinada tarefa: quero que a sintaxe torne o que estou fazendo mais fácil. Se estou escrevendo algo que precisa ser mantido por muitas pessoas, provavelmente vou querer que seja mais prolixo; mas se estou escrevendo um script rápido para tarefas administrativas ou de desenvolvimento, só quero uma maneira rápida de testar coisas como null.

A terceira parte do meu feedback parece ter saído de proporção. Falando como uma das pessoas que ocasionalmente usou variáveis ​​nomeadas como $ IsThisRight? = Test-Something, minha primeira reação ao ouvir notícias em potencial sobre essa restrição de variável foi "ah, bem, acho que terei que renomear algumas variáveis, não estava conseguindo muito por isso de qualquer maneira."

Favorecer a compatibilidade com versões anteriores é sempre um argumento justo. No entanto, neste caso específico, parece que muitos scripts do PowerShell estão prestes a quebrar devido à transição para o Core de qualquer maneira. Quando a compatibilidade com versões anteriores é interrompida regularmente, é frustrante, mas eu seria a favor de que quaisquer alterações significativas ocorressem de uma vez, o que significa que provavelmente este é um bom momento.

Em um cenário no qual você está tentando atingir um novo grupo demográfico - ou seja, desenvolvedores não centrados no Windows - pode ser necessário fazer algumas alterações significativas para competir com o que já existe.

Se você decidir fazer isso, transmita a mudança claramente para que qualquer outra pessoa que tenha nomeado variáveis ​​com um? pode atualizar em conformidade.

Para ser claro, estou bem com a necessidade de ativar o recurso - isso não me impediria de usar o PowerShell. Já estou acostumado a precisar de um monte de set comandos no topo de meus scripts bash. Eu não o consideraria ideal, mas teria pouco impacto na minha decisão de usar o PowerShell, desde que seja bem documentado.

Pessoalmente, a sintaxe de açúcar é muito importante para mim quando estou escolhendo uma linguagem para fazer uma determinada tarefa

Então você veio ao idioma certo! PowerShell é positivamente saccrino com açúcar sintático.

Para educar aqueles que não sabem, você pode "null coalese" de várias maneiras no PowerShell:
~$ ListWhereNotNull = $ null, 1, 2, $ null, 3 -ne $ null # Isso verificará se cada item não é nulo e retornará uma lista de 1,2,3$ FirstNonNull = @ ($ null, 1, $ null, 2 -ne $ null) [0] # Isso retornará o primeiro não nulo$ lastNonNull = @ ($ null, 1, 2, $ null -ne $ null) [- 1] # Isso retornará o último não nulo, usando um índice negativo$ TheObjectPipelineWay = $ null, 1, $ null, 2 | Select-Object -First 1$ TheAssigningForeachWay = foreach ($ item in $ null, 1, 2, $ null, 3, 4) {if ($ item -ne $ null) {$ item;



Isso é o suficiente por agora, mas deve provar que a sintaxe da linguagem tem muita flexibilidade. Todas essas demonstrações funcionarão desde o PowerShell 1.0

No entanto, neste caso específico, parece que muitos scripts do PowerShell estão prestes a quebrar devido à transição para o Core de qualquer maneira.

Nisto você está incorreto. O Core, na verdade, tem muito poucas mudanças de quebra de sintaxe e mais módulos do que você acha que funcionam bem no core (+ - alguns problemas com Linux vs Windows pathing / newlines). Em termos mais práticos, é menos provável que seu módulo funcione no Core se você tiver algum tipo de dependência de API específica da plataforma Windows (por exemplo, não vou prender a respiração para fazer meus wrappers WPF funcionarem no Linux).

Ainda estou bem com a mudança de sintaxe: acabei de sentir que duas das afirmações na resposta mais recente precisavam ser abordadas e evocadas.

Re: Açúcar sintático, minha mente sarcástica cita o filme "Snatch": "Já sou doce o suficiente"
Re: quebrando potenciais de mudança no Core: temos poucos agora, e vamos mantê-lo assim o máximo que pudermos.

@StartAutomating : Você está dizendo que eu não posso atirar? porque ainda há lugar para mais açúcar, eu acho 😜

Para educar aqueles que não sabem, você pode "null coalese" de várias maneiras no PowerShell:

Eu descobri isso (essa é a minha própria resposta que escrevi), mas parecia um erro. É uma ideia legal com muito potencial, mas como um comentarista sobre a resposta do Stack Overflow apontou, a avaliação não causa curto-circuito. O operador ?. significa que isso geralmente não é mais um problema ao encadear chamadas / propriedades. Em 2013, porém, ele não estava disponível - e, mesmo assim, há muita coisa acontecendo nesses exemplos, e você pode ver nos comentários como as pessoas acharam isso confuso. Tive que decompor exatamente o que esses exemplos fazem, símbolo por símbolo.

Nisto você está incorreto. O Core, na verdade, tem muito poucas mudanças de quebra de sintaxe e mais módulos do que você acha que funcionam bem no core (+ - alguns problemas com Linux vs Windows pathing / newlines). Em termos mais práticos, é menos provável que seu módulo funcione no Core se você tiver algum tipo de dependência de API específica da plataforma Windows (por exemplo, não vou prender a respiração para fazer meus wrappers WPF funcionarem no Linux).

Estou apenas falando por experiência própria a esse respeito, em vez de realmente analisar e coletar estatísticas adequadas. Por exemplo, pré-Core, eu poderia gerar uma senha com [System.Web.Security.Membership]::GeneratePassword(32, 6) , mas System.Web não está disponível no Core. (Sei que provavelmente poderia carregar o antigo System.Web.dll, mas prefiro não fazer isso se não for necessário.) As alterações de sintaxe são, na verdade, mais fáceis de resolver do que APIs ausentes. Gerar vários segredos do Docker para um devkit parece um ótimo lugar para usar um script rápido, mas é surpreendentemente mais detalhado no PowerShell Core do que no bash. Claro, as soluções do bash geralmente parecem muito feias.

Re: Açúcar sintático, minha mente sarcástica cita o filme "Snatch": "Já sou doce o suficiente"

Depende. Às vezes, sinto que o PowerShell é extraordinariamente agradável. Lidar com null é uma daquelas áreas-chave em que geralmente não me sinto assim - embora diria o mesmo para muitas linguagens. Também considero a instrução if-else tradicional do PowerShell embutido ser complicada em comparação com um operador ternário ou o encadeamento de && e || do bash. Como esse é um dos principais problemas que me fizeram afastar-me do PowerShell anos atrás, achei que valia a pena mencioná-lo.

Por outro lado, lido com uma grande variedade de idiomas diariamente e, independentemente do que escrevo, estou sempre pensando: "isso seria muito mais fácil na língua X!" - e então, quando Estou usando a linguagem X, estou pensando, "isso seria muito mais fácil na linguagem Y!" Mas, ei, se eu tiver a oportunidade de dizer isso em voz alta e talvez influenciar a direção da linguagem, vou aproveitá-la.

Não estou realmente interessado em mudar de opinião aqui, apenas adicionando outra perspectiva.

@StartAutomating : Você está dizendo que eu não posso atirar? porque ainda há lugar para mais açúcar, eu acho 😜

Eu sempre adoro alguns novos açúcares de sintaxe! Eu seleciono linguagens de programação como uma criança fazendo doces ou travessuras no Halloween.

@Zenexer : Gosto muito do Powershell . Tenho a sorte de fazer PS extensiva e diariamente no trabalho. Eu amo a flexibilidade, especialmente a simbiose com .NET, oh cara! É por isso que me preocupo com sua direção e é por isso que a sintaxe ${...}? é tão difícil de aceitar para mim.

Boas observações, mas deixe-me esclarecer algumas coisas.

Para o bem ou para o mal, o PowerShell até o momento não usou o controle de versão semântico.
Uma nova versão principal trouxe apenas _novos_ recursos, com um compromisso inflexível com a compatibilidade com versões anteriores.
Mudanças violentas começaram a surgir no Core, em parte por necessidade devido ao uso de plataforma cruzada.
A versão principal v7.0 foi escolhida para sinalizar um _aumento_ na compatibilidade com versões anteriores: uma tentativa de fornecer uma substituição viável para o Windows PowerShell.

Embora muitos problemas históricos não possam ser corrigidos sem quebrar seriamente o código existente e, portanto, só possam ser oferecidos como recursos opcionais (opt-in),
mudanças de quebra de intervalo 3 devem sempre ser uma opção: fazer uma correção / melhoria que tem o _potencial_ para quebrar o código existente, mas é improvável na prática; no balanço, vale a pena fazer para melhorar o idioma.

Conforme argumentado no OP, o caso em questão me parece uma mudança de balde 3; embora saibamos que _alguns_ scripts irão quebrar ( @StartAutomating saberá como consertar seus scripts), os dados sugerem que tais casos são bastante raros.

Por que é raro?

Porque o uso de ? em nomes de variáveis ​​não ocorre para a maioria das pessoas, e se acontecer, eles não devem esperar que funcione _sem {...} _: Se todos os itens a seguir funcionarem apenas com {...} , por que esperaria que fosse diferente para ? ?: . , ; ( : não pode ser usado de forma alguma, porque é sempre interpretado como um separador de unidade)

Permitir nomes como $var? ou $va?r sem também exigir {...} era uma permissividade que pedia problemas, e o problema chegou: atrapalha a evolução do linguagem, como neste caso.

Existem 3 novos recursos relacionados (pelo menos relacionados na forma sintática) inspirados em C #:

  • Condicionais ternários - será um recurso de boa-fé no 7.0

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

  • Operador de coalescência nula - será um recurso genuíno em 7.0

    • $var ?? 'default' e $var ??= 'default'

  • Operador nulo-condicional - o problema em questão - será um recurso _experimental_ no 7.0

    • $possiblyNull?.ToString()

Todos eles têm ? como parte de sua sintaxe, e todos eles são afetados pelo fardo da análise $var? legada, embora ?. mais obviamente:

  • Embora você possa argumentar que algo como $var?1:0 só tem interesse para codificar jogadores de golfe, funcionaria muito bem se ? tivesse função sintática - atualmente você deve usar um _space_ antes de ?

  • Por outro lado, me parece razoável alguém tentar $var??='bar' (talvez menos $baz = $foo??$bar ), que atualmente também requer um espaço.

Ou seja, se ficarmos com a análise atual de $var? , estaremos sobrecarregando _três_ novos recursos (embora em graus variáveis), 2 dos quais já estão geralmente disponíveis (não experimentais).

O problema de exigir {...} _não_ é apenas o inconveniente da digitação: trata-se apenas de _não esperar a necessidade_ em primeiro lugar e, posteriormente, _esquecer a necessidade_ (com mau funcionamento potencialmente sutil) - porque é tão contra-intuitivo.

@ ExE-Boss, concordo com @jhoneill que implementar o comportamento condicional de versão seria problemático. Não acho que haja necessidade de sobrecarregar o motor com uma caixa especial de compatibilidade com versões anteriores neste caso.

@ mklement0 : Não é natural / contra-intuitivo / você-nome-isso. Os colchetes são duros, os colchetes ainda mais difíceis. Não há necessidade deles neste caso.

@ mklement0 já que o acordo parece estar saindo ... acho que é um bom resumo.

Ainda estou para ser completamente conquistado adicionando construções C # ao PowerShell. Já temos problemas em muitos lugares em que simplesmente não há símbolos suficientes no teclado. É ">" "Maior que" ou "redirecionar para"? É "=" atribuição ou igualdade de teste (diferentes idiomas usam: = para atribuir ou == para igualdade). Pessoas pulando entre idiomas vão esquecer de usar -eq ou -gt e estamos presos a isso ... É + adição aritmética, ou concatenação de string / array. * é multiplicação e um curinga. % é Módulo e um alias para forEach-object.

Pobre ? É o apelido de Where-Object. E é o nome de uma variável automática. Mas tente fazer Get-Alias ? ou Get-Variable ? ... e então fará o trabalho de um curinga.

Faça Get-PSdrive e você verá "variável" etc não tem um ":" no final do nome, adicionando um: em um nome faz com que ele seja tratado como uma unidade. A menos, é claro, que você indique ter escrito "$drive="C" e seguido com "$drive:temp" Qualquer pessoa nova no PowerShell (e alguns veteranos, quando não estão pensando nisso) ficará perplexo ao saber que ' acabamos de inventar um escopo chamado "drive".

Portanto, contra isso ... pode-se dizer 'Crie uma nova sintaxe com outros usos para ":" e "?" você é louco?'
IIRC, no twitter @BrucePay descreveu a operadora ternária como "Não muito PowerShelly" e tendo a concordar. Mas as vozes "Torne-o mais parecido com C #" ganharam o dia ...

A maioria das pessoas escreveria $PSEdition.length -gt 2
mas $PSEdition. length-gt2 também funciona (você pode até ter uma quebra de linha entre as duas metades.) "." também está sobrecarregado servindo como "diretório atual", "executar neste escopo" e "membro". No entanto, no PS V1-V5 não consigo pensar em nenhum operador que requeira um espaço inicial; "." é estranho em quebrar se um _está_ presente. Mas, para analisar, o operador ternário _pode_ precisar de um espaço inicial
$host.debuggerEnabled?0:1 está OK porque "?" assumido _não_ como parte de uma propriedade
$IsCoreCLR?0:1 não é porque "?" é considerado parte do nome da variável.

O mesmo é verdadeiro para os operadores de coalescência nulos
$null?? "default" precisa de um espaço $true.length?? "default" não.

Coloque o caso de uso original no topo da questão de lado por um momento. Os casos acima mostram que algo não está funcionando bem. Seria corrigido se os nomes das variáveis ​​contivessem "?" precisava ser travado como nomes contendo "." ou "+" ou ":" e @StartAutomating quem é o usuário principal de "?" em nomes pensa que é uma _quebra que pode ser tratada_. Antes mesmo de chegarmos aos membros nulos, soa como 'encontre uma maneira de fazer isso já'

Eu acho que há um curto espaço de tempo para colocar em um recurso experimental que exige? para conter nomes, e se esse recurso estiver LIGADO - qual deveria ser o padrão - tudo novo? os operadores devem analisar em conformidade. Somente se esse recurso estiver DESLIGADO, o analisador requer espaços ou colchetes com esses operadores. Essa é uma alteração importante, alguns scripts (um pequeno número não quantificável) seriam quebrados a menos que uma opção fosse definida (e não permitir o nome da variável deve evitar $ IsIt ? .tostring () sendo o primeiro erro em um script, portanto, os scripts devem falhar, não devem ser executados de forma perigosa). e, idealmente, isso não deveria acontecer com um lançamento pontual (você está correto sobre o PowerShell não usar realmente sem-ver, mas ainda é um bom princípio)

O problema de exigir {...} não é apenas o inconveniente de digitar: é muito mais sobre não esperar a necessidade dele em primeiro lugar, e depois esquecer a necessidade dele (com mau funcionamento potencialmente sutil) - porque ele tão contra-intuitivo.

Este é um bom ponto e, em grande parte, é o que me irritaria ${...?} na prática. Mesmo que eu saiba usar {} , há uma grande chance de esquecer, e isso pode resultar em bugs sutis que não são óbvios a princípio, já que provavelmente ainda será sintaticamente válido. $var??='bar' é um bom exemplo disso, embora normalmente eu seja muito cuidadoso com o espaçamento.

Obrigado, @jhoneill , essa é uma bela ilustração de quantos símbolos realizam muitas tarefas duplas (triplas, ...) no PowerShell.
Na maioria das vezes, uma vez que você esteja ciente de todos os usos, isso não será um problema, pelos seguintes motivos:

  • contextos diferentes (por exemplo, * como um operador vs. * como um caractere curinga.)

    • Os diferentes contextos para ? realmente têm algo em comum, em um certo nível de abstração: você pode conceber o ? em Get-ChildItem | ? Target , $var?.Target , $? , $var ?? 'none' como todos envolvendo um tipo de _question_ e, portanto, _teste_ ou _comportamento condicional_.

  • comportamento polimórfico sensível (por exemplo, + realizando concatenação com string).

Um exemplo problemático é & (operador de chamada vs. operador de fundo), que é usado no mesmo contexto com semânticas diferentes, dependendo apenas de sua _posição_.

Seria corrigido se os nomes das variáveis ​​contivessem "?" precisava ser travado como nomes contendo "." ou "+" ou ":"

Na verdade, e vale a pena repetir: a capacidade de usar ? em nomes de variáveis ​​não desaparecerá - mas você terá que usar {...} daqui para frente: ${var?} = @{ foo=1 }; ${var?}?.foo

Como você demonstra, da perspectiva de alguém sensivelmente _não_ esperando que ? seja um caractere válido. . para dot-sourcing sendo a exceção justificável; ? como Where-Object alias requerendo um espaço depois pode ser surpreendente (por exemplo, gci|?target não funciona), mas os nomes de comando _do_ precisam ser separados de seus argumentos com um espaço em branco).

Ser capaz de colocar um espaço em branco entre . e um nome de membro ( $obj. foo ) faz sentido principalmente em instruções de várias linhas, embora a forma indiscutivelmente mais legível - familiar de outras linguagens - seja (também) permitir espaços em branco _antes_ de . :

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

Ser capaz de fazer isso refletiria a capacidade recentemente adicionada de colocar | na linha _próxima_:

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

No entanto, isso introduziria ambigüidades insolúveis com . como o operador dot-sourcing e nomes de comando que começam com . - embora improvável. (por exemplo, . foo em uma nova linha pode ser um acesso de propriedade ou um comando que origina pontos de uma função ou script denominado foo (que deveria estar em $env:PATH ) ; .foo pode ser o nome de um _comando_).

Multi linha de continuação RFC 's @KirkMunro, enquanto focada em permitindo _commands_ para abranger várias linhas, nos daria a próxima melhor solução - ver https://github.com/PowerShell/PowerShell-RFC/pull/179#issuecomment -498734875.

Quanto ao recurso experimental sendo ativada por padrão: Felizmente, _preview_ lançamentos - mas não liberar candidatos - agora voltar _todos_ recursos experimentais por padrão; entretanto, note que isto é persistentemente substituído se você já executou Enable-ExperimentalFeature _without_ -Scope ou com -Scope CurrentUser com quaisquer recursos que você habilitou lá.

@ mklement0 Embora eu concorde basicamente com tudo isso, temo que estejamos indo para descarrilar esta discussão em particular um pouco com os pedaços extras que você tem lá; se você deseja abordar a continuação de linha em torno do operador dot-property, pode desejar abrir um novo problema para isso, se ainda não houver um. 🙂

Eu concordo que continuar permitindo ? como um caractere de nome de variável normal é, na melhor das hipóteses, extremamente confuso para os usuários, já que o uso do caractere repentinamente torna as coisas sensíveis a espaços em branco e, historicamente, pelo menos, não acho que nomes de variáveis ​​tenham _ever_ era sensível a espaços em branco no passado.

Apresentar essa ambigüidade é, eu acho, mais um problema do que fazer uma alteração significativa e desautorizar ? de usar em um nome de variável. Com a alteração significativa, podemos construir facilmente uma regra PSSA para alertar os autores de que a sintaxe não é mais permitida. No entanto, o inverso não é tão facilmente verdadeiro e, ao contrário da alteração significativa, não necessariamente ocorrerá um erro; pode muito facilmente acabar com os usuários acidentalmente referenciando a variável errada silenciosamente, sem saber o que estão fazendo.

@ PowerShell / powershell-Committee discutiu isso, analisamos o corpus de scripts do PowerShell e havia bastante uso de nomes de variáveis ​​terminando com o ponto de interrogação. Como tal, não podemos quebrar essas pessoas e chaves em torno dos nomes de variáveis ​​do PowerShell é um recurso de linguagem existente para distinguir o nome da variável de outros caracteres (como em uma string).

analisamos o corpus de scripts do PowerShell e havia bastante uso de nomes de variáveis ​​terminando com o ponto de interrogação

Obrigado, @ SteveL-MSFT - você pode compartilhar os resultados desta análise?

Sim, eu ficaria curioso para ver um pouco disso, porque estou ciente de que alguns membros da comunidade fizeram suas próprias análises e não obtiveram nada perto desse tipo de resultado.

Também gostaria de ver os resultados e o método usado. Fiz minha própria análise do PowerShell Corpus ontem à noite usando o AST, que levou quase 6 horas para ser concluído na minha máquina. Havia um pequeno número de arquivos que não puderam ser analisados ​​devido à quarentena do arquivo pelo Windows Defender.

Encontrei 11 nomes de variáveis ​​exclusivos em 8 arquivos terminando em um ponto de interrogação (excluindo $? Que apareceu 8.846 vezes). Havia um total de 1.895.983, variáveis ​​exclusivas em 408.423 arquivos. Isso significa que 0,0006% de todas as variáveis ​​exclusivas do PowerShell no corpus tem um nome de variável terminando com um ponto de interrogação.

Aqui estão os 8 arquivos e 11 variáveis ​​exclusivas:

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

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

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

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

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

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

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

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

Este é o script usado para gerar o relatório:

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

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

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

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

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

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

        $output
    }
    catch {
        throw
    }
}

Informe se houver algum problema com o método usado para gerar este relatório. Aqui está um script de exemplo usado para testar a auditoria.

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

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

Resulta em:

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

Como se costuma dizer na aula de matemática, "mostre seu trabalho" 😄

Excelente investigação, @ThomasNieto - obrigado.

Eu acho que você pode potencialmente eliminar ainda mais casos, visto que o formulário ${...} continuará a funcionar com a mudança proposta - por exemplo, é apenas $isAwesome? que precisamos nos preocupar, não ${isAwesome?}

Com seu arquivo de exemplo:

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

nós só temos:

$isAwesome?
$isabc?
$adce?

Ou seja, ${def?} e ${is a bad var name?} não precisam ser considerados.

PS: Provavelmente também é melhor usar $PSItem | Get-Content -Raw para ler o script inteiro como uma única string.

@ mklement0 Correto. Eu adicionei esses casos logo antes de postar para garantir que o script não estava excluindo variáveis ​​com a notação de chaves todas juntas.

Depois de atualizar o script com suas recomendações, o arquivo de teste produz a seguinte saída como esperamos.

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

Eu executei o script atualizado nesses 8 arquivos e nenhum deles está usando a notação de chaves, então o resultado ainda é que 11 variáveis ​​únicas seriam impactadas com a mudança proposta.

Script atualizado:

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

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

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

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

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

        $output
    }
    catch {
        throw
    }
}

Obrigado, @ThomasNieto - Eu esperava que pudéssemos baixar o percentual para 0,0005% ... (brincadeirinha).

Portanto, o comitê deve ter usado um diferente - privado? - corpus.

Assim, além de decidir o assunto em questão, surge a pergunta: o corpus disponível ao

@ SteveL-MSFT, pode esclarecer?

@ mklement0 Eu arredondei, então se você quiser se sentir um pouco melhor, na verdade é 0,00058% 😄

Em https://github.com/PowerShell/PowerShell-RFC/pull/223#discussion_r318340339 @adityapatwardhan fiz uma análise usando o mesmo corpus público que usei e cheguei a 62% com variáveis ​​terminando com um ponto de interrogação. Não vi uma resposta sobre como ele chegou a essa porcentagem. O comitê de PS usou essa análise para determinar essa solicitação?

Olhando para o comentário vinculado, acho que @adityapatwardhan está dizendo que _entre todas as variáveis ​​que têm ? em seus nomes_ 62% têm um ? como o _último_ caractere do nome.

Por outro lado, sua análise da prevalência real de variáveis ​​(sem colchetes) terminando em ? (como uma porcentagem do uso de variável geral) parece muito mais relevante.

Nesse ponto, parece que o comitê está apenas dizendo "não queremos" e, se for esse o caso, por que ter esse recurso se ele vai ficar desajeitado e não usado como resultado?

@ThomasNieto da mesma forma, fiz uma análise em algum lugar em torno dessas questões e cheguei a resultados semelhantes aos seus.

@mburszley Obrigado por fazer sua análise. Eu queria usar outro método (ast vs regex) para confirmar os resultados que você obteve no ano passado.

Eu ouço você, @mburszley , mas re:

o comitê está apenas dizendo "não queremos"

Se esta fosse realmente uma decisão _fiat_ - uma não fundamentada na análise do uso no mundo real - não seria um bom presságio para a comunidade em geral, especialmente considerando que o motivo _determinado_ era uma base para tal análise.

Portanto, devemos dar ao comitê a chance de mostrar a análise de uso do mundo real na qual a decisão se baseou - ou de reconhecer que a análise foi insuficiente e reconsiderar a decisão.

E a minha sugestão em https://github.com/PowerShell/PowerShell/issues/11379#issuecomment-566756120 ?

Eu sugiro fazer isso de forma que $obj?.Method() funcione como $<var i="9">obj</var> ?. <var i="12">Method</var>() por padrão, e apenas volte para $<var i="14">obj?</var> . <var i="17">Method</var>() quando $<var i="19">obj</var> não existe no escopo, mas $<var i="21">obj?</var> sim e #requires ‑Version não é v7 +.

Eu esperaria que isso resolvesse a maioria das questões de compatibilidade com versões anteriores.

@ThomasNieto

@ mklement0 está correto, eu quis dizer que entre as variáveis ​​que usam ? , 62% usam no final.

A partir dos resultados mostrados na comentário aqui parece todas as variáveis que usam ? usá-lo no final. Daí a proposta de fazer uma mudança ininterrupta.

Se o requisito de {..} for removido, o script ainda analisará, mas a semântica será diferente, o que, em minha opinião, é muito perigoso.

Claro ... mas a análise mostra que literalmente quase ninguém os está usando.

Como qualquer outra alteração importante, ela pode ser anunciada, introduzida como um recurso experimental por um tempo e, em seguida, documentada e trazida à estabilidade.

Não vejo razão para recomendar uma sintaxe esotérica quando ? como um caractere identificador não está realmente sendo usado em um número remotamente significativo de casos.

Se o requisito para {..} for removido, o script ainda analisará, mas a semântica será diferente, o que, em minha opinião, é muito perigoso.

Você também não está preocupado com as pessoas usando ?. da maneira que fazem em C / C ++ / C # e obtendo o resultado errado sem nenhuma indicação de que fizeram algo errado? Por exemplo:

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

Claro, o modo estrito encontrará isso, mas não sei se muitas pessoas usam o modo estrito. Portanto, para evitar que uma porcentagem muito pequena de scripts seja interrompida, parece que estamos preparando um monte de gente para a falha usando ?. . Eu não sou louco por trade-off. Dito isso, eu definitivamente entendo o desejo de evitar quebrar pessoas, mesmo que seja um número muito pequeno de pessoas.

Talvez o PowerShell precise de um equivalente a Enable-ExperimentalFeature chame-o de Enable-PSFeature , para recursos que não estão mais no estado experimental, mas não estão prontos para serem ativados por padrão . Para scripts que executam com código como este $?.GetType() você pode começar a emitir avisos de que no futuro isso falhará e que você deve usar ${?}.GetType() lugar. Scripts individuais podem ativar o recurso com #requires -version 7.1 -feature PSNullConditionalOperators . Então, talvez sempre que o PS 8 chegar, o recurso pode ser ativado por padrão.

O exemplo de @rkeithhill vai direto ao ponto.
Esqueça por um momento o que você sabe sobre como o PowerShell funciona hoje. Quando você vê

PS> $res?.Length

Ele se parece com _nome, operador, nome_ - o que é claro.
O que faria mais sentido: _ "?" faz parte do operator_ ou _ "?" faz parte do primeiro nome-element_?
Se esses operadores são necessários (e eu acho que eles "não são PowerShelly"), é uma escolha entre algo que será usado incorretamente e uma pequena quebra.

Eu começaria a colocar regras em psScriptAnalyzer _agora_ para dizer que é um estilo ruim usar certos caracteres [legais] em nomes de variáveis.

Não gosto de Enable-feature . Alguém o coloca no script para fazer algo que deseja usar, e um script antigo quebra se executado após o novo script (mas funciona no resto do tempo). Eu vi _script de terceiros X falhar depois de executar o script Y_ porque Y ativa o modo estrito e X depende dele estar desativado; os autores de ambos os scripts estão em meus livros ruins.

Eu começaria a colocar regras em psScriptAnalyzer agora para dizer que é um estilo ruim usar certos caracteres [legais] em nomes de variáveis.

Aqui está o problema: se você alterar a maneira como o PowerShell simboliza as variáveis, o PSScriptAnalyzer também as verá de forma diferente. Se você tem uma variável como $res? , então olhando para algo assim:

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

da-te

res?

MAS, se você alterar a forma como o PowerShell é analisado em, digamos, 7.2, você obterá


Agora PSScriptAnalyzer não pode ver ? , porque ele não faz mais parte do nome da variável (por definição, já que é a alteração que estamos tentando testar). Isso ocorre porque PSScriptAnalyzer é um módulo do PowerShell e reutiliza o mesmo analisador que vem no PowerShell. (E seria totalmente insustentável e uma receita para o desastre se tentasse reimplementá-lo).

Existem maneiras de contornar isso, como compilar condicionalmente PSScriptAnalyzer (e enviar um novo assembly) para testar a nova lógica 7.2 para fornecer o mesmo diagnóstico. Mas então você aumentou a complexidade em todos os lugares (ou alternativamente diminuiu sua superfície de teste) para atender a um cenário um tanto cosmético.

Pior, todas as outras ferramentas que também dependem do AST não terão a capacidade de sustentar esse tipo de complexidade, mesmo que o PSScriptAnalyzer o faça. A maioria das pessoas que criam ferramentas de segmentação AST por aí não vai compilar em versões secundárias individuais do PowerShell para correção perfeita. Eles provavelmente nem saberão sobre uma alteração tão pequena, especialmente porque não é um erro em nenhuma versão; é a forma mais insidiosa de interromper a mudança, em que um comportamento silenciosamente se transforma em outro.

Isso é basicamente o que torna as alterações no nível do analisador / tokenizador como essa mais perigosas. Até mesmo PSScriptAnalyzer deve se dobrar para trás para atendê-los corretamente, o que significa que eles devem ser pesados ​​contra tais mudanças onerosas.

@rjmholt , embora eu ache que todos podemos apreciar como uma mudança como essa é complicada _em princípio_, neste caso específico não importa _na prática_, e eu não acho que uma regra PSSA seja sequer necessária:

  • A análise de @ThomasNieto mostrou que - se acreditarmos que o corpus representa todo o código do PowerShell por aí - que _virtualmente ninguém usa variáveis ​​com nomes que terminam em ? _.

  • Se eu fosse adivinhar, a maioria das pessoas nem mesmo _pensaria_ em usar esses nomes de variáveis, porque não esperariam que funcionassem (eu incluído) e provavelmente nem notariam a exceção de que o $? automático bash ).

    • Na verdade, olhando mais de perto a análise de @ThomasNieto revela que, entre os 8 arquivos listados:

      • 5 contém apenas _falsos positivos_, ou seja, variáveis ​​usa dentro de _strings_, como "Are you sure you want to kill $vParam?" - na verdade, esses usos são _interrompidos_ e revelam que os autores do script _não_ esperavam que ? fossem parte do nome da variável.

      • 2 desses arquivos não estão mais disponíveis publicamente.

      • Apenas _1_ deles é um uso genuíno de ? variáveis ​​finais - como variáveis ​​_Booleanas (por exemplo, $key1? ), que, à parte, são, portanto, _não_ combinadas com o operador de acesso de membro , . / cc @stevenayers.

Com base no que precede, isso me parece um livro didático Balde 3: Mudança

_Documentar_ a mudança de comportamento (com uma justificativa) deve ser suficiente.

Claro:

  1. isto é baseado em (a) a análise ser correta e (b) o corpus publicamente disponível ser representativo.

  2. está em total desacordo com a afirmação do comitê de que "havia bastante uso de nomes de variáveis ​​terminando com o ponto de interrogação" (o que, é claro, só seria um problema se esses nomes de variáveis ​​não estivessem entre {...} ).

Partindo do pressuposto de que 1. é verdadeiro (não ouvimos nada para apoiar 2.), temos duas opções:

  • Retire o band-aid e simplesmente desabilite ? em nomes de variáveis ​​daqui para frente, a menos que estejam incluídos em {...} (com a exceção óbvia de $? ) - isso quebrará o pequeno proporção de scripts que atualmente dependem disso.

    • Ou seja, algo como $key1? que não é seguido por . nem por outro ? ( $key1??'else' ) nem por uma expressão ternária ( $key1?1:0 ) causaria um _erro do analisador_.

      • Como mostram os exemplos de operador ternário e coalescência nula, eles também se beneficiariam com essa mudança - atualmente, você precisa de um _space_ - $key1 ?1:0 / $key1 ??'else' - ou {...} - ${key1}?1:0 / ${key1}??'else'

    • Dentro de _cadeias expansíveis_, ? seria mais considerado parte de um nome de variável (não {...} -enclosed), então, na verdade, _fixaríamos_ scripts existentes que usaram por engano strings como "Are you sure you want to kill $vParam?" . 😁
  • Se realmente sentimos que precisamos evitar quebrar a proporção cada vez menor de scripts existentes, nós _pudemos_ considerar a lógica proposta por @ ExE-Boss , mas não acho que queremos introduzir essa complexidade conceitual - muito menos complexidade de implementação (que eu realmente não posso falar).

@ SteveL-MSFT

Obrigado por discutir esse problema na chamada da comunidade de setembro (https://aka.ms/PSCommunityCall; no momento da redação deste artigo, a gravação da chamada de setembro não foi postada lá, mas está acessível em https://aka.ms/JoinPSCall, até a próxima chamada), começando às 46h, com base na sugestão de @ThomasNieto na solicitação anterior de tópicos de discussão em https://github.com/PowerShell/PowerShell-RFC/issues/260 :

(Eu esperava fornecer um link direto para a parte relevante da gravação, mas isso terá que esperar até que seja postado no YouTube e vinculado a https://aka.ms/PSCommunityCall.)

Com base em suas observações na gravação:

  • Visto que você indicou que gostaria de revisitar isso, reabra este problema e marque-o novamente como Review - Committee .

  • Quanto às suas observações de que colocar nomes de variáveis ​​entre chaves é um recurso preexistente e que as pessoas se opõem a ter que usá-los neste caso por motivos _estéticos_:

    • Não acho que alguém aqui _não_ saiba que {...} _pode_ e às vezes _deve_ ser usado para eliminar a ambigüidade de nomes de variáveis.
    • Também não é sobre _estética_, é principalmente sobre _não obter o comportamento esperado_ de uma maneira que _falha silenciosamente_, devido a _não esperar ter que usar {...} neste caso_; uma preocupação secundária é a inconveniência de digitação.
GitHub
Documentos RFC (Request for Comments) para feedback da comunidade sobre alterações de design e melhorias no ecossistema do PowerShell - PowerShell / PowerShell-RFC
Microsoft Teams

Estou preocupado com o fato de que este problema - agora aparentemente indefinidamente em um estado fechado - vai passar despercebido, então abri o # 14025 como um lembrete para revisitá-lo e convido todos os interessados ​​a mostrarem seu apoio.

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