Output is different between manual and programmatic executions in PowerShell

java powershell

110 观看

2回复

286 作者的声誉

I'm getting two different outputs from the same command when run manually vs. programmatically, and I don't understand why.

The command in question:

powershell -Command "Get-Module -ListAvailable"

When run manually in the command prompt, I get this output:

U:\> powershell -Command "Get-Module -ListAvailable"

Directory: C:\Program Files\WindowsPowerShell\Modules

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Binary     1.0.0.1    PackageManagement                   {Find-Package, Get-Package, Get-PackageProvider, Get-PackageSource...}
Script     1.0.0.1    PowerShellGet                       {Install-Module, Find-Module, Save-Module, Update-Module...}
Binary     6.5.1.6... VMware.DeployAutomation             {Add-DeployRule, Add-ProxyServer, Add-ScriptBundle, Copy-DeployRule...}
Binary     6.5.1.6... VMware.ImageBuilder                 {Add-EsxSoftwareDepot, Add-EsxSoftwarePackage, Compare-EsxImageProfile, Export-EsxImageProfile...}
Manifest   6.5.4.7... VMware.PowerCLI
Binary     6.5.4.6... VMware.VimAutomation.Cis.Core       {Connect-CisServer, Disconnect-CisServer, Get-CisService}
Binary     6.5.1.5... VMware.VimAutomation.Cloud          {Add-CIDatastore, Connect-CIServer, Disconnect-CIServer, Get-Catalog...}
Manifest   6.5.4.6... VMware.VimAutomation.Common
Binary     6.5.2.6... VMware.VimAutomation.Core           {Add-PassthroughDevice, Add-VirtualSwitchPhysicalNetworkAdapter, Add-VMHost, Add-VMHostNtpServer...}
Binary     6.5.4.7... VMware.VimAutomation.HA             Get-DrmInfo
Binary     7.1.0.5... VMware.VimAutomation.HorizonView    {Connect-HVServer, Disconnect-HVServer}
Binary     6.5.1.5... VMware.VimAutomation.License        Get-LicenseDataManager
Binary     2.0.0.6... VMware.VimAutomation.Nsxt           {Connect-NsxtServer, Disconnect-NsxtServer, Get-NsxtService}
Binary     6.5.1.5... VMware.VimAutomation.PCloud         {Connect-PIServer, Disconnect-PIServer, Get-PIComputeInstance, Get-PIDatacenter}
Manifest   1.0.0.5... VMware.VimAutomation.Sdk            {Get-PSVersion, Get-InstallPath}
Binary     6.5.1.5... VMware.VimAutomation.Srm            {Connect-SrmServer, Disconnect-SrmServer}
Binary     6.5.4.7... VMware.VimAutomation.Storage        {Add-KeyManagementServer, Copy-VDisk, Export-SpbmStoragePolicy, Get-KeyManagementServer...}
Script     1.1        VMware.VimAutomation.StorageUtility Update-VmfsDatastore
Binary     6.5.1.5... VMware.VimAutomation.Vds            {Add-VDSwitchPhysicalNetworkAdapter, Add-VDSwitchVMHost, Export-VDPortGroup, Export-VDSwitch...}
Binary     6.5.4.7... VMware.VimAutomation.Vmc            {Connect-Vmc, Disconnect-Vmc, Get-VmcService, Connect-VmcServer...}
Binary     6.5.1.5... VMware.VimAutomation.vROps          {Connect-OMServer, Disconnect-OMServer, Get-OMAlert, Get-OMAlertDefinition...}
Binary     6.5.1.5... VMware.VumAutomation                {Add-EntityBaseline, Copy-Patch, Get-Baseline, Get-Compliance...}

Directory: C:\Windows\system32\WindowsPowerShell\v1.0\Modules

ModuleType Version Name                             ExportedCommands
---------- ------- ----                             ----------------
Manifest   1.0.0.0 AppLocker                        {Set-AppLockerPolicy, Get-AppLockerPolicy, Test-AppLockerPolicy, Get-AppLockerFileInformation...}
Manifest   1.0.0.0 BitsTransfer                     {Add-BitsFile, Remove-BitsTransfer, Complete-BitsTransfer, Get-BitsTransfer...}
Manifest   1.0.0.0 CimCmdlets                       {Get-CimAssociatedInstance, Get-CimClass, Get-CimInstance, Get-CimSession...}
Script     1.0.0.0 ISE                              {New-IseSnippet, Import-IseSnippet, Get-IseSnippet}
Manifest   1.0.0.0 Microsoft.PowerShell.Archive     {Compress-Archive, Expand-Archive}
Manifest   3.0.0.0 Microsoft.PowerShell.Diagnostics {Get-WinEvent, Get-Counter, Import-Counter, Export-Counter...}
Manifest   3.0.0.0 Microsoft.PowerShell.Host        {Start-Transcript, Stop-Transcript}
Manifest   3.1.0.0 Microsoft.PowerShell.Management  {Add-Content, Clear-Content, Clear-ItemProperty, Join-Path...}
Script     1.0     Microsoft.PowerShell.ODataUtils  Export-ODataEndpointProxy
Manifest   3.0.0.0 Microsoft.PowerShell.Security    {Get-Acl, Set-Acl, Get-PfxCertificate, Get-Credential...}
Manifest   3.1.0.0 Microsoft.PowerShell.Utility     {Format-List, Format-Custom, Format-Table, Format-Wide...}
Manifest   3.0.0.0 Microsoft.WSMan.Management       {Disable-WSManCredSSP, Enable-WSManCredSSP, Get-WSManCredSSP, Set-WSManQuickConfig...}
Manifest   1.0.0.0 NetworkSwitchManager             {Disable-NetworkSwitchEthernetPort, Enable-NetworkSwitchEthernetPort, Get-NetworkSwitchEthernetPort, Remove-NetworkSwitchEthernetPortIPAddress...}
Manifest   1.1     PSDesiredStateConfiguration      {Set-DscLocalConfigurationManager, Start-DscConfiguration, Test-DscConfiguration, Publish-DscConfiguration...}
Script     1.0.0.0 PSDiagnostics                    {Disable-PSTrace, Disable-PSWSManCombinedTrace, Disable-WSManTrace, Enable-PSTrace...}
Binary     1.1.0.0 PSScheduledJob                   {New-JobTrigger, Add-JobTrigger, Remove-JobTrigger, Get-JobTrigger...}
Manifest   2.0.0.0 PSWorkflow                       {New-PSWorkflowExecutionOption, New-PSWorkflowSession, nwsn}
Manifest   1.0.0.0 PSWorkflowUtility                Invoke-AsWorkflow
Manifest   1.0.0.0 TroubleshootingPack              {Get-TroubleshootingPack, Invoke-TroubleshootingPack}

Directory: C:\opscode\chefdk\modules

ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script     0.0     chef {chef-apply, chef-client, chef-service-manager, chef-shell...}

When run programmatically (via Java), I get this output:

ModuleType Name                      ExportedCommands
---------- ----                      ----------------
Binary     PackageManagement         {Find-Package, Get-Package, Get-PackageProvider, Get-PackageSource...}
Script     PowerShellGet             {Install-Module, Find-Module, Save-Module, Update-Module...}
Manifest   BitsTransfer              {Add-BitsFile, Remove-BitsTransfer, Complete-BitsTransfer, Get-BitsTrans...
Manifest   CimCmdlets                {Get-CimAssociatedInstance, Get-CimClass, Get-CimInstance, Get-CimSessio...
Script     ISE                       {New-IseSnippet, Import-IseSnippet, Get-IseSnippet}
Manifest   Microsoft.PowerShell.A... {Compress-Archive, Expand-Archive}
Manifest   Microsoft.PowerShell.D... {Get-WinEvent, Get-Counter, Import-Counter, Export-Counter...}
Manifest   Microsoft.PowerShell.Host {Start-Transcript, Stop-Transcript}
Manifest   Microsoft.PowerShell.M... {Add-Content, Clear-Content, Clear-ItemProperty, Join-Path...}
Script     Microsoft.PowerShell.O... Export-ODataEndpointProxy
Manifest   Microsoft.PowerShell.S... {Get-Acl, Set-Acl, Get-PfxCertificate, Get-Credential...}
Manifest   Microsoft.PowerShell.U... {Format-List, Format-Custom, Format-Table, Format-Wide...}
Manifest   Microsoft.WSMan.Manage... {Disable-WSManCredSSP, Enable-WSManCredSSP, Get-WSManCredSSP, Set-WSManQ...
Manifest   PSDesiredStateConfigur... {Set-DscLocalConfigurationManager, Start-DscConfiguration, Test-DscConfi...
Manifest   PSDiagnostics             {Start-Trace, Stop-Trace, Enable-WSManTrace, Disable-WSManTrace...}
Binary     PSScheduledJob            {New-JobTrigger, Add-JobTrigger, Remove-JobTrigger, Get-JobTrigger...}
Manifest   TroubleshootingPack       {Get-TroubleshootingPack, Invoke-TroubleshootingPack}
Script     chef                      {chef-apply, chef-client, chef-service-manager, chef-shell...}

The output from a programmatic run is missing the "Version" column and is only a subset of the available modules.

I thought maybe somehow I was invoking two different PowerShell executables, so I ran powershell -Command "$PSVersionTable" both manually and programmatically.

The output from a manual run of powershell -Command "$PSVersionTable":

Name                           Value
----                           -----
PSVersion                      5.0.10586.117
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.10586.117
CLRVersion                     4.0.30319.18444
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

The output from a programmatic run of powershell -Command "$PSVersionTable":

Name                           Value
----                           -----
PSVersion                      5.0.10586.117
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.10586.117
CLRVersion                     4.0.30319.18444
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

So I think I'm running the same PowerShell executable.

Why am I getting two completely different outputs when running the same command?

The actual Java code that is being used to programmatically run the command:

public class Example {

    private void myMethod(String command) throws IOException {
        Process process = Runtime.getRuntime().exec(command);
        BufferedReader bufferedReader =
            new BufferedReader(new InputStreamReader(process.getInputStream()));

        String output = "";
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            output += (line + "\n");
        }

        System.out.println((output.isEmpty() ?
                            "No output was received!!!" :
                            output));


        BufferedReader errorBufferedReader =
            new BufferedReader(new InputStreamReader(process.getErrorStream()));
        String errorOutput = "";
        while ((line = errorBufferedReader.readLine()) != null) {
            errorOutput += (line + "\n");
        }

        System.out.println((errorOutput.isEmpty() ?
                           "Nothing in the error output stream." :
                           errorOutput));
    }


    public static void main(String[] args) throws IOException {
        new Example().myMethod(
            "powershell -Command \"Get-Module -ListAvailable\"");
    }

}
作者: Chiefwarpaint 的来源 发布者: 2017 年 12 月 27 日

回应 2


1

22767 作者的声誉

My guess is that you just don't get formatted output when calling PowerShell programmatically from Java, or the different versions have different default format configurations for some reason.

First, some background:

The output you get from running PowerShell at the command line is dictated by the formatting in the format files. These are all stored at $PSHome\*.format.ps1xml. Get-Module returns objects of type System.Management.Automation.PSModuleInfo. If you run Select-String -Pattern 'PSModuleInfo' -Path "$PSHome\*.format.ps1xml" you can find where the default formats are specified. There will be Table for Format-Table, which is the default you normally get. There's also Wide for Format-Wide, and List for Format-List. You can learn more about the console output formatting at Get-Help about_Format.ps1xml.

Since the default output for PSModuleInfo is to use the Table formatting, you should be able to make your Java match PowerShell by calling:

new Example().myMethod("powershell -Command \"Get-Module -ListAvailable | Format-Table\"");

Or possibly:

new Example().myMethod("powershell -Command \"Get-Module -ListAvailable | Format-Table | Out-String\"");

Or maybe even just:

new Example().myMethod("powershell -Command \"Get-Module -ListAvailable | Out-String\"");

As for what Java is doing different or why it's not formatting similarly, I'm afraid I don't know.

You can also always do something like:

new Example().myMethod("powershell -Command \"Get-Module -ListAvailable | Out-String\"");

You may also have success with Get-Module -ListAvailable | ConvertTo-Json -Compress -Depth 1 or Get-Module -ListAvailable | ConvertTo-Xml -NoTypeInformation -As String -Depth 1 or similar if you want to serialize your output. Just bear in mind that the default depth of ConvertTo-Json is 2, the default depth of ConvertTo-Xml is 1, and that anything more than depth 3 is going to be really slow.

作者: Bacon Bits 发布者: 2017 年 12 月 27 日

0

286 作者的声誉

决定

I'm 99.9% certain this was the true cause of my issue.

When I was manually running the command to check the list of available modules, I was using a 64-bit instance of PowerShell.

My Java program was running in a 32-bit context (was built against a 32-bit JDK). When my program invoked the cmd prompt, it was getting a 32-bit cmd prompt. When the cmd prompt invoked PowerShell, it was getting a 32-bit PowerShell. The 32-bit and 64-bit versions of PowerShell have unique install paths where they store modules.

So, when I was manually installed modules, I was always using a 64-bit PowerShell. I had never manually opened a 32-bit PowerShell and installed any modules. That's why the programmatic output of a Get-Module -ListAvailable was so small, compared to what I was seeing when I was manually running the same command.

Thanks to @BaconBits for providing me with the clue that helped me identify what was going on. That tip was to execute a [Environment]::Is64BitProcess to verify what OS architecture context my PowerShell was actually running in.

作者: Chiefwarpaint 发布者: 2017 年 12 月 29 日
32x32