Como preservar o formato de saída dos comandos do Powershell executados a partir de um objeto?

Como preservar o formato de saída dos comandos do Powershell executados a partir de um objeto?

Cenário

Gostaria de ver a saída dos comandos do PowerShell que são executados dentro de um método de um objeto, da mesma forma que os vejo "ao vivo/quando estão acontecendo" quando não estão dentro do objeto.

Exemplos

Para ilustrar a diferença indesejada, mostrarei 2 scripts, o primeiro mostra a saída do commnad, o segundo não. Ambos são executados abrindo o PowerShell, navegando até seu diretório e executados com./<scriptname>.ps1

  1. Considere scriptA.ps1com conteúdo:
lxrun /install /y

O que produz o resultado desejado de:

Warning: lxrun.exe is only used to configure the legacy Windows
Subsystem for Linux distribution. Distributions can be installed by
visiting the Windows Store: https://aka.ms/wslstore

This will install Ubuntu on Windows, distributed by Canonical and
licensed under its terms available here: https://aka.ms/uowterms

The legacy Windows Subsystem for Linux distribution is already
installed. 
  1. scriptB.ps1com conteúdo:
# runs single command
Class Object{
    runCommand(){
        lxrun /install /y
    }
}

# create object
[Object] $object = [Object]::new()

# execute command in method runCommand() of [Object] object
$object.runCommand()

O que não mostra nenhuma saída.

Tentativas

Todas as tentativas são feitas dentro do seguinte script, na linha:<command here>

# runs single command
Class Object{
    runCommand(){
        `<command here>`
    }
}
  1. Linha:
Write-Output (lxrun /install /y)

Saída resultante:(sem saída)


  1. Linha:
Write-Host (lxrun /install /y)

Saída resultante:Conteúdo correto, mas formatação de texto legível perdida/alterada:

W a r n i n g :   l x r u n . e x e   i s   o n l y   u s e d   t o  
c o n f i g u r e   t h e   l e g a c y   W i n d o w s   S u b s y s
t e m   f o r   L i n u x   d i s t r i b u t i o n .       D i s t r
i b u t i o n s   c a n   b e   i n s t a l l e d   b y   v i s i t i
n g   t h e   W i n d o w s   S t o r e :       h t t p s : / / a k a
. m s / w s l s t o r e             T h i s   w i l l   i n s t a l l 
U b u n t u   o n   W i n d o w s ,   d i s t r i b u t e d   b y   C
a n o n i c a l   a n d   l i c e n s e d   u n d e r   i t s   t e r
m s   a v a i l a b l e   h e r e :         h t t p s : / / a k a . m
s / u o w t e r m s             T h e   l e g a c y   W i n d o w s  
S u b s y s t e m   f o r   L i n u x   d i s t r i b u t i o n   i s 
a l r e a d y   i n s t a l l e d . 
  1. Linha:
Write-Host (lxrun /install /y) | Out-Host

Saída resultante: igual à tentativa 2.

  1. Linha:
Write-Host (lxrun /install /y) | Out-Default

Saída resultante: igual à tentativa 2.

  1. Linha:
Write-Host (lxrun /install /y) | Out-String

Saída resultante: igual à tentativa 2

  1. Linha:
write-verbose (lxrun /install /y)

Saída resultante:(sem saída)

Write-Verbose : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Message'.
Specified method is not supported.
At F:\path to example script\example.ps1:30 char:23
+         write-verbose (lxrun /install /y)
+                       ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Write-Verbose], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.WriteVerboseCommand
  1. Linha:
write-verbose (lxrun /install /y) -Verbose

Saída resultante: igual a 6 (exceto quando a linha em que ocorre o erro, o -Verbose é incluído)


  1. Linha:
(lxrun /install /y) | Write-Host

Saída resultante: Melhor legível, mas ainda não é o nem

o   c o n f i g u r e   t h e   l e g a c y  W i n d o w s   S u b s y
s t e m   f o r   L i n u x   d i s t r i b u t i o n .


 D i s t r i b u t i o n s   c a n   b e   i n s t a l l e d   b y   v
i s i t i n g   t h e   W i n d o w s   S t o r e :


 h t t p s : / / a k a . m s / w s l s t o r e





 T h i s   w i l l   i n s t a l l   U b u n t u   o n   W i n d o w s
,   d i s t r i b u t e d   b y   C a n o n i c a l   a n d   l i c e
n s e d   u n d e r   i t s   t e r m s   a v a i l a b l e   h e r e
:


 h t t p s : / / a k a . m s / u o w t e r m s





 T h e   l e g a c y   W i n d o w s   S u b s y s t e m   f o r   L i
n u x   d i s t r i b u t i o n   i s   a l r e a d y   i n s t a l l
e d . ```
  1. Linha:
Write-Verbose -Message (lxrun /install /y)

Saída resultante:

Write-Verbose : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Message'. Specified method is not supported.
At G:/path to file\example.ps1:35 char:32
 +         Write-Verbose -Message (lxrun /install /y)
 +                                ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Write-Verbose], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.WriteVerboseCommand
  1. Linha:
Write-Verbose -Message lxrun /install /y

Saída resultante: igual à tentativa 9

  1. Linha:
Write-Verbose -Message (lxrun /install /y) -Verbose

Saída resultante: igual à tentativa 9 (exceto quando a linha em que ocorre o erro, o -Verbose é incluído)

  1. Baseado ema pergunta e as respostas vinculadas nos comentários, modifiquei o script para:
# runs single command

#[CmdletBinding(SupportsShouldProcess=$true)]
[CmdletBinding()]
Param()
Class Object{    
    runCommand(){
        lxrun /install /y|Write-Verbose
    }
}

# create object
[Object] $object = [Object]::new()

# execute command in method runCommand() of [Object] object
$object.runCommand()

que é chamado com o parâmetro -Verbose ao executá-lo: ./example.ps1 -Verbose. Ele retorna um texto preto e amarelo:

VERBOSE: W a r n i n g :   l x r u n . e x e   i s   o n l y   u s e d   t o   >c o n f i g u r e   t h e
l e g a c y   W i n d o w s   S u b s y s t e m   f o r   L i n u x   d i s t >r i b u t i o n .
VERBOSE:
VERBOSE:
VERBOSE:  D i s t r i b u t i o n s   c a n   b e   i n s t a l l e d   b y   v >i s i t i n g   t h e
W i n d o w s   S t o r e :
VERBOSE:
VERBOSE:
VERBOSE:  h t t p s : / / a k a . m s / w s l s t o r e
VERBOSE:
VERBOSE:
VERBOSE:
VERBOSE:
VERBOSE:
VERBOSE:  T h i s   w i l l   i n s t a l l   U b u n t u   o n   W i n d o w s >,   d i s t r i b u t e d
b y   C a n o n i c a l   a n d   l i c e n s e d   u n d e r   i t s   t e r >m s   a v a i l a b l e
h e r e :
VERBOSE:
VERBOSE:
VERBOSE:  h t t p s : / / a k a . m s / u o w t e r m s
VERBOSE:
VERBOSE:
VERBOSE:
VERBOSE:
VERBOSE:
VERBOSE:  T h e   l e g a c y   W i n d o w s   S u b s y s t e m   f o r   L i >n u x
d i s t r i b u t i o n   i s   a l r e a d y   i n s t a l l e d .
VERBOSE:
VERBOSE:
VERBOSE:

Onde pelo que entendi está Param()incluído para passar/absorver o -Verboseargumento, e o[CmdletBinding()] permite deixar a ligação da maneira que cmdleta faz, em vez da maneira "normal" que um script faz.Ainda preciso entender o que essa diferença é/significa. E a formatação ainda não está correta/desejada com esta implementação Write-Verbose.

Pergunta

Como escrevo a saída de um comando no terminal PowerShell sem demora, sem perder sua formatação original (como acontece com scriptA.ps1)?

Observação

Poderíamos construir um analisador, mas acho que pode haver soluções mais eficientes e rápidas que ainda não encontrei.

Responder1

No final, descobriu-se que escrever um analisador era uma solução mais rápida (em termos de construção, não [necessariamente] em termos de tempo de execução), já que meu procedimento de pesquisa (ainda) não analisou por que e como os comandos com write..etc. alteraram a saída ( formatar).

# runs single command
Class Object{
    runCommand(){
        $output = (lxrun /install /y)
        Write-Host $this.parseOutput($output) # return formating back to "normal"
    }

    # removes null characters and then replaces triple spaces with enters.
    [String] parseOutput([String] $output){

        # first remove all nulls
        $output = $output.Replace(([char]0).ToString(),'')

        # then replace all triple occurrences of spaces with newlines
        $output = $output.Replace(([char]32+[char]32+[char]32).ToString(),"`n")

        return $output
    }

}

# create object
[Object] $object = [Object]::new()

# execute command in method runCommand() of [Object] object
$object.runCommand()

Isso "resolve"/remedia o problema, mas prefiro uma compreensão mais profunda. Portanto, se alguém puder explicar:

  1. Por que os caracteres extras nulle spacesão adicionados quando um comando é executado de/em um objeto, eu agradeceria muito.
  2. Como isso pode ser feito na mesma linha do comando, sem chamar um método auto-escrito, encontra-se uma solução melhor.

informação relacionada