AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 77778774
Accepted
Samuel
Samuel
Asked: 2024-01-08 18:13:31 +0800 CST2024-01-08 18:13:31 +0800 CST 2024-01-08 18:13:31 +0800 CST

Localização do erro de sintaxe imprecisa ao usar ForEach-Object em vez de foreach

  • 772

Na maioria das vezes, o relatório de erros no PowerShell é muito útil. Vejo erros, vejo a origem, mas notei que quaisquer erros nos pipelines ForEach-Object perdem sua linha de origem e a pilha de erros simplesmente aponta para a linha de código com a extensão ForEach-Object.

Exemplo de localização de erro incorreta

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

[cultureinfo]::CurrentUICulture = 'en-US'

function Test-Something($val)
{
    # var is not defined here
    Write-Host($undefined_variable)
}

@("a","b") | ForEach-Object{
    $entry = $_

    Test-Something $entry
}

Resulta em

ForEach-Object : The variable '$undefined_variable' cannot be retrieved because it has not been set.
In D:\dummy.ps1:12 Line:14
+ @("a","b") | ForEach-Object{
+              ~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (undefined_variable:String) [ForEach-Object], RuntimeException
    + FullyQualifiedErrorId : VariableIsUndefined,Microsoft.PowerShell.Commands.ForEachObjectCommand

A linha 12aponta para @("a","b") | ForEach-Object{, que obviamente não é o local do erro.

Exemplo muito mais utilizável com foreach

Agora estou usando um foreach(apenas as últimas 4 linhas de código foram alteradas)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

[cultureinfo]::CurrentUICulture = 'en-US'

function Test-Something($val)
{
    # var is not defined here
    Write-Host($undefined_variable)
}

$data = @("a","b")

foreach($entry in $data)
{
    Test-Something $entry
}

O erro agora é muito mais utilizável, na verdade aponta para a linha de erro 9paraWrite-Host($undefined_variable)

The variable '$undefined_variable' cannot be retrieved because it has not been set.
In D:\dummy.ps1:9 Line:16
+     Write-Host($undefined_variable)
+                ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (undefined_variable:String) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : VariableIsUndefined

Isso está relacionado à natureza da tubulação dos operadores do gasoduto ou estou entendendo algo errado. O primeiro cenário torna o rastreamento de erros muito difícil quando há mais funções entre eles. Na maioria dos casos, posso mudar, foreachmas adoraria entender o que realmente está acontecendo.

powershell
  • 2 2 respostas
  • 105 Views

2 respostas

  • Voted
  1. mclayton
    2024-01-08T19:16:17+08:002024-01-08T19:16:17+08:00

    Isso pode ser uma simplificação excessiva, e alguém com conhecimento formal mais profundo pode ter uma resposta técnica mais precisa, mas vale a pena notar que fore foreachsão instruções internas do PowerShell (chamadas de "comandos de linguagem" nos links da documentação) que contêm uma lista de instruções aninhadas:

    for (<Init>; <Condition>; <Repeat>)
    {
        <Statement list>
    }
    
    foreach ($<item> in $<collection>)
    {
        <statement list>
    }
    

    Eles fazem parte da sintaxe do PowerShell e o mecanismo sabe qual instrução da lista está sendo executada, enquanto ForEach-Objecté "apenas" um cmdlet como, por exemplo, Invoke-Commandou Get-ChldItem.

    ForEach-Object
        ... snip ...
        [-Begin    <ScriptBlock>]
        [-Process] <ScriptBlock[]>
        [-End      <ScriptBlock>]
        ... snip ...
    

    Como resultado, seu foreach-objectexemplo é equivalente a este:

    1 | $myScriptBlock = {
    2 |     $entry = $_
    3 | 
    4 |     Test-Something $entry
    5 | }
    6 |
    7 | @("a", "b") | foreach-object -process $myScriptBlock
    

    ForEach-ObjectOs parâmetros posicionais de permitem que você invoque o cmdlet com sintaxe semelhante a for/ foreach, mas a mecânica subjacente é diferente e, no que diz respeito ao mecanismo do PowerShell, a linha de código de nível superior que relata o erro é a linha 7, não a 4 .

    Ele não remonta à linha de código-fonte que define o bloco de script - no seu caso provavelmente poderia , mas acho que seria difícil em um caso mais planejado, onde o bloco de script está sendo gerado a partir de código dinâmico, por exemplo

    1 | $myScriptBlock = [scriptblock]::Create(
    2 |     "`$entry = `$_" +
    3 |     "`r`n" + "`r`n" + "`r`n" +
    4 |     "Test-Something `$entry"
    5 | )
    6 |
    7 | @("a", "b") | foreach-object -process $myScriptBlock
    

    Nesse caso, qual número de linha o erro deve relatar?

    Atualizar

    De acordo com o comentário de @ zett42 sobre rastreamentos de pilha, o PowerShell contém informações sobre qual linha lança a exceção na $_.ScriptStackTracepropriedade da exceção - veja, por exemplo:

    Set-StrictMode -Version Latest
    $ErrorActionPreference = "Stop"
    
    [cultureinfo]::CurrentUICulture = 'en-US'
    
    function Test-Something($val)
    {
        # var is not defined here
        Write-Host($undefined_variable)
    }
    
    try
    {
        $myScriptBlock = [scriptblock]::Create(
            "`$entry = `$_" + "`r`n" + "`r`n" +
            "Test-" +
            "Something `$entry"
        )
        @("a", "b") | foreach-object -process $myScriptBlock
    }
    catch
    {
        $_.ScriptStackTrace
        throw
    }
    

    Executar isso dá:

    C:\> pwsh C:\src\so\stack.ps1
    at Test-Something, C:\src\so\stack.ps1: line 9
    at <ScriptBlock>, <No file>: line 3
    at <ScriptBlock>, C:\src\so\stack.ps1: line 19
    ForEach-Object: C:\src\so\stack.ps1:19
    Line |
      19 |      @("a", "b") | foreach-object -process $myScriptBlock
         |                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         | The variable '$undefined_variable' cannot be retrieved because it has not been set.
    

    Portanto, a resposta simples é que parece que o PowerShell apenas relata a linha de nível superior que a exceção atingiu. No caso de fore foreachessa é a linha que contém as instruções aninhadas, e for foreach-objecté a linha foreach-objectem que aparece.

    E para responder à minha própria pergunta retórica:

    qual número de linha o erro deve relatar?

    Para um scriptblock dinâmico, é o número da linha interna relativo à origem do scriptblock (e não o número da linha do script principal) - veja:

    at <ScriptBlock>, <No file>: line 3
    
    • 5
  2. Best Answer
    Darin
    2024-01-08T21:00:10+08:002024-01-08T21:00:10+08:00

    Para uma versão mais rápida e informativa do ForEach-Object, use .{process {<code>}} .

    Esta versão é mais otimizada e executada em menos tempo, mas é indiscutivelmente menos legível e carece de algumas funcionalidades do ForEach-Object. No entanto, como a funcionalidade extra raramente é usada, ela é uma substituição individual na maioria dos casos. Nos casos em que você deseja usar -Beginou -End, remova o comentário conforme necessário nas linhas begin {}ou end {}e adicione o código desejado.

    Este código:

    Set-StrictMode -Version Latest
    $ErrorActionPreference = "Stop"
    
    [cultureinfo]::CurrentUICulture = 'en-US'
    
    function Test-Something($val) {
       # var is not defined here
       Write-Host($undefined_variable)
    }
    
    @("a", "b") | .{
       # begin {}
       process {
          $entry = $_
    
          Test-Something $entry
       }
       # end {}
    }
    

    Produz esta saída:

    The variable '$undefined_variable' cannot be retrieved because it has not been set.
    At D:\Sync\Work\StackOverflow\PowerShell\77778774\SeluwuFE-Oiof.ps1:8 char:15
    +    Write-Host($undefined_variable)
    +               ~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidOperation: (undefined_variable:String) [], ParentContainsErrorRecordException
        + FullyQualifiedErrorId : VariableIsUndefined
    

    Explicação:

    (Pule para o número 7 para o verão de 2 a 6)

    1. Primeiramente aprendi isso com uma resposta dada aqui no Stack Overflow, infelizmente não tenho link para essa resposta, mas quase 100% de certeza que foi essa resposta dada por Santiago .
    2. Esta solução usa dot-sourcing , uma breve declaração sobre isso pode ser encontrada aqui .
    3. O dot-sourcing executa um bloco de script e "Assim como as funções, os blocos de script podem incluir as palavras-chave DynamicParam, Begin, Process e End."
    4. Acredito que a seção Usando blocos de script de ligação com atraso com parâmetros se aplica a esse uso de um bloco de script, mas, para ser honesto, preciso fazer mais pesquisas para ter certeza de que entendi completamente o que está acontecendo.
    5. Pelo que vi, as seções , e em uma função ou bloco de script são projetadas para lidar com entradas de pipeline begin {}. A seção é executada antes de qualquer entrada do pipeline ser processada, executada uma vez para cada item no pipeline e, posteriormente, a seção é executada. process {}end {}begin {}process {}end {}
      • O que às vezes considero muito útil begin {}é que você pode definir funções nele e também variáveis ​​que podem ser usadas para acumular informações relacionadas a itens no pipeline (como uma contagem ou soma).
      • O que às vezes considero muito útil end {}é que você pode criar a saída do pipeline desejada e enviá-la logo antes da saída do bloco de script (isso pode incluir informações como contagem ou soma).
    6. Pelo que posso dizer, o aumento de desempenho vem do fato de que Foreach-Object não faz parte da linguagem PowerShell, mas é um cmdLet fornecido pelo módulo Microsoft.PowerShell.Core. Onde o fornecimento de pontos e os blocos de script fazem realmente parte da linguagem do PowerShell. Acredito que o cmdlet Foreach-Object é uma classe C# que herda do PSCmdlet. Como tal, uma instância da classe deve ser criada e inicializada, e vários métodos da classe são chamados. Isto, e provavelmente outros fatores, produz sobrecarga que diminui o desempenho. Os cmdlets são ótimos e provavelmente 99% das vezes o impacto no desempenho não tem consequências e pode ser ignorado.
    7. Para juntar tudo isso, a entrada do pipeline é alimentada em um bloco de script de origem pontual com uma process {}seção que é executada uma vez por cada item do pipeline, com cada item atribuído à variável automática $_e com o aumento de desempenho adicional de não usar um cmdlet para obter o trabalho feito.

    Desempenho:

    Santiago, nos comentários, destacou que a operadora Call &é otimizada internamente, enquanto a operadora Dot Sourcing .não. Acredito que ele descobriu isso observando o código-fonte do PowerShell. Pensando nisso, o uso da operadora Call deveria ter melhor desempenho em relação à operadora Dot Sourcing, então executei um teste de desempenho.

    O problema de executar testes em um computador é que os processos externos podem causar problemas de desempenho que interferem no tempo de execução do código do PowerShell. Para distribuir esses problemas uniformemente em cada método, os resultados do teste para o operador Call, Dot Sourcing, loop Foreach e cmdlet Foreach-Object envolvem a execução de cada método em turnos para somar um milhão de números aleatórios (0-99), com um total de 100 execuções por método. O tempo total foi calculado como a soma de todas as execuções de cada método.

    Como você pode ver nas tabelas a seguir, o operador Call &executou a tarefa no menor tempo possível.

    PowerShell 5.1.19041.3803

    Call operator   : 00:00:40,227 (40.227 seconds)
    Dot Sourcing    : 00:01:33,959 (1 minute and 33.956 seconds)
    Foreach loop    : 00:01:29,010 (1 minute and 29.010 seconds)
    Foreach-Object  : 00:07:37,280 (7 minutes and 37.280 seconds)
    

    PowerShell 7.3.9

    Call operator   : 00:00:26,817 (26.817 seconds)
    Dot Sourcing    : 00:00:50,946 (50.946 seconds)
    Foreach loop    : 00:00:40,562 (40.562 seconds)
    Foreach-Object  : 00:03:13,608 (3 minutes and 13.608 seconds)
    

    Código de teste do operador de chamada &:

    $CallOperator = {
       $RandomIntegers | &{
          begin {
             $Sum = 0
          }
          process {
             $Sum += $_
          }
          end {
             'Sum: {0:N0}' -f $Sum
          }
       }
    }
    

    .Código de teste do operador de fornecimento de pontos :

    $DotSourcing = {
       $RandomIntegers | .{
          begin {
             $Sum = 0
          }
          process {
             $Sum += $_
          }
          end {
             'Sum: {0:N0}' -f $Sum
          }
       }
    }
    

    Código de teste do loop Foreach:

    $ForeachLoop = {
       $Sum = 0
       foreach ($RandomInteger in $RandomIntegers) {
          $Sum += $RandomInteger
       }
       'Sum: {0:N0}' -f $Sum
    }
    

    Código de teste do cmdlet Foreach-Object:

    $ForeachObject = {
       $RandomIntegers | Foreach-Object -Begin {
          $Sum = 0
       } -Process {
          $Sum += $_
       } -End {
          'Sum: {0:N0}' -f $Sum
       }
    }
    

    Código para executar o teste:

    Write-Host "Creating Random Numbers:"
    [int[]]$RandomIntegers = 1..1000000 | .{ process { Get-Random -Maximum 100 } }
    Write-Host "Defining Variables:"
    $CallOperatorMilliseconds = 0
    $DotSourcingMilliseconds = 0
    $ForeachLoopMilliseconds = 0
    $ForeachObjectMilliseconds = 0
    Write-Host "Staring loop"
    [int]$LoopCount = 100
    
    for ($i = 0; $i -lt $LoopCount; $i++) {
       Write-Host "[$i]"
       $CallOperatorMilliseconds += (Measure-Command -Expression $CallOperator).TotalMilliseconds
       $DotSourcingMilliseconds += (Measure-Command -Expression $DotSourcing).TotalMilliseconds
       $ForeachLoopMilliseconds += (Measure-Command -Expression $ForeachLoop).TotalMilliseconds
       $ForeachObjectMilliseconds += (Measure-Command -Expression $ForeachObject).TotalMilliseconds
    }
    

    Imprimindo os resultados do teste:

    $Width = 16
    "{0,-$Width}: {1:hh\:mm\:ss\,fff}" -f 'Call operator', [TimeSpan]::FromMilliseconds($CallOperatorMilliseconds)
    "{0,-$Width}: {1:hh\:mm\:ss\,fff}" -f 'Dot Sourcing', [TimeSpan]::FromMilliseconds($DotSourcingMilliseconds)
    "{0,-$Width}: {1:hh\:mm\:ss\,fff}" -f 'Foreach loop', [TimeSpan]::FromMilliseconds($ForeachLoopMilliseconds)
    "{0,-$Width}: {1:hh\:mm\:ss\,fff}" -f 'Foreach-Object', [TimeSpan]::FromMilliseconds($ForeachObjectMilliseconds)
    
    • 4

relate perguntas

  • Como faço para obter o identificador de processo de um trabalho?

  • One-liner do PowerShell - Como

  • Powershell 5.1: descrições de alias estão faltando no meu módulo

  • Import-CSV: adiciona linhas (membros) ao objeto resultante no loop foreach

  • Use e modifique minha variável em todo powershell startthreadjob

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve