Display program call from script

Hello,
I try to start an external program (e.g. “rar.exe”) with different parameters (e.g. “a -r source target”) from a script. As the parameters depend on the use of the program, they may change, so I store them in variables. The call thus looks like this (and normally it works):
& $progname $param1 $param2 $param3…
When running the script, I see the output of rar.exe, but the call itself (“rar a -r c:.…”) is not shown. However, if rar.exe delivers an error, I would very much like to see the call as well, to see what and why this happened. I did not find a way to get this displayed (Set-PSDebug Trace… did not help, this only displays “& $progname $param1…”).
Thans for any hint!

hi and welcome to the forum.
Please make sure you use the “Preformatted text” option when making a post to correctly format your code examples, that way it looks readable like this:

& $progname $param1 $param2 $param3

Or you can put three backtic characters before and after the lines with code.

For your question, I think the problem is that this isn’t Powershell, and so there aren’t a lot of guarantees in terms of behavior. I don’t have rar.exe to test with, so I used 7z.exe.

$progname = "C:\Program Files\7-Zip\7z.exe"
$param1 = "l"
$param2 = "c:\temp\archive1.7z"

then tried wrapping it in a try/catch block like you would normally do in Powershell

try {
& $progname $param1 $param2
} catch {
write-warning "didn't work"
}

But instead I just get executable output:

7-Zip 24.06 (x64) : Copyright (c) 1999-2024 Igor Pavlov : 2024-05-26

Scanning the drive for archives:

ERROR: The system cannot find the file specified.
c:\temp\archive1.7z



System ERROR:
The system cannot find the file specified.

I’m not sure what the circumstances are like for rar, but the only idea I have for you is to make a variable each time you use it with your “call”, and then once you figure out how to trigger based on an issue, you write “call” to the console.

$Call = $progname,$param1,$param2 -join ' ' 
# issue happens
    $Call

Hi grey0out,
thanks for the reply, tips, and sorry I am a bit slow in reacting - not much time for the moment.
I am not sure what exactly you mean that “this is not powershell”, and tried again some simpler things, even without variables. In fact, the basic problem can maybe best be seen when comparing to a cmd-batch file, which looks like (get contents of l:, which does not exist, so produces an error)

dir l:\ /s

The corresponding PowerShell code for a ps1-file would look like

Get-ChildItem -Path l:\ -recurse

The output of the cmd-File is in the first line the command, then in the second one the error message:

D:\>dir l:\ /s
Das System kann den angegebenen Pfad nicht finden.

The output of the ps1-File is only the error message.

PS D:\> D:\test3.ps1
Get-ChildItem : Das Laufwerk wurde nicht gefunden. Ein Laufwerk mit dem Namen "l" ist nicht vorhanden.
In D:\test3.ps1:1 Zeichen:1
+ Get-ChildItem -Path l:\ -recurse
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (l:String) [Get-ChildItem], DriveNotFoundException
    + FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

I personally find this confusing and not helpful when trying to locate an error :frowning:

I don’t think I’m understanding given your new example. Dir and Get-ChildItem both produce an error that more or less says the same thing?
In Powershell the error is given even more context by providing the line number and character position from the script file where this error occurred.

What I mean by this is that rar.exe is not a Powershell cmdlet, function or script. It’s an executable. When you execute it from Powershell session it will run, but it doesn’t behave like something that’s “Powershell aware.” Meaning, it doesn’t produce objects, it doesn’t have cmdletbinding to offer ErrorActionPreference, VerbosePreference etc.
So what I mean by this is that the normal Powershell rules don’t necessarily apply when talking about something that’s not powershell.
If you want context surrounding the execution of rar.exe I still think my original suggestion makes sense. Given your new examples though, I’m not sure I’m understanding the issue anymore.

Ok, then there was really a misunderstanding. My problem is not in any way related to rar.exe or another executable, nor the output of rar.exe, but to the output of the PowerShell, therefore my new example.
If I exectute a PowerShell command from a script, PowerShell will display the results of the command (e.g. an error message), but not the command itself. If you look at the examples above, you see that in the .cmd file output, what is displayed in top of the error message is the command itself

dir l:\ /s

whereas the output of the .ps1 file starts directly with the error message.

Get-ChildItem : Das Laufwerk wurde nicht gefunden....

I.e. you cannot see the command which led to the error message. What I would expect is, as in the .cmd example:

Get-ChildItem -Path l:\ -recurse
Get-ChildItem: Das Laufwerk wurde nicht gefunden....

(of course, this is not relevant for this example, but if you use variables as in the rar-example at the very top, and something goes wrong, it may be helpful to see which command exactly was executed and led to the error.

I’m gonna run something, and use pictures, and you tell me if I’m on the right track or not.
I’ve written Start-Test.ps1 and it contains the following code:

$TempDir = Get-ChildItem C:\Temp
$LDrive = Get-ChildItem L:\
$ScriptsDir = Get-ChildItem C:\Scripts

The first line, and last line should execute without error, but the middle line should generate an error.
If I execute it from a Powershell window I get this:


The error text tells me that the script produced the error, at line 2, character 11, and then it shows the full context of line 2 from the script. Isn’t that the “command call” you’re looking for?

1 Like

Ohps, I am sorry, I overlooked this.
But in my original example, where I used variables, it does not work like this. For example:

$prog = "copy"
$param1 = "d:\test\ax.txt"
$param2 = "d:\test\b.txt"
& $prog $param1 $param2

Where d:\test\ax.txt does not exist, it give the output:

PS C:\Users\Winni> D:\test2.ps1
Copy-Item : Der Pfad "D:\test\ax.txt" kann nicht gefunden werden, da er nicht vorhanden ist.
In D:\test2.ps1:4 Zeichen:1
+ & $prog $param1 $param2
+ ~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (D:\test\ax.txt:String) [Copy-Item], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.CopyItemCommand

It does not display the program call

copy d:\test\ax.txt d:\test\b.test

but instead the unresolved line in the .ps1 file

+ & $prog $param1 $param2

Furthermore, this helps only in case the call itself produces an error. In my case

& rar.exe param1 param2 param3

param3 was “>>err.txt” to write the errors produced by rar.exe to a file. This file was not produced, and I wanted to see why - i.e. see the program call itself to see whether I had made an error with my variables or the program was called the way I wanted. Or even to copy the program call and run it stand-alone to see what happens, but this is not possible because the call never appears in the output.

I simply store the command in a string, add in a verbose output that you can invoke by adding -Verbose to your script/function. (Be sure to use [cmdletbinding()])

$command = "$prog $param1 $param2"
Write-Verbose $command

That will contain the actual values

Ok I think I’m better understanding now, but I still don’t really see the issue.

Even though I don’t read German I can tell what the error is here. It says that the Copy-Item cmdlet failed to copy the file “D:\test\ax.txt” because it does not exist. Then it says the exact code that led to this error:

& $prog $param1 $param2

I know that $prog would be “Copy-Item” because that’s the cmdlet that produced the error, and since I’m inappropriately calling a cmdlet without naming parameters I know that $Param1 would be the value for -Path and $Param2 would be the value for -Destination. Since it failed to “find” an object to copy, the issue must be with $Param1.

Now that’s a very specific Powershell example and I don’t think it covers everything you’re talking about but a more appropriate way to code that same example would be

$param1 = "d:\test\ax.txt"
$param2 = "d:\test\b.txt"

try {
    Copy-Item -Path $param1 -Destination $param2 -ErrorAction Stop
} catch {
    throw $Error[0]
}

This would produce the same red error text, but the original code is more clear bout what’s happening.
image
Or you could have the catch block enumerate your variable values

$param1 = "d:\test\ax.txt"
$param2 = "d:\test\b.txt"

try {
    Copy-Item -Path $param1 -Destination $param2 -ErrorAction Stop
} catch {
    [pscustomobject]@{Param1=$Param1;Param2=$Param2}
    $Error[0]
}

image

and now we’re back to “this isn’t Powershell”. You’re using Powershell to call an executable that is not Powershell-aware. Powershell may not even recent a stop-code from rar.exe and so there’d be nothing to trigger off of.

What I suggest is to create a logging function, and write everything you’re doing in the script to a file.

Function Write-LogEntry {
    [cmdletbinding()]
    [Alias("Logmsg")]
    Param(
        [Parameter(Mandatory = $false, Position = 0)]
        [String]$Message,
        $Obj,
        [Parameter(Mandatory = $false, Position = 1)]
        [ValidateSet("Info","Warning","Error")]
        [String]$Severity = "Info"
    )

    Begin {
        $Destination = "C:\Path\To\Where\You\Want\Logs.txt"
        $CurrentTime =  Get-Date -Format 'MM/dd/yy HH:mm:ss'
        $OutputObject = [PSCustomObject]@{
            Timestamp = $CurrentTime
            Severity = $Severity
            LogObject = $Message
        }
    }

    Process {
        if ($Obj) {
            Out-File -FilePath $Destination -Encoding 'UTF8' -Append -NoClobber -InputObject ('{0} {1}: {2}' -f $OutputObject.Timestamp,$OutputObject.Severity,"Object Output") -ErrorAction Stop
            Out-File -FilePath $Destination -InputObject $Obj -Encoding 'UTF8' -Append -NoClobber
        } else {
            Out-File -FilePath $Destination -Encoding 'UTF8' -Append -NoClobber -InputObject ('{0} {1}: {2}' -f $OutputObject.Timestamp,$OutputObject.Severity,$OutputObject.LogObject) -ErrorAction Stop
        }
    }
}

Then before you execute rar.exe, or copy-item, or whatever you can leverage your logging function:

Write-LogEntry -Message "executing win rar with the following arguments"
Write-LogEntry -Message "Param1 = $Param1"
Write-LogEntry -Message "Param2 = $Param2"

Then regardless of what happens in your script you’ll have a record outside of it.

Thank you very much for the proposals and the effort you invested! I am on leave for some days and will give it a try when I am back.

1 Like

Hello again,
taking some of your proposals, I think I am closer to the reason of the problem. It seems that it is related to the parameter for redirecting the output to a file. However, as this is a different issue now, will open a separate discussion on it.