Capture cmd error to file in CURRENT directory? - powershell

I have a PowerShell script that restarts servers listed in a file.
foreach ($server in $servers) {
try {
cmd /c "shutdown.exe /r /f /m \\$server /t 0 /d p:0:0 /c 'PlannedRestart'"
"$server`tRestarted"
"$server`tRestarted" | Out-File -FilePath .\RestartServers_LOG.txt -Append
} catch {
"$server`t" + cmd /c ">&1"
"$server`t" + cmd /c "dir > .\RestartServers_LOG.txt 2>&1"
}
}
I am trying to catch any errors that may occur from the CMD to output on the PowerShell session as well as to a file in the CURRENT directory, so in PowerShell that would be .\ but I don't know what the current directory specification is for CMD. Unless its the same thing?
Essentially, I am hoping to accomplish something similar to how we can capture the exception message of PowerShell like this:
"$server`t" + $_.Exception.Message.ToString().Split(":")[1].Replace("`n","") |
Out-File -FilePath .\RestartServers_LOG.txt -Append
but CMD doesn't deal with exceptions like PowerShell, and instead STDERR.
Note: I am using the command shutdown.exe because PowerShell doesn't have ability to restart "planned" or "unplanned" unfortunately.

Just do it the PoSh way:
$params = '/r', '/f',
'/t', '0',
'/d', 'p:0:0',
'/c', 'PlannedRestart'
$servers | ForEach-Object {
$output = & shutdown.exe /m "\\${_}" #params 2>&1
if ($LastExitCode -eq 0) {
"{0}`tRestarted" -f $_
} else {
"{0}`tRestart failed:`t{1}" -f $_, $output
}
} | Set-Content '.\RestartServers_LOG.txt'
External commands don't throw exceptions in the first place, so your try..catch wouldn't do anything anyway.

Related

xcopy show only when files have been copied

I have made a script using xcopy that generates a csv log file with the date to check that my copies are done.
I would like to be able to display only when it has copied files, and not display anything when there are 0 files copied.
How can I do this?
if I didn't make myself clear let me know
Thanks :)
Here is my code:
$Logfile = "C:\Users\Name\Documents\Power\"
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring and
}
function Get-TimeStamp
{
return "[{0:dd/MM/yy} {0:HH:mm:ss}]" -f (Get-Date)
}
xcopy /s /f /y /b /d C:\Users\Name\Documents\SourceBack C:\Users\Name\Documents\DestBack
>> xcopy.csv
Write-Output "last copied file(s) on $(Get-TimeStamp)" | Out-file
C:\Users\Name\Documents\Power\xcopy.csv -append
I think this is what you're looking for.
The output of xcopy is redirected to a temporary file. If that file contains only a single line (e.g. 0 File(s) copied), no further action is taken.
Otherwise, the output and an additional line with the timestamp is added to your CSV file.
At the end, regardless of the outcome, the temporary file is removed.
$Logfile = "C:\Users\Name\Documents\Power\xcopy.csv"
$TempFile = New-TemporaryFile
try {
Start-Process xcopy `
-ArgumentList '/s','/f','/y','/b','/d','C:\Users\Name\Documents\SourceBack','C:\Users\Name\Documents\DestBack' `
-RedirectStandardOutput $TempFile.FullName `
-Wait
$ProcessOutput = #(Get-Content $TempFile)
if ($ProcessOutput.Length -gt 1) {
($ProcessOutput + "last copied file(s) on $(Get-Date -Format '[dd/MM/yy HH:mm:ss]')") -join "`n" | Add-Content $Logfile
}
} finally {
Remove-Item $TempFile -Force
}
Remarks:
I removed the Get-TimeStamp function, as it was only used once and doesn't add a lot of benefit in that case.
I also removed the LogWrite function as it isn't used in your sample code, and it contains a syntax error (stray and).
You're appending a line to a CSV file (last copied file(s) …), which is bound to cause issues when you try to parse that file later on.
Update
You're never too old to learn, and it seems that Start-Process isn't necessary at all. For more information, see here and here.
$Logfile = "C:\Users\Name\Documents\Power\xcopy.csv"
[string[]] $Output = & xcopy /s /f /y /b /d C:\Users\Name\Documents\SourceBack C:\Users\Name\Documents\DestBack 2>&1
if ($ProcessOutput.Length -gt 1) {
($ProcessOutput + "last copied file(s) on $(Get-Date -Format '[dd/MM/yy HH:mm:ss]')") -join "`n" | Add-Content $Logfile
}

uninstall installed programs using powershell using partial match

I have some programs installed in Windows 10 and 7 that start with (in Uninstall Programs)
"Python info.data-5.332234" "Python delta.ind-5.332234" "Python
module.data-15.332234" "Python hatch.back-0.332234"
I've tried various scripts to try and uninstall using partial match with PowerShell, but none of them seems to uninstall the programs.
This is the latest script I've used and does not work... it uninstalls the registry entry but not actually remove the folder or the entry from Uninstall Programs
$remove = #('Python info.data', 'Python delta.ind', 'Python module.data', 'Python hatch.back')
foreach ($prog_name in $remove) {
Write "Uninstalling" % $prog_name
$uninstall32 = gci "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" | foreach { gp $_.PSPath } | ? { $_ -match $prog_name } | select UninstallString
if ($uninstall32) {
$uninstall32 = $uninstall32.UninstallString -Replace "msiexec.exe","" -Replace "/I","" -Replace "/X",""
$uninstall32 = $uninstall32.Trim()
Write "Uninstalling..."
Write $uninstall32
start-process "msiexec.exe" -arg "/X $uninstall32 /qn" -Wait}
}
The Problem is, that the variable $uninstall32 can contain more than 1 entry.
Add a $uninstall32.GetType() before you start msiexec to check if the variable may contains more than one string. If so, msiexec wont run because you are passing two GUIDs at once.
Use the Win32_Product WMI Class to get the GUIDs of the desired Applications.
$remove = #('Python info.data', 'Python delta.ind', 'Python module.data', 'Python hatch.back')
foreach ($prog_name in $remove) {
$uninstall32 = #(Get-WmiObject -Query "SELECT * FROM Win32_Product WHERE Name like '$prog_name%'")
foreach ($product in $uninstall32) {
Write "Uninstalling...`n$($product.Name)"
$exitCode = (Start-Process "msiexec.exe" -ArgumentList "/X $($product.IdentifyingNumber) /qn" -Wait -PassThru).ExitCode
Write "$($product.Name) return ExitCode: $exitCode"
}
}
Also, add the -PassThrue Switch at the Start-Process CMDLet and catch/output the ExitCode of each Uninstallation Process.
Make sure your Powershell/ISE is running with elevated privileges.

PowerShell in bat (multiline code as one line)

There is a working batch file with PowerShell code in one line:
powershell -Command "& {$cont = wget http://САЙТ.info/; $cont = $cont.Content; $FilePath = 'contentFronHtml.txt' -f $env:SystemDrive; $cont | Out-File -FilePath $FilePath -Append -Width 200;}"
I want to break the PowerShell code to get something like:
SET LONG_COMMAND="$cont = wget http://САЙТ.info/;"
"$cont = $cont.Content;"
"$FilePath = 'contentFronHtml.txt' -f $env:SystemDrive;$cont | Out-File -
FilePath; $FilePath -Append -Width 200;"
powershell -Command "& {LONG_COMMAND}"
How do I connect strings of PowerShell code?
This is how I would expect to do this sort of thing:
PowerShell -Command "first line up to the end of a command;"^
"next line up to the end of a command;"^
"another line up to the end of a command;"^
"last line"
You could also try this idea too:
Set "MyCmnd=first line up to the end of a command;"
Set "MyCmnd=%MyCmnd% next line up to the end of a command;"
Set "MyCmnd=%MyCmnd% another line up to the end of a command;"
Set "MyCmnd=%MyCmnd% last line"
Echo %MyCmnd%
I would save your PowerShell code as a .ps1 file and then run it from the command line:
powershell -File "C:\folder\test.ps1"
Then to break long commands up over multiple lines, follow the answer from: How to enter a multi-line command
This means you're separating your PowerShell code from your batch code, and no longer have inline PowerShell code within a batch file which can get hard to read.
Do something like this:
echo>%temp%\file.ps1 SET LONG_COMMAND="$cont = wget http://САЙТ.info/;"
echo>>%temp%\file.ps1 "$cont = $cont.Content;"
echo>>%temp%\file.ps1 "$FilePath = 'contentFronHtml.txt' -f
echo>>%temp%\file.ps1 $env:SystemDrive;$cont ^| Out-File -
echo>>%temp%\file.ps1 FilePath; $FilePath -Append -Width 200;"
echo>>%temp%\file.ps1 powershell -Command "& {LONG_COMMAND}"
powershell %temp%\file.ps1

How to execute powershell commands (not from ps1 file) from cmd.exe

I am trying to execute powershell if-else from cmd.
For example to check the number of files, that has "temp" in its name in D: drive,I used,
if(($i=ls D:\* | findstr /sinc:Temp).count -ne 0 ) {Write-Host $i}
This works fine from PS windows
But if want to do the same from cmd, How do i do it?
I tried
powershell -noexit if(($i=ls D:\* | findstr /sinc:Temp).count -ne 0 ) {Write-Host $i}
which did not work unfortunately.
Just put the command in double quotes:
powershell "if(($i=ls D:\* | findstr /sinc:Temp).count -ne 0 ) {Write-Host $i}"
I also think you don't need the -NoExit switch here. This switch prevents powershell from exiting after running the command. If you want to return back to cmd, remove this switch.
I know this doesn't answer how to run the command(others have covered it already), but why would you want to combine cmd and powershell, when both can do the job alone?
Ex powershell:
#Get all files on D: drive with temp in FILEname(doesn't check if a foldername was temp)
Get-ChildItem D:\ -Recurse -Filter *temp* | where { !$_.PSIsContainer } | foreach { $_.fullname }
#Check if fullpath includes "temp" (if folder in path includes temp, all files beneath are shown)
Get-ChildItem D:\ -Recurse | where { !$_.PSIsContainer -and $_.FullName -like "*temp*" } | foreach { $_.fullname }
Ex cmd:
#Get all files with "temp" in filename
dir d:\*temp* /s /a:-d /b
Just another solution for you problem without using powershell:
dir /b D:\*Temp* | find /v /c "::"
This will print just the number of files or folders on D: that have "Temp" in their names. The double-colon here is just a string that should not be in the output of dir /b, so find /v /c "::" counts all lines of the output of dir /b.
#utapyngo's double quote solution works.
Also another way for #utapyngo's another way to make it in cmd:
dir /b D:\* | find /c "*Temp*"
And to Bill: there should not be a opening double quote before & in your first code, I guess?
powershell -noexit "& "C:\........\run_script.ps1"
see these -- http://poshoholic.com/2007/09/27/invoking-a-powershell-script-from-cmdexe-or-start-run/
http://www.leeholmes.com/blog/2006/05/05/running-powershell-scripts-from-cmd-exe/
or for V2
Powershell.exe -File C:\.........\run_script.ps1

How to pipe all output of .exe execution in Powershell?

In Powershell I am running psftp.exe which is PuTTy's homepage. I am doing this:
$cmd = "psftp.exe"
$args = '"username#ssh"#ftp.domain.com -b psftp.txt';
$output = & $cmd $args
This works; and I am printing out $output. But it only catches some output in that variable (like "Remote working directory is [...]") and is throwing other output to an error type like this:
psftp.exe : Using username "username#ssh".
At C:\full_script.ps1:37 char:20
+ $output = & <<<< $cmd $args
+ CategoryInfo : NotSpecified: (Using username "username#ssh".:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
This "Using username ..." etc looks like a normal FTP message. How can I make sure all output gets put into $output?
The problem is some output is being sent to STDERR and redirection works differently in PowerShell than in CMD.EXE.
How to redirect output of console program to a file in PowerShell has a good description of the problem and a clever workaround.
Basically, call CMD with your executable as a parameter. Like this:
UPDATE
I fixed my code so it would actually work. :)
$args = '"username#ssh"#ftp.domain.com -b psftp.txt';
$output = cmd /c psftp.exe $args 2`>`&1
Give this a try
$output = [string] (& psftp.exe 'username#ssh#ftp.domain.com' -b psftp.txt 2>&1)
There is a PowerShell bug about 2>&1 making error records. The [string] cast works around it.
& "my.exe" | Out-Null #go nowhere
& "my.exe" | Out-Default # go to default destination (e.g. console)
& "my.exe" | Out-String # return a string
the piping will return it in real-time
& "my.exe" | %{
if ($_ -match 'OK')
{ Write-Host $_ -f Green }
else if ($_ -match 'FAIL|ERROR')
{ Write-Host $_ -f Red }
else
{ Write-Host $_ }
}
Note:
If the executed program returns anything other than a 0 exitcode, the piping will not work. You can force it to pipe with redirection operators such as 2>&1
& "my.exe" 2>&1 | Out-String
sources:
https://stackoverflow.com/a/7272390/254276
https://social.technet.microsoft.com/forums/windowsserver/en-US/b6691fba-0e92-4e9d-aec2-47f3d5a17419/start-process-and-redirect-output-to-powershell-window