I have a powershell script, p.ps1, which runs a batch-file, b.bat. The batch file runs an application MyApp.exe. I also have a log file saved in local variable $LogFile.
Is there a way to configure the PowerShell script in a way that all nested applications including MyApp.exe will write to the same log file, without changing b.bat and MyApp.exe?
I've already tried:
Start-Process "cmd.exe" "/c b.bat" -Wait | Add-Content $LogFile
And
Start-Process "cmd.exe" "/c b.bat" -Wait | Out-File $LogFile -Append
Expected Results:
All outputs will be written to $LogFile, I don't care whether outputs will be written to another log file, configured by the code of MyApp.exe.
Actual Results:
All outputs of b.bat were written to $LogFile, but the outputs from MyApp.exe were written to another log file, configured by the code of MyApp.exe.
I think I found the solution:
From the powershell file:
$command = "& 'b.bat' 'arg1' 'arg2'"
Invoke-Expression $command | Add-Content $LogFile
From the myApp code, writing in C#:
Trace.WriteLine(msg);
This way, the c# program wrote everything to the screen. The powershell script caught the output and added it to the log file using Add-Content command.
Related
What is the required syntax to redirect standard input/output on Windows PowerShell?
On Unix, we use:
$./program <input.txt >output.txt
How do I execute the same task in PowerShell?
You can't hook a file directly to stdin, but you can still access stdin.
Get-Content input.txt | ./program > output.txt
If there is someone looking for 'Get-Content' alternative for large files (as me) you can use CMD in PowerShell:
cmd.exe /c ".\program < .\input.txt"
Or you can use this PowerShell command:
Start-Process .\program.exe -RedirectStandardInput .\input.txt -NoNewWindow -Wait
It will run the program synchronously in same window. But I was not able to find out how to write result from this command to a variable when I run it in PowerShell script because it always writes data to the console.
EDIT:
To get output from Start-Process you can use option
-RedirectStandardOutput
for redirecting output to file and then read it from file:
Start-Process ".\program.exe" -RedirectStandardInput ".\input.txt" -RedirectStandardOutput ".\temp.txt" -NoNewWindow -Wait
$Result = Get-Content ".\temp.txt"
For output redirection you can use:
command > filename Redirect command output to a file (overwrite)
command >> filename APPEND into a file
command 2> filename Redirect Errors
Input redirection works in a different way. For example see this Cmdlet http://technet.microsoft.com/en-us/library/ee176843.aspx
Or you can do:
something like:
$proc = Start-Process "my.exe" "exe commandline arguments" -PassThru -wait -NoNewWindow -RedirectStandardError "path to error file" -redirectstandardinput "path to a file from where input comes"
if you want to know if process errored out, add following code:
$exitCode = $proc.get_ExitCode()
if ($exitCode){
$errItem = Get-Item "path to error file"
if ($errItem.length -gt 0){
$errors = Get-Content "path to error file" | Out-String
}
}
I find that this way I do have a better handle on execution of your scripts, when you need to handle external program/process. Otherwise I have encountered situations where script would hang out on some of external process errors.
You can also do this to have standard error and standard out go to the same place (note that in cmd, 2>&1 must be last):
get-childitem foo 2>&1 >log
Note that ">" is the same as "| out-file", and by default the encoding is unicode or utf 16. Also be careful with ">>", because it can mix ascii and unicode in the same text file. "| add-content" probably works better than ">>". "| set-content" might be preferable to ">".
There's 6 streams now. More info: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_redirection?view=powershell-5.1
I think all you can do is save to a text file and then read it into a variable after.
This is my code:
set-location [PATH]
$A = Start-Process -FilePath .\refresh.bat -Wait
set-location C:\
When executed in powershell, the system opens a Command prompt window and executes the bat file without issue. The problem is that the window closes and I cannot see if there was an error if it succeeds.
I want to keep the CMD window open.
I also tried at the end of the bat file:
:END
cmd /k
but no luck.
First, unless you specifically need to run the batch file in a new window, do not use Start-Process - use direct invocation instead, which is implicitly synchronous and allows you to capture or redirect output:
# Invoke the batch file synchronously (wait for it to exit)
# and capture its (standard) output in variable $A
# To print the batch file's output to the console instead, just use:
# .\refresh.bat
$A = .\refresh.bat
See this answer for more information.
Also note Start-Process never allows you to capture the invoked program's output directly (you can only redirect it to files with -RedirectStandardOutput and -RedirectStandardOutput); your specific command captures nothing[1] in $A; adding -PassThru does return something, but not the program's output, but a process-information object (System.Diagnostics.Process).
If you do need to run the batch file in a new window and want to keep that window open:
Start-Process -Wait -FilePath cmd -ArgumentList '/k .\refresh.bat'
Relying on positional parameter binding, the above can be simplified to:
Start-Process -Wait cmd '/k .\refresh.bat'
[1] Strictly speaking, $A is assigned the [System.Management.Automation.Internal.AutomationNull]::Value singleton, which in most contexts behaves like $null.
Thank you mklement0 with your post gave me the solution I wanted. This is how I solved it.
set-location [PATH]
$A = Start-Process -FilePath .\refresh.bat -Wait -NoNewWindow
set-location C:\
-NoNewWindow allowed me to run my batch in the same powershell window getting the feedback of the bat file. That way I have errors if any and success status if no errors.
Thanks!
I have a batch file in my PowerShell script that I want to run and write its output to a text file. I tried the following:
Start-Process C:\path\file.bat | Out-File C:\path\output.txt
I also tried using Tee-Object and Write-Output in place of Out-File, but nothing is being written to the text file.
You can use Start-Transcript and Stop-Transcript to let the PowerShell console write all console output to a text file.
Use it like this:
Start-Transcript -Path C:\temp\log.txt
&C:\path\file.bat
Stop-Transcript
See the documentation for more details.
If you want the output of the bat file, e.g. results of a build script, then I found the following worked best:
$Product = 'ProductName'
&C:\Path\$Product.bat | Out-File C:\Path\$Product-Output.txt
You just needed a couple more parameters:
Start-Process -FilePath 'C:\Path\File.bat' -RedirectStandardOutput 'C:\Path\Output.txt' -Wait -WindowStyle Hidden
-RedirectStandardOutput to send output to your text file, -Wait to wait for the batch file to complete and -WindowStyle Hidden to hide the window used for the batch file.
What is the required syntax to redirect standard input/output on Windows PowerShell?
On Unix, we use:
$./program <input.txt >output.txt
How do I execute the same task in PowerShell?
You can't hook a file directly to stdin, but you can still access stdin.
Get-Content input.txt | ./program > output.txt
If there is someone looking for 'Get-Content' alternative for large files (as me) you can use CMD in PowerShell:
cmd.exe /c ".\program < .\input.txt"
Or you can use this PowerShell command:
Start-Process .\program.exe -RedirectStandardInput .\input.txt -NoNewWindow -Wait
It will run the program synchronously in same window. But I was not able to find out how to write result from this command to a variable when I run it in PowerShell script because it always writes data to the console.
EDIT:
To get output from Start-Process you can use option
-RedirectStandardOutput
for redirecting output to file and then read it from file:
Start-Process ".\program.exe" -RedirectStandardInput ".\input.txt" -RedirectStandardOutput ".\temp.txt" -NoNewWindow -Wait
$Result = Get-Content ".\temp.txt"
For output redirection you can use:
command > filename Redirect command output to a file (overwrite)
command >> filename APPEND into a file
command 2> filename Redirect Errors
Input redirection works in a different way. For example see this Cmdlet http://technet.microsoft.com/en-us/library/ee176843.aspx
Or you can do:
something like:
$proc = Start-Process "my.exe" "exe commandline arguments" -PassThru -wait -NoNewWindow -RedirectStandardError "path to error file" -redirectstandardinput "path to a file from where input comes"
if you want to know if process errored out, add following code:
$exitCode = $proc.get_ExitCode()
if ($exitCode){
$errItem = Get-Item "path to error file"
if ($errItem.length -gt 0){
$errors = Get-Content "path to error file" | Out-String
}
}
I find that this way I do have a better handle on execution of your scripts, when you need to handle external program/process. Otherwise I have encountered situations where script would hang out on some of external process errors.
You can also do this to have standard error and standard out go to the same place (note that in cmd, 2>&1 must be last):
get-childitem foo 2>&1 >log
Note that ">" is the same as "| out-file", and by default the encoding is unicode or utf 16. Also be careful with ">>", because it can mix ascii and unicode in the same text file. "| add-content" probably works better than ">>". "| set-content" might be preferable to ">".
There's 6 streams now. More info: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_redirection?view=powershell-5.1
I think all you can do is save to a text file and then read it into a variable after.
I'm trying to write a Powershell script for work that will take the output of w32tm.exe \monitor and output everything into a text file. I'm trying the following code, however something is wrong since I'm able to create the text file, but nothing is being written in it:
#Take output of w32tm.exe and output into a plain text file
$file = "C:\Documents and Settings\a411882\My Documents\Scripts\timeScript.txt"
$executable = "w32tm.exe /monitor"
invoke-expression $executable | out-file -filepath $file
What am I doing wrong here? I'm new to Powershell so any advice would be greatly appreciated!
EDIT:
I can get all of the data to be displayed on the console when I run this, however I want the data to be written on the text file.
EDIT 2:
I managed to get everything to finally output. I wanted to try avoiding using the > operator to write out to the text file in hopes of learning a little more on the out-file cmdlet however doing a simple invoke-expression $executable > $file managed to get the job done. I still don't understand why the cmdlet wouldn't work properly.
Perhaps Invoke-Expression isn't properly sending output to stdout? I wouldn't expect that, but stranger things have happened. Try running w32tm.exe on its own:
$file = "C:\Documents and Settings\a411882\My Documents\Scripts\timeScript.txt"
w32tm.exe /monitor | Out-File -FilePath $file
You could use Start-Process which allows you to redirect also the error output:
Start-Process w32tm.exe -ArgumentList "/monitor" -Wait -RedirectStandardOutput "output.txt" -RedirectStandardError "error.txt"