Working on a script to delete all but the highest "Copy" printer (Microsoft and its infinite helpfulness creates "Copy" printers every time one of my remote users unplugs/plugs in a printer) on Windows 7 PCs.
I have two different printer names which get many "copies" made due to this problem. In one case, it's easy because I want to delete all of the "Copy" printers, but leave the original printer - the one that doesn't have "Copy" in its name. I do that by first clearing all print jobs (will not delete the printer if there's an existing job sitting in the queue), then delete all the "POS Lexmark (Copy)" printers -
Get-WmiObject Win32_Printer | ForEach-Object {$_.CancelAllJobs()}
Get-WmiObject Win32_Printer -Filter "name LIKE '%POS Lexmark (Copy%'" | ForEach-Object {$_.Delete()}
Works great. In the second case, I want to keep the highest "Copy" number printer - i.e. if there are 12 "Copy" printers, I want to keep the "Lexmark Universal PS3 (Copy 12)" printer, but delete all the rest. I do have a natural sort function line:
$ToNatural = { [regex]::Replace($_, '\d+', { $args[0].Value.PadLeft(20) }) }
Which I can use to sort all the "Copy" printers in this case, but this
Get-WmiObject Win32_Printer -Filter "name LIKE '%PS3 (Copy%'" | Sort-Object $ToNatural | Select-Object | ForEach-Object {$_.Delete()}
won't work because I still need to keep whatever that highest number printer is after the sort. I'm a Powershell newbie, so any help would be appreciated since a Google search has not turned anything up for me yet.
Thank you
Could you place your sorted results into a variable, and select-object all but the last (highest) result?
$ToNatural = { [regex]::Replace($_, '\d+', { $args[0].Value.PadLeft(20) }) }
$sorted = Get-WmiObject Win32_Printer -Filter "name LIKE '%PS3 (Copy%'" | Sort-Object $ToNatural
$sorted | Select-Object -First ($sorted.Count-1) | ForEach-Object {$_.Delete()}
Related
bit of a noob question.
I have the following cmd which grabs the server members of a group which I can copy into a text list. however as the group changes I need to modify the text list manually.
Get-AdGroupMember -identity "Reboot 7pm" | Sort-Object | select name
when I have that output in a text list, the following works fine.
$listpath = "C:\Scripts\servers.txt"
[System.Collections.ArrayList]$list = #(Get-content $listpath)
foreach($ComputerName in $list)
{
Get-Uptime -ComputerName $ComputerName
I want to know if it is possible to use a variable that I can use again in a for each loop. I've tried to do so, however the format of the list is not the same when is goes into a variable, thus the function (get-uptime) against the server doesn't work, anyone know what I can do to format the output so I only get the server name?
EG.
$WSUS_7PM = Get-AdGroupMember -identity "Reboot 7pm" | Sort-Object | select name
PS C:\Windows\system32> $WSUS_7PM
name
----
AXXXXX003
BXXXXX005
CXXXXX006
DXXXXX007
PS C:\Windows\system32> foreach($Name in $WSUS_7PM) {Write-Host $Name}
#{name=AXXXXX003}
#{name=BXXXXX005}
#{name=CXXXXX006}
#{name=DXXXXX007}
so when I run the same cmds as above modified with the variable instead of the text list, I get the following as the server name is obviously incorrect.
$listpath = $WSUS_7PM
[System.Collections.ArrayList]$list = #(Get-content $WSUS_7PM)
foreach($ComputerName in $list)
{
Get-Uptime -ComputerName $ComputerName
WARNING: Unable to connect to #{name=AXXXXX003}
WARNING: Unable to connect to #{name=BXXXXX005}
I hope that makes sense to someone, appreciate the help in understanding what the difference is in the object output.
Thanks
Alzoo
When you use Select-Object name you are creating a list of objects with a name property. You can either expand it ahead of time
$WSUS_7PM = Get-AdGroupMember -identity "Reboot 7pm" | Sort-Object | Select-Object -ExpandProperty name
or reference the name property later
foreach($Name in $WSUS_7PM.name) {Write-Host $Name}
I am attempting to write a script to copy some information from one computer to another. The first computer has a name similar to "SERVERxx" where xx is the site number. There are multiple computers on the network with names similar to "TERMINALxx_yy", where xx is the site number and yy is the number of the TERMINALS. What I would like to do is find the lowest numbered of the "TERMINALS" (as 1 may not always be the lowest). There is an environment variable on the SERVER named TERMSTR that is equal to "TERMINALxx_", as well as an environment variable named NUMTERMS that is the number of TERMINALS at the site.
The most I've been able to figure out is:
net view | Select-string $termstr
But that just gives the table output.
I'm figuring I need to first have NET VIEW give just the computer names, then sort in descending order and select the first one.
Thanks
if you absolutely MUST use net view, then this will give you the highest lowest system number with the header, footer, line padding, and non-digits removed. it does not add any leading zeros to the resulting number, tho. [grin]
net view |
# skip the 3 header lines
Select-Object -Skip 3 |
# skip the footer lines
Select-Object -SkipLast 2 |
# trim away the "net view" line padding
# remove the non-digits
ForEach-Object {
[int]($_.Trim() -replace '[^0-9]')
} |
Sort-Object |
Select-Object -First 1
Here is a starting point of code you can use.
$Servers = Get-ADComputer -Filter 'Name -like "SERVER*"'
foreach($Server in $Servers | Sort-Object){
$N = $Server.name.substring(($Server.name.length)-2)
$Terminals = Get-ADComputer -Filter 'Name -like "TERMINAL$($N)*"'
$count = $Terminals.Count
$Terminal = $Terminals | Sort-Object
$TerminalZero = $Terminal[0].Name
Write-Host "Terminal Name: $TerminalZero"
$COMMAND = {
Write-Host [System.Environment]::GetEnvironmentVariable("termstr","Machine")
[System.Environment]::SetEnvironmentVariable("TERMSTR", $TerminalZero, "Machine")
[System.Environment]::SetEnvironmentVariable("NUMTERMS", $Count, "Machine")
}
Invoke-Command -ComputerName $Server -ScriptBlock { $COMMAND }
}
}
I'm trying to write a query of the replication status of our VMs. I would like to be more selective in what I'm looking for, however.
I can run this:
PS C:\Users\hc> Get-VMReplication -computername servername
and it'll return this:
Image 1
I'd like it to return the line in the list when there is a match, or nothing when there isn't. Ive so far gotten it to select an item from the list by writing it as this:
PS C:\Users\hc> ((Get-VMReplication -computername servername | select-string -inputobject {$_.Health} -pattern “Normal”) -like “Normal”)
but it unfortunately only displays a list of Normal:
Image 2
Ultimately I would like it it to list the column headings and the entire row if possible but I'm unsure as to where to go next. (note that I've used the "Normal" pattern just so it would create entries in this list. The final product will look for "Warning" and "Critical")
Don't use Select-String, use Where instead.
Get-VMReplication -computername servername | Where{ $_.Health -eq "Normal"}
Or later down the road it would look like:
Get-VMReplication -computername servername | Where{ $_.Health -eq "Warning" -or $_.Health -eq "Critical"}
I have a small script to get all user mobile devices info from exchange 2013 server.
Get-Mailbox -ResultSize Unlimited |
ForEach {Get-MobileDeviceStatistics -Mailbox:$_.Identity} |
Select-Object #{label="User" ; expression={$_.Identity}},DeviceOS, lastsuccesssync
I just want to get an exact user name instead of a path in AD. How can I do it in expression={?}
Here is another script to do it, it gives me the user name, but all devices belongs to user are not in separated lines, they all in one line...
$EASMailboxes = Get-CASMailbox -Filter {HasActiveSyncDevicePartnership -eq $True -and DisplayName -notlike "CAS_{*"} | Get-Mailbox
$EASMailboxes | Select-Object DisplayName, PrimarySMTPAddress, #{Name="Mobile Devices";Expression={(Get-MobileDeviceStatistics -Mailbox $_.Identity).DeviceOS}} |
Out-GridView
I don't have the environment to test this but is this not what you are looking for ?
Get-Mailbox -ResultSize Unlimited | ForEach {
$user = $_.SamAccountName
Get-MobileDeviceStatistics -Mailbox:$_.Identity |
Select-Object #{label="User" ; expression={$user}},DeviceOS, lastsuccesssync
}
That should output the user for every device they own on its own line. You could then easily export this to Export-CSV or some such thing that way.
We save the $user so it is available later in the pipe. Could also have used Add-Member but the result would have been the same.
If you have the identity field, which looks like this
domain.com/Users/OU/UserName/ExchangeActiveSyncDevices/iPhone
Then to split on the / and get the third result, you simply request:
$_.Identity.Split("/")[3]
>UserName
PowerShell begins indexing with number zero, so to request the fourth entry in the list, we request index number 3.
Update
OP mentioned that the OU level might vary, meaning that he couldn't count on a fixed position to request the Index. In order to accomodte that scenario, try this method, which will look for the index of ExchangeActiveSyncDevices and then pick the index position before that.
$_.Identity.Split('/')[($_.Identity.Split('/').Indexof('ExchangeActiveSyncDevices')-1)]
Get-Mailbox -resultsize unlimited|foreach {Get-MobileDeviceStatistics -Mailbox:$_.identity} |Select-Object #{l="user";e={$_.Identity.parent.parent.name}}, lastsuccesssync
on e2k13
I entered gwmi win32_product | select -property name | select -first 1 and output to a file. My result was #{name=Google Talk Plugin}.
How can I get rid of #{}, and name. I only want it to show Google Talk Plugin?
#{} means your exporting an object with properties. Try the -ExpandProperty parameter in Select-Object. You could also combine both select-object commands, like:
gwmi win32_product | select -expandproperty name -first 1
I ran into a problem similar with
$drive = Get-WmiObject Win32_LogicalDisk -ComputerName $servername | Select-Object DeviceID
$drive comes up as #{DeviceID=C:}, #{DeviceID=D:}, ...
Here is my brute force hack at it.
The second Trim statement was because for some reason if I put it in the first Trim it starts to Trim the letters in the Drive =D: becomes :
enter code here
$Asdrive = #() #declared before to get rid of null pointer issue, also to tell PS this is an array not a string
#Write-Host "Trimming for Get-WmiObject"
for($i=0;$i -lt $drive.length; $i++) {
[string]$sdrive = $drive[$i]
[string]$sdrive1 = $sdrive.Trim("#","{","}","D","e","v","i","c","e","I","D")
[string]$sdrive2 = $sdrive1.Trim("=")
$Asdrive += $sdrive2
}
If you're running at least Version 3, you can also use the member enumeration feature and then array slicing to take the first one, instead of using select:
(gwmi win32_product).name[0]
I add some code as I found this question with google.
Frode F. solution is the best one.
If you write out something like:
Get-ADComputer -Filter * -SearchBase $OU | Select-Object Name
you get a proper List of all Computers in an OU. You can also pipe that to a CVS/HTML file and its still nice.
| Export-CSV "mylist.csv"
But if you store it into a variable (array) every name will be wrapped in #{}.
In my case I needed computer names in a variable. Here is the solution thanks to Frodo:
$computerList = Get-ADComputer -Filter * -SearchBase $OU | Select-Object -ExpandProperty Name
Hope it helps someone.
(would add it as comment under the right solution, but I don't have enough reputation to do so)