How do I output each processing item to file? - powershell

Get-AppXPackage -AllUsers | Foreach {Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppXManifest.xml"} | Out-File -path $env:temp\AppXPackage.log
This performs the task that I want it to perform. However, the log file is blank.
I would like for the log file to contain both the errors, and log the individual operations.

I'm not familiar with that particular cmdlet, you could probably try redirecting other output streams (so not stdout) to the log file, and it might work. at the very worst you can do something like this:
Foreach-Object {
try {
Add-AppxPackage ... -ErrorAction Stop
"$_ done" > success.log
} catch {
$_ > errors.log
}
}

The Add-AppxPackagedoesn't return anything, neither does it support -PassThru argument. However, you could push the $_ object to the pipe from the foreach block:
Get-AppXPackage -AllUsers |
Foreach {Add-AppxPackage -DisableDevelopmentMode -Register
"$($_.InstallLocation)\AppXManifest.xml"; $_ } |
Out-File -path $env:temp\AppXPackage.log

As the other 2 answers mentioned, that cmdlet doesn't seem to give any output. Which leaves you with either manual logging or redirection. I just wanted to expand a bit on the other two to give you a couple options.
For redirection, you can do something like this:
.\my_script *>> myfile.log #appen all streams (output and errors) to a file
.\my_script >> output.log 2>> errors.log #send output to one file and error stream to another
For normal logging, take a look at $error, which will contain all the errors in your session. You could start your script with $error.clear(), manually log your output you want since the cmdlet doesn't have output and then end with $error | out-file errors.log

Related

Getting the error code from a command piped to `-replace` in powershell

I have a powershell script, where one of my commands' output is piped to -replace to make sure sensitive information isn't logged.
$(mycommand do stuff) -replace 'START_SECRET.*?END_SECRET' 'SECRET_ELIDED'
However, when mycommand fails, the error gets eaten by -replace and the script still succeeds where I would like it to fail. How can I get the error code from that command?
I'm open to solutions that don't look quite like this -replace, but ideally it would be something where mycommand output appears as it is generated, rather than all at once at the end like would happen if I saved the output, checked the error code, and grepped it later.
Your current example also finishes running before displaying any output. If you want to output as it comes you need to lose the $(...). Now each line will be sent down the pipe as they are generated
mycommand do stuff | ForEach-Object {$_ -replace 'START_SECRET.*?END_SECRET' 'SECRET_ELIDED' }
As for the error "being eaten", I'm not seeing this behavior, even when wrapping in $(...)
PS C:\temp> $(dir nonexistentpath) -replace '^', '----'
Get-ChildItem: Cannot find path 'C:\temp\nonexistentpath' because it does not exist.
even with native applications
PS C:\Program Files\GIMP 2\bin> $(.\bzip2.exe sklfjslf) -replace '^', '----'
bzip2.exe: Can't open input file sklfjslf: No such file or directory.
As others have mentioned it's not clear what your command is or even what you are doing with the output. If you are trying to capture/process the error records as well you will need to redirect them to the success stream using 2>&1
mycommand do stuff 2>&1 | ForEach-Object {$_ -replace 'START_SECRET.*?END_SECRET' 'SECRET_ELIDED' }
If the command you are running is a cmdlet you can also instead use -ErrorVariable to capture error records instead of redirecting them to the success stream.
PS C:\temp> dir nonexistentpath -ErrorVariable errors
Get-ChildItem: Cannot find path 'C:\temp\nonexistentpath' because it does not exist.
PS C:\temp> $errors
Get-ChildItem: Cannot find path 'C:\temp\nonexistentpath' because it does not exist.
$var = <#your stuff here#>
$var.Replace('START_SECRET.*?END_SECRET','SECRET_ELIDED')

How to output script to file?

I found a ping script which is useful, but I'd prefer to write the output to a txt or csv rather than using Write-Host to output to the PS console.
What is the best way of doing this?
You will need to change the Write-Host to Out-File, or, better still, Out-Default. Using Out-Default will allow you to pipe the output to other cmdlets, and therefore allow you to handle the output differently on different occasions, depending on your particular need at the moment.
Write-Host bypasses the PowerShell pipeline, and effectively removes any objects it uses from the pipeline, making them unavailable for assignment or use by other cmdlets.
References:
Get-Help Write-Host
Get-Help Out-File
Get-Help Out-Default
Try adding the stdOut to an Array. Then write results at the end.
#Define the array
$myOutput = #()
#Do something here"
$myOutput += $myStdOut
#Done with something
$myOutput | out-file -FilePath c:\myOutput.txt -Encoding utf8 -NoClobber
$cat c:\myOutput.txt

Tail a log file and if match trigger an actrion

I am currently using the following line of code to return the most recent line of a log via PowerShell
Get-Content -Path C:\folder\thisisalog.log -Tail 1 -Wait |
Where {$_ -match "Remote_http"}
This works correctly and will write to the console each time a log that matches "Remote_http" is logged.
However what I would like to do is run another script when this is returned.
So far I have tried to add to a variable and check if it is null with no luck and I have tried using if statements with no success.
Trying both of these the script runs indefinitely with no output to console or triigers.
I think it may be something to do with -Wait which is causing the issue.
just do it
Get-Content -Path C:\folder\thisisalog.log -Tail 1 -Wait | % {if ($_ -match "Remote_http") {write-host "run code here"}}
or directly into your where
Get-Content -Path C:\folder\thisisalog.log -Tail 1 -Wait | where {if ($_ -match "Remote_http") {write-host "run code here"}}

PowerShell to monitor a text file

I have a PowerShell script running on a remote machine. I have it writing data to a text file as it completes its work. Once it's done it writes a specific line.
I have this in the local PowerShell script to monitor that file on the remote machine:
Get-Content -Path $Path -Tail 0 -Wait
It is working great, but how do I tell it to stop monitoring once that specific line is reached?
I tired putting it into a do while loop, but it never releases to complete the do while.
Here is a link to a simpler version of what I am asking:
How to monitor a text file in realtime
The first answer is good, but I don't want to just look for a certain line. I want to write them all VIA Write-Host till that phrase then break from Get-Content and continue with the remaining parts of the script.
Here is what I finally ended up with. It is not pretty due to the way I exited the ForEach-Object.
Get-Content -Path $path -Tail 0 -wait | ForEach-Object{if($_ -match $word){write-host "- $_" ;cjklnsrvf } else {write-host "- $_"} }
I used a Try and Catch for the cjklnsrvf in the if statement above. This is done because ForEach-Object cannot use the break or continue statements. It seems that when piping a ForEach loop it is turned into (alias) the ForEach-Object cmdlet. The ForEach-Object cmdlet doesn't use the break and continue commands like a foreach loop.
If you use a break in a ForEach-Object it will immediately exit the whole script. There was one guy on one site that brought up loop death by garbage, and it indeed does work here as well.
UPDATE: Here is what I finally ended up with. It is not pretty due to the way I exited the foreach-object.
Get-Content -Path $path -Tail 0 -wait | ForEach-Object{if($_ -match $word){write-host "- $_" ;cjklnsrvf } else {write-host "- $_"} }
I used a Try and Catch for the cjklnsrvf in the if statement above. This is done because ForEach-Object cannot use the break or continue statements. It seems that when piping a ForEach loop it is turned into(alias) the ForEach-Object cmdlet. The ForEach-Object cmdlet doesn't use the break and continue commands like a foreach loop. If you use a break in a ForEach-Object it will immediately exit the whole script. There was one guy on one site that brought up loop death by garbage and it indeed does work here as well.
Get-Content $path -Tail 0 -Wait | foreach { if ($_ -eq "Specific Line") { Write-Output $_ ; break } }
Or just the break, obviously, if you have no use of the output.
Do {
$content = get-content $path -tail 0 -ea 00 | where {$_ -like "*string found*"}
Sleep -milliseconds 1000
} until($content)
Invoke-Command -ComputerName xxx -Credential $cred -ScriptBlock {if(test-path 'c:\1.txt'){cat 'c:\1.txt'}else{'file not exist'}}

How to redirect the output of a PowerShell to a file during its execution

I have a PowerShell script for which I would like to redirect the output to a file. The problem is that I cannot change the way this script is called. So I cannot do:
.\MyScript.ps1 > output.txt
How do I redirect the output of a PowerShell script during its execution?
Maybe Start-Transcript would work for you. First stop it if it's already running, then start it, and stop it when done.
$ErrorActionPreference="SilentlyContinue"
Stop-Transcript | out-null
$ErrorActionPreference = "Continue"
Start-Transcript -path C:\output.txt -append
# Do some stuff
Stop-Transcript
You can also have this running while working on stuff and have it saving your command line sessions for later reference.
If you want to completely suppress the error when attempting to stop a transcript that is not transcribing, you could do this:
$ErrorActionPreference="SilentlyContinue"
Stop-Transcript | out-null
$ErrorActionPreference = "Continue" # or "Stop"
Microsoft has announced on Powershell's Connections web site (2012-02-15 at 4:40 PM) that in version 3.0 they have extended the redirection as a solution to this problem.
In PowerShell 3.0, we've extended output redirection to include the following streams:
Pipeline (1)
Error (2)
Warning (3)
Verbose (4)
Debug (5)
All (*)
We still use the same operators
> Redirect to a file and replace contents
>> Redirect to a file and append to existing content
>&1 Merge with pipeline output
See the "about_Redirection" help article for details and examples.
help about_Redirection
Use:
Write "Stuff to write" | Out-File Outputfile.txt -Append
I take it you can modify MyScript.ps1. Then try to change it like so:
$(
Here is your current script
) *>&1 > output.txt
I just tried this with PowerShell 3. You can use all the redirect options as in Nathan Hartley's answer.
powershell ".\MyScript.ps1" > test.log
If you want a straight redirect of all output to a file, try using *>>:
# You'll receive standard output for the first command, and an error from the second command.
mkdir c:\temp -force *>> c:\my.log ;
mkdir c:\temp *>> c:\my.log ;
Since this is a straight redirect to file, it won't output to the console (often helpful). If you desire the console output, combined all output with *&>1, and then pipe with Tee-Object:
mkdir c:\temp -force *>&1 | Tee-Object -Append -FilePath c:\my.log ;
mkdir c:\temp *>&1 | Tee-Object -Append -FilePath c:\my.log ;
# Shorter aliased version
mkdir c:\temp *>&1 | tee -Append c:\my.log ;
I believe these techniques are supported in PowerShell 3.0 or later; I'm testing on PowerShell 5.0.
One possible solution, if your situation allows it:
Rename MyScript.ps1 to TheRealMyScript.ps1
Create a new MyScript.ps1 that looks like:
.\TheRealMyScript.ps1 > output.txt
You might want to take a look at the cmdlet Tee-Object. You can pipe output to Tee and it will write to the pipeline and also to a file
If you want to do it from the command line and not built into the script itself, use:
.\myscript.ps1 | Out-File c:\output.csv
To embed this in your script, you can do it like this:
Write-Output $server.name | Out-File '(Your Path)\Servers.txt' -Append
That should do the trick.