How can I use clip and display output at the same time?
Every time if I put | clip at the of the end line, the output is copied to clipboard, but not displayed in the console window.
Get-Date | clip
Use common parameter -ov (-OutVariable) to also capture Get-Date's output in a variable, then output that variable:
Get-Date -ov output | clip; $output
If the command you're invoking is not a cmdlet or advanced function/script and therefore doesn't support -OutVariable, you can use this technique instead:
($output = Get-Date) | clip; $output
This relies on the fact that enclosing a variable assignment in (...) passes the assigned value through.
You can package this functionality with the help of a custom function:
Function Write-OutputAndClip { $Input | Write-Output -ov output | clip; $output }
If you also define an alias for it, say clipecho ...
Set-Alias clipecho Write-OutputAndClip
... you can invoke it succinctly as:
Get-Date | clipecho # copies output to the clipboard *and* echoes it.
After looking into this a bit I think the only way to do what you're asking is to do it in two separate lines.
$value = Get-Date
Write-host $value
$value | clip
One line would look like this $value = Get-Date;$value;$value|clip
Powershell really wants to redirect anything from write-host to the console. And clip doesn't want to pass anything further down the pipeline...
Related
I have the following powershell code I wrote that I am trying to optimize even further. I essentially need to get this code block down to under 259 characters. It's currently at 319, this is a challenge I know.
Using a mix of regex/wildcard matching/code golfing I think it is possible. But this is something I'm still learning.
This function will convert each character in the z file into its capslock and numlock value, then use send keys to in a very simplified way of explaining use the lights as a form of Morse code but in binary format instead.
I need this to run from the run box hence the character limit.
Why am I doing this? I'm passing data through the channel that controls the lock keys on the keyboard.
powershell "foreach($b in $(cat $env:tmp\z -En by)){foreach($a in 0x80,
0x40,0x20,0x10,0x08,0x04,0x02,0x01){if($b-band$a){$o+='%{NUMLOCK}'}else
{$o+='%{CAPSLOCK}'}}};$o+='%{SCROLLLOCK}';echo $o >$env:tmp\z;$f=(cat $env:tmp\z);Add-Type -A System.Windows.Forms;[System.Windows.Forms.SendKeys]::SendWait($f);rm $env:tmp\z"
I'm at work so I haven't gotten to test it yet but I think I got it down to 268 characters. And again it's needs to be at 259 or less.
powershell "$d='$env:tmp\z'%($b in $(cat -En by)){%($a in 0x80,
0x40,0x20,0x10,0x08,0x04,0x02,0x01){if($b-band$a){$o+='%{NUMLOCK}'}else
{$o+='%{CAPSLOCK}'}}};$o+='%{SCROLLLOCK}';echo $o >$d;$o=(cat $d);Add-Type -A *m.W*s.F*s;[*m.W*s.F*s.SendKeys]::SendWait($o);rm $d"
You'll know your solution works if you have a file in the tmp folder called "z" with no extention, and after running your code the lights for your lock keys should look like a rave.
Just to post the entirety of the code in readable format, here's the result with Mclayton's suggestion included:
# Gather the content from the file
# The use of (...) around the variable assignment lets the value pass through evaluating it as an expression.
Get-Content ($d=".\desktop\Abe.txt") -Encoding byte |
ForEach-Object -Process {
# Assign the current object in the pipeline to $b.
# This will allow the use of another Foreach-Object (%) for shorter code.
$b = $_;
128,64,32,16,8,4,2,1 |
Foreach-Object -Process {
# Append the results to $o as a concatenated string.
# Given a hashtable with the wanted values, you can access the value by providing the name in []'s.
# Since the bitwise operator -BAND only operates "properly" on two equal-length binary representations and if statement is needed.
# The if statement will return values 1/0 in accordance with -BAND
$o += "%{$(#{1="NUM";0="CAPS"}[$( if ($_-band$b) { 1 } else { 0 } )])LOCK}%{SCROLLLOCK}"
}
};
# Concatenate again to $o, while assigning to $f then outputting to $d.
($f = $o + "%{SCROLLLOCK}") | Out-File -FilePath $d;
Add-Type -AssemblyName 'System.Windows.Forms';
[System.Windows.Forms.SendKeys]::SendWait($f);
Remove-Item -Path $d
...and in shorter form:
gc ($d="$env:TEMP\z") -en by|%{$b=$_;128,64,32,16,8,4,2,1|%{$o+="%{$(#{1="NUM";0="CAPS"}[$(if($_-band$b){1}else{0})])LOCK}%{SCROLLLOCK}"}};($f=$o+"%{SCROLLLOCK}")>$d;Add-Type -A *m.W*s.F*s;[System.Windows.Forms.SendKeys]::SendWait($f);rm $d
I added comments in the code just for future readers trying to follow along.
I trying to write a script, which show iis pools state with a different color.
And I can't understand why script coloring in one color all strings when I use echo.
Here script:
$pools = invoke-command -session $session -scriptblock {Import-Module WebAdministration; Get-ChildItem IIS:\AppPools | Where {$_.Name -like "*abc*"}}
$poolsShow = $pools | Select-Object -Property name, state
$poolsShow | ForEach-Object {
if($_.state -eq "Started") {
$Host.UI.RawUI.ForegroundColor = "Green";
echo $_;
[Console]::ResetColor();
}
if($_.state -eq "Stopped") {
$Host.UI.RawUI.ForegroundColor = "Red";
echo $_;
[Console]::ResetColor();
}
}
It is work if I go through the $pools, but if I select name and state via Select-Object - all strings are coloring in the color of the last service.
I have tried via Write-Host - and it's worked, but I didn't find a way, how to format output in one table with a headers only at first line and with the same width in every string.
You can take a similar approach as the one proposed in this answer, the only difference would be that the ANSI Escape Sequences are prepended to the property values of the objects created by Select-Object. Helpful answer provided by #mklement0 in the same question provides more details on this.
function status {
$ansi = switch($args[0]) {
Stopped { "$([char] 27)[91m" }
Started { "$([char] 27)[32m" }
}
$ansi + $args[0]
}
Invoke-Command -Session $session -ScriptBlock {
Import-Module WebAdministration
Get-ChildItem IIS:\AppPools | Where-Object { $_.Name -like "*abc*" }
} | Select-Object Name, #{ N='Status'; E={ status $_.State }}
A demo using custom objects:
0..5 | ForEach-Object {
[pscustomobject]#{
Name = "Test $_"
Status = ('Started', 'Stopped')[$_ % 2]
}
} | Select-Object Name, #{ N='Status'; E={ status $_.Status }}
To complement Santiago's helpful answer:
As for what you tried:
echo in PowerShell is a built-in alias for Write-Output, which does not print directly to the console - instead, it prints to the success output stream.
If the success output stream isn't captured or redirected in a given command, it is eventually printed to the console, after undergoing for-display formatting by PowerShell's formatting system.
Because your output objects have 4 or fewer properties, PowerShell applies tabular formatting by default; that is, the Format-Table cmdlet is implicitly used, which has a perhaps surprising implication:
So as to allow determining suitable column widths for the table, a 300-millisecond delay is introduced during which the objects are internally cached and analyzed.
While this behavior is helpful in principle, it has surprising side effects, notably in that direct-to-host output and output from other streams then can appear out of order; a simple example: [pscustomobject] #{ foo = 1 }; Write-Host 'Why am I printing first?? - see this answer for background information.
Therefore, the formatted table's rows only started printing after that delay, so your attempt to control their color one by one with ForEach-Object was ineffective.
As an aside: In PowerShell (Core) 7.2+ there's an additional consideration: formatted output now applies its own coloring by default, as controlled by the .OutputRendering property of the $PSStyle preference variable.
Santiago's answer bypasses this problem by using a calculated property to color individual property values rather than trying to control the coloring of the already-formatted representation of the object.
If you want a prepackaged, general-purpose solution, you can use the Out-HostColored function from this Gist (authored by me), which in your case would make the solution as simple as piping your objects to Out-HostColored.ps1 #{ Started = 'Green'; Stopped = 'Red' }:
# Download and define function `Out-HostColored` on demand (will prompt).
# To be safe, inspect the source code at the specified URL first.
if (-not (Get-Command -ErrorAction Ignore Out-HostColored1)) {
$gistUrl = 'https://gist.github.com/mklement0/243ea8297e7db0e1c03a67ce4b1e765d/raw/Out-HostColored.ps1'
if ((Read-Host "`n====`n OK to download and define function ``Out-HostColored```n from Gist ${gistUrl}?`n=====`n(y/n)?").Trim() -notin 'y', 'yes') { Write-Warning 'Aborted.'; exit 2 }
Invoke-RestMethod $gistUrl | Invoke-Expression 3>$null
if (-not ${function:Out-HostColored}) { exit 2 }
}
# Emit sample objects and color parts of their formatted representation
# based on regex patterns.
0..5 | ForEach-Object {
[pscustomobject]#{
Name = "Test $_"
State = ('Started', 'Stopped')[$_ % 2]
}
} |
Out-HostColored.ps1 #{ Started = 'Green'; Stopped = 'Red' }
Output:
Add -WholeLine if you want to color matching lines in full.
The hashtable maps search text patterns to colors.
Whenever a pattern is found in the formatted representations of the input objects, it is colored using the specified color.
Note that this means that finding what to color is purely based on string parsing, not on OOP features (such as checking specific properties).
Note that the hashtable keys are regexes by default, unless you also specify
-SimpleMatch.
Thus you could easily make the above more robust, if needed, such as replacing Started = with '\bStarted\b' = in order to only match full words.
I am importing a CSV file with two records per line, "Name" and "Path".
$softwareList = Import-Csv C:\Scripts\NEW_INSTALLER\softwareList.csv
$count = 0..($softwareList.count -1)
foreach($i in $count){
Write-Host $softwareList[$i].Name,$softwareList[$i].Path
}
What I am trying to do is dynamically assign the Name and Path of each record to a WPFCheckbox variable based on the $i variable. The names for these checkboxes are named something such as WPFCheckbox0, WPFCheckbox1, WPFCheckbox2 and so on. These objects have two properties I planned on using, "Command" to store the $SoftwareList[$i].path and "Content" to store the $SoftwareList[$i].Name
I cannot think of a way to properly loop through these variables and assign the properties from the CSV to the properties on their respective WPFCheckboxes.
Any suggestions would be very appreciated.
Invoke-Expression is one way, though note Mathias' commented concerns on the overall approach.
Within your foreach loop, you can do something like:
invoke-expression "`$WPFCheckbox$i`.Command = $($SoftwareList[$i].Path)"
invoke-expression "`$WPFCheckbox$i`.Content= $($SoftwareList[$i].Name)"
The back-tick ` just before the $WPFCheckBox prevents what would be an undefined variable from being immediately evaluated (before the expression is invoked), but the $I is. This gives you a string with your $WPFCheckbox1, to which you then append the property names and values. The $SoftwareList values are immediately processed into the raw string.
The Invoke-Expression then evaluates and executes the entire string as if it were a regular statement.
Here's a stand-alone code snippet to play with:
1..3 |% {
invoke-expression "`$MyVariable$_` = New-Object PSObject"
invoke-expression "`$MyVariable$_` | add-member -NotePropertyName Command -NotePropertyValue [String]::Empty"
invoke-expression "`$MyVariable$_`.Command = 'Path #$_'"
}
$MyVariable1 | Out-String
$MyVariable2 | Out-String
$MyVariable3 | Out-String
As a side note (since I can't comment yet on your original question,) creating an array just to act as iterator through the lines of the file is really inefficient. There are definitely better ways to do that.
I am currently using the below PS script to check if the currents months MS patches are installed on the system. The script is set to check the $env:COMPUTERNAME.mbsa and the Patch_NA.txt file and send the result to the $env:COMPUTERNAME.csv file.
I now need to modify this script to also pull information from other POS devices in the same location (C:\Users\Cambridge\SecurityScans) and send the results to the $env:COMPUTERNAME.csv file.
The POS devices are listed like this:
172.26.210.1.mbsa
172.26.210.2.mbsa
172.26.210.3.mbsa
and so forth.
The IP range at all our locations (last octet) is 1 - 60. Any ideas on how I can set this up?
Script:
$logname = "C:\temp\PatchVerify\$env:COMPUTERNAME.csv"
[xml]$x=type "C:\Users\Cambridge\SecurityScans\$env:COMPUTERNAME.mbsa"
#This list is created based on a text file that is provided.
$montlyPatches = type "C:\Temp\PatchVerify\Patches_NA.txt"|
foreach{if ($_ -mat"-KB(? <KB>\d+)"){$matches.KB}}
$patchesNotInstalled=$x.SecScan.check | where {$_.id -eq 500} |foreach{`
$_.detail.updatedata|where {$_.isinstalled -eq "false"}}|Select -expandProperty KBID
$patchesInstalled =$x.SecScan.check | where {$_.id -eq 500} |foreach{`
$_.detail.updatedata|where {$_.isinstalled -eq "true"}}|Select -expandProperty KBID
"Store,Patch,Present"> $logname
$store = "$env:COMPUTERNAME"
foreach ($patch in $montlyPatches)
{
$result = "Unknown"
if ( $patchesInstalled -contains $patch)
{
$result = "YES"
}
if ( $patchesNotInstalled -contains $patch)
{
$result = "NO"
}
"$store,KB$($patch),$result" >>$logname
}
You can find lots of information on creating functions on the web, but a simple example would be:
Function Check-Patches{
Param($FileName)
$logname = "C:\temp\PatchVerify\$FileName.csv"
[xml]$x=type "C:\Users\Cambridge\SecurityScans\$FileName.mbsa"
The rest of your existing code goes here...
}
Check-Patches "$env:ComputerName"
For($i=1;$i -le 60;$i++){
Check-Patches "172.26.210.$i"
}
If you need me to break down anything in that let me know and I'll go into further explanation, but from what you already have it looks like you have a decent grasp on PowerShell theory and just needed to know what resources are available.
Edit: I updated my example to better fit your script, having it accept a file name, and then applying that file name to the $logname and $x variables within the function.
The break down...
First we declare that we are creating a Function using the Function keyword. Following that is the name of the function that you will use later to call it, and an opening curly brace to start the scriptblock that makes up the actual function.
Next is the Param line, which in this case is very simple only declaring one variable as input. This could alternatively be done as Function Check-Patches ($FileName){ but when you start getting into more advanced functions that only gets confusing, so my recommendation is to stick with putting the parameters inside the function's scriptblock. This is the first thing you want inside of your function in most cases, excluding any Help that you would write up for the function.
Then we have updated lines for $logname and [xml]$x that use the $FileName that the function gets as input.
After that comes all of your code that parses the patch logs, and outputs to your CSV, and the closing curly brace that ends the scriptblock, and the function.
Then we call it for the ComputerName, and run a For loop. The For loop runs everything between 1 and 60, and for each loop it uses that number as the last octet of the file name to feed into the function and check those files.
A few comments on the rest of your code. $monthlypatches = could be changed to = type | ?{$_ -match "-KB(? <KB>\d+)"}|%{$matches.KB} so that the results are filtered before the ForEach loop, which could cut down on some time.
On the $patchesInstalled and $patchesNotInstalled lines you don't need the backtick at the end of that line. You can naturally have a linebreak after the beginning of the scriptblock for a ForEach loop. Having it there can be hard to see later if the script breaks, and if there is anything after it (including a space) the script can break and throw errors that are hard to track down.
Lastly, you loop through $x twice, and then $monthlyPatches once, and do a lot of individual writes to the log file. I would suggest creating an array, filling it with custom objects that have 3 properties (Store, Patch, and Present), and then outputting that at the end of the function. That changes things a little bit, but then your function outputs an object, which you could pipe to Export-CSV, or maybe later you could want it to do something else, but at least then you'd have it. To do that I'd run $x through a switch to see if things are installed, then I'd flush out the array by setting all of the monthlypatches that aren't already in that array to Unknown. That would go something like:
Function Check-Patches{
Param($FileName)
$logname = "C:\temp\PatchVerify\$FileName.csv"
[xml]$x=type "C:\Users\Cambridge\SecurityScans\$FileName.mbsa"
$PatchStatus = #()
#This list is created based on a text file that is provided.
$monthlyPatches = GC "C:\Temp\PatchVerify\Patches_NA.txt"|?{$_ -match "-KB(? <KB>\d+)"} | %{$matches.KB}
#Create objects for all the patches in the updatelog that were in the monthly list.
Switch($x.SecScan.Check|?{$_.KBID -in $monthlyPatches -and $_.id -eq 500}){
{$_.detail.updatedata.isinstalled -eq "true"}{$PatchStatus+=[PSCustomObject][Ordered]#{Store=$FileName;Patch=$_.KBID;Present="YES"};Continue}
{$_.detail.updatedata.isinstalled -eq "false"}{$PatchStatus+=[PSCustomObject][Ordered]#{Store=$FileName;Patch=$_.KBID;Present="NO"};Continue}
}
#Populate all of the monthly patches that weren't found on the machine as installed or failed
$monthlyPatches | ?{$_ -notin $PatchStatus.Patch} | %{$PatchStatus += [PSCustomObject][Ordered]#{Store=$FileName;Patch=$_;Present="Unknown"}}
#Output results
$PatchStatus
}
#Check patches on current computer
Check-Patches "$env:ComputerName"|Export-Csv "C:\temp\PatchVerify\$env:ComputerName.csv" -NoTypeInformation
#Check patches on POS Devices
For($i=1;$i -le 60;$i++){
Check-Patches "172.26.210.$i"|Export-Csv "C:\temp\PatchVerify\172.26.210.$i.csv" -NoTypeInformation
}
I have many oracle forms in one folder and I want to compile those forms through frmcmp command in powershell script.
I have written a powershell script which is following
$module="module="
get-childitem "C:\forms\fortest" -recurse |
where { $_.extension -eq ".fmb" } |
foreach {
C:\Oracle\Middleware\Oracle_FRHome1\BIN\frmcmp $module $_.FullName userid=xyz/xyz#xyz Output_File=C:\forms\11\common\fmx\$_.BaseName+'.fmx'
}
but this one is not working. i am new in powershell.
but when I try to compile a single form through command prompt its working like following.
frmcmp module=C:\forms\src\xyz.fmb userid=xyz/xyz#xyz Output_File=C:\forms\11\common\fmx\xyz.fmx
When you want to use variables inside a string in PowerShell you have different options. To start with, you will always need to use " as opposed to ' to wrap the string, if you want variables in your string.
$myVariable = "MyPropertyValue"
Write-Host "The variable has the value $MyVariable"
The above code would yield the output:
The variable has the value MyPropertyValue
If you want to use a property of a variable (or any expression) and insert it into the string, you need to wrap it in the string with $(expression goes here), e.g.
$MyVariable = New-Object PSObject -Property #{ MyPropertyName = 'MyPropertyValue' }
# The following will fail getting the property since it will only consider
# the variable name as code, not the dot or the property name. It will
# therefore ToString the object and append the literal string .MyPropertyName
Write-Host "Failed property value retrieval: $MyVariable.MyPropertyName"
# This will succeed, since it's wrapped as code.
Write-Host "Successful property value retrieval: $($MyVariable.MyPropertyName)"
# You can have any code in those wrappers, for example math.
Write-Host "Maths calculating: 3 * 27 = $( 3 * 27 )"
The above code would yield the following output:
Failed property value retrieval: #{MyPropertyName=MyPropertyValue}.MyPropertyName
Successful property value retrieval: MyPropertyValue
Maths calculating: 3 * 27 = 81
I generally try to use the Start-Process cmdlet when I start processes in PowerShell, since it gives me the possibility of additional control over the process started. This means that you could use something similar to the following.
Get-ChildItem "C:\forms\fortest" -Filter "*.fmb" -recurse | Foreach {
$FormPath = $_.FullName
$ResultingFileName = $_.BaseName
Start-Process -FilePath "C:\Oracle\Middleware\Oracle_FRHome1\BIN\frmcmp.exe" -ArgumentList "module=$FormPath", "userid=xyz/xyz#xyz", "Output_File=C:\forms\11\common\fmx\$ResultingFileName.fmx"
}
You could also add the -Wait parameter to the Start-Process command, if you want to wait with compilation of the next item until the current compilation has completed.