Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
KS
Jun 10, 2003
Outrageous Lumpwad
I have what I think is a very basic question. For commands that return data tables like this:

adaz posted:

pre:
HostName   DisplayName      EntryDate            Usages
compy1      blahblah         1/1/1000             10000
compy2      blahblah         1/1/1000             10000
compy3      foobar           1/1/1000             10000
compy4      snafu            1/1/1000             10000
compy5      foobar           1/1/1000             10000
compy6      foobar           1/1/1000             10000

How should I be scraping arbitrary data out of it? For instance, if I know I want the last row? Or just the hostname value from the last row? I know how to use | where and match a regex, but I am more interested in how it should be done when the data in the fields isn't known.


Edit: finally answered my own question reading about manipulating objects. select-object is what I needed.

KS fucked around with this message at 07:11 on Aug 23, 2011

Adbot
ADBOT LOVES YOU

adaz
Mar 7, 2009

KS posted:

I have what I think is a very basic question. For commands that return data tables like this:


How should I be scraping arbitrary data out of it? For instance, if I know I want the last row? Or just the hostname value from the last row? I know how to use | where and match a regex, but I am more interested in how it should be done when the data in the fields isn't known.

Is there a resource I should be reading for basic stuff like this? Thanks in advance.

There are a lot of different ways of doing it depending on the type of object you are working with, but for that example you're talking about a array -- an actually a huge variety of powershell objects are arrays so this will work for well, all of them. As an example, let's get all the processes on a machine and do some stuff

code:
$processes = get-WmiObject win32_process

# We can iterate through each member in a foreach loop if we want to.
foreach($process in $processes) {
   if($process.property -match "Blah") {
     # do stuff
   }
}

# or in a plain for loop
for($i=0;$I -lt $processes.count;$i++) {
  # do stuff with each process
  $processes[$i]
}

# pipe your object to where-Object and grab the data based off a condition
$processes | where-Object {$_.name -match "chrome.exe"}

# Use Where-Object and Select-Object combined to only grab the data you want
$processes | where-Object {$_.name -match "chrome.exe"} | select-Object name,path,threadCount
}

code:
# Store our example above into a variable
$Chrome = $processes | where-Object {$_.name -match "chrome.exe"} | select-Object name,path,threadCount

# Grab the Path off the first row
$chrome[0].Path


code:
# Grab the last row - indexes start at 0, whereas the length property isn't
$chrome[$($Chrome.psbase.Length - 1)]

# The .psbase is a way to get  at some of the "hidden" properties/functions on the objects that powershell is obscuring for various reasons. Including
# getting at the enumerator if there is one and other handy features for messing with arrays

$chrome.psbase | get-Member


Is any of that any help? Select-Object also has ways of grabbing first & last values built into it, but if you are working with arrays (which you will tend to) I find it usually easier just to get used to indexing into them yourself.

adaz fucked around with this message at 07:34 on Aug 23, 2011

Rabid Snake
Aug 6, 2004



Anyone have any experience with NetCmdlets and PowerShell? I'm trying to recursively download a directory in a FTP and than save it onto a local drive. The NetCmdlet's Get-FTP does not do recursive like Get-ChildItems so I wrote a simple script to try and get around that. It seems to fail when grabbing ChildItem5 due to scoping issues that I've been trying to iron out.

Parent
|
| |--ChildItem3
|--ChildFolder1-|--ChildFolder3--|-ChildItem5
|
|--ChildFolder2-|ChildItem4
|
|--ChildItem1
|--ChildItem2

Anyone write a script that can download FTP directories recursively?

EDIT:

Is it also possible to automate this?
http://blogs.technet.com/b/heyscriptingguy/archive/2008/11/10/how-can-i-check-spelling-and-grammar-in-microsoft-word.aspx

Instead of going through all spelling and grammar errors manually, is there a way to accept all suggestions?

Rabid Snake fucked around with this message at 21:54 on Aug 24, 2011

Sprawl
Nov 21, 2005


I'm a huge retarded sperglord who can't spell, but Starfleet Dental would still take me and I love them for it!
Maybe try the ncftp client.

ftp://ftp.ncftp.com/ncftp/binaries/Setup%20NcFTP%203.2.5.msi

adaz
Mar 7, 2009

The system.net namespace contains all the .NET framework ftp stuff, I do not have any experience with the netcmdlets unfortunately. I've used system.net ftp classes before, but only on things I knew exactly where it was at so I didn't have to iterate through directories or anything.


As far as not bringing up a popup box the CheckGrammar method (http://msdn.microsoft.com/en-us/library/bb179621%28v=office.12%29.aspx) & Check Spelling(http://msdn.microsoft.com/en-us/library/bb179624%28v=office.12%29.aspx) don't have that ability. I took a look at the methods available on the doc object and nothing really seems to give you that ability, if you really wanted to do that you would have to do something ghetto like send keys to the window or something.

adaz fucked around with this message at 18:18 on Aug 25, 2011

Drumstick
Jun 20, 2006
Lord of cacti
So... what am I doing wrong here? Im trying to create users from a csv. Im not getting any errors, but im not getting any users either.


$user = Import-Csv "csvlocation"
$container = [ADSI] "LDAP://OU=ADUSERTEST,DC=XYZ,DC=LOCAL"

$user | foreach {
$description = $user.description
$userPrincipalname = $user.userPrincipalname
$displayName = $user.displayname
$givenName = $user.givenName
$sn = $user.sn
$password = $user.password
$homeDirectory = $user.homeDirectory
$homedrive = $user.homeDrive

}

adaz
Mar 7, 2009

Drumstick posted:

So... what am I doing wrong here? Im trying to create users from a csv. Im not getting any errors, but im not getting any users either.


$user = Import-Csv "csvlocation"
$container = [ADSI] "LDAP://OU=ADUSERTEST,DC=XYZ,DC=LOCAL"

$user | foreach {
$description = $user.description
$userPrincipalname = $user.userPrincipalname
$displayName = $user.displayname
$givenName = $user.givenName
$sn = $user.sn
$password = $user.password
$homeDirectory = $user.homeDirectory
$homedrive = $user.homeDrive

}

You need to invoke the create method off the container. Right now you're just creating a bunch of variables. Something like what I posted below. I had to add some things as well, SamAccountName usually you set to the same as CN but it's required unless you want a random string. And password requires a special invoke, you can't just flat out set it.

Also, If i'm accessing the properties off of a object inside double quotes I always use $() to expand it first because I can never remember the rules for powershell and what gets expanded and what doesn't.

code:
$user = Import-Csv "csvlocation"
$container = [ADSI] "LDAP://OU=ADUSERTEST,DC=XYZ,DC=LOCAL"

$user	| foreach {
        $userObj = $container.create("User","CN=$($user.cn)")
        $userObj.Put("userPrincipalname","$($user.userPrincipalname)")
        $userObj.put("displayName","$($user.displayname)")
        $userObj.put("samaccountname","$($user.samaccountname)")
        $userObj.SetInfo()

        $userObj.psbase.invoke("setPassword",$user.password)
	$userObj.put("description",$user.description)
	$userObj.put("givenName",$user.givenName)
	$userObj.put("sn",$user.sn)
	$userObj.put("homeDirectory",$user.homeDirectory)
	$userObj.put("homedrive",$user.homeDrive)
	$userObj.setInfo()
        
# enable account
$userObj.psbase.invokeset("accountdisabled","false")
	}

adaz fucked around with this message at 23:02 on Aug 26, 2011

Drumstick
Jun 20, 2006
Lord of cacti
Ah, thanks. I was afraid that all I was doing was creating variables, but so far, I just kinda fumble around until I get fed up and ask on here. Thank you for the help.

Clanpot Shake
Aug 10, 2006
shake shake!

I'm not sure if this is the best place to ask, but I've got a question for people who know Windows scripting. We have an AD setup where about half of the users are missing a piece of required software. Rather than installing this one by one, we want to run a logon script to install and configure it using elevated privileges. We're a newly setup department so we don't have SCCM or similar to push out software yet, so this will be a one-time thing.

I started looking into this and found we could use VBScript to run commands, but I haven't been able to get it to do what I want. I've got the logic right, I'm just struggling with how VBScript handles certain things. In particular, I can't seem to get it to call the installer .exe file, but I also can't see any errors because it's running quiet.

Is this the right approach, or is there a better/easier alternative for installing software than VBScript? Are there any resources/examples of doing this?

adaz
Mar 7, 2009

Clanpot Shake posted:

I'm not sure if this is the best place to ask, but I've got a question for people who know Windows scripting. We have an AD setup where about half of the users are missing a piece of required software. Rather than installing this one by one, we want to run a logon script to install and configure it using elevated privileges. We're a newly setup department so we don't have SCCM or similar to push out software yet, so this will be a one-time thing.

I started looking into this and found we could use VBScript to run commands, but I haven't been able to get it to do what I want. I've got the logic right, I'm just struggling with how VBScript handles certain things. In particular, I can't seem to get it to call the installer .exe file, but I also can't see any errors because it's running quiet.

Is this the right approach, or is there a better/easier alternative for installing software than VBScript? Are there any resources/examples of doing this?

If you don't have SCCM you should look into installing software via group policy (http://support.microsoft.com/kb/816102) you also might want to ask the question in just serious hardware forum: http://forums.somethingawful.com/forumdisplay.php?forumid=22

Not typically something you'd do with powershell.

Moey
Oct 22, 2010

I LIKE TO MOVE IT

Clanpot Shake posted:

I'm not sure if this is the best place to ask, but I've got a question for people who know Windows scripting. We have an AD setup where about half of the users are missing a piece of required software. Rather than installing this one by one, we want to run a logon script to install and configure it using elevated privileges. We're a newly setup department so we don't have SCCM or similar to push out software yet, so this will be a one-time thing.

I started looking into this and found we could use VBScript to run commands, but I haven't been able to get it to do what I want. I've got the logic right, I'm just struggling with how VBScript handles certain things. In particular, I can't seem to get it to call the installer .exe file, but I also can't see any errors because it's running quiet.

Is this the right approach, or is there a better/easier alternative for installing software than VBScript? Are there any resources/examples of doing this?

What software are you installing? The GPO megathread in SH/SC is a good place to start for things like this. You will probably need to turn that exe into an msi, so look into something like http://www.advancedinstaller.com/

It works, but can break some installers. You can then use that with a script to check to see if it is installed, then decide if it needs to be installed or not. I have not gotten that far along with this (my environment still has users manually updating flash/java/ff, and I hate it), but that seems to be the route to go.

Geno
Apr 26, 2004
STUPID
DICK
i asked this in the "Ask General Progamming.." but never got a response so i'll try here. this is a question for Batch scripts.

I have a batch script that's being run from opening via Windows Explorer. While it runs, if the user terminates it (via cmd + c), instead of the window closing , I want Command Prompt to be in the specific directory. Is this possible or do I have to call another additional script? I was thinking of opening up a new CMD and if the user terminates that, the orignal CMD will be in a specific directory.

KS
Jun 10, 2003
Outrageous Lumpwad

adaz posted:

There are a lot of different ways of doing it ... Is any of that any help?

Thanks so much for the effort you put into this. I found the specific tool I needed and found a toolbox full of other useful things to boot.

Another question: I think I have a pretty good grasp on handling errors and building logic into a script to handle failures. However, yesterday I had one script simply hang -- it didn't error out, but one of the cmdlets in the script hung indefinitely. What are the methods for dealing with this mode of failure?

adaz
Mar 7, 2009

KS posted:

Thanks so much for the effort you put into this. I found the specific tool I needed and found a toolbox full of other useful things to boot.

Another question: I think I have a pretty good grasp on handling errors and building logic into a script to handle failures. However, yesterday I had one script simply hang -- it didn't error out, but one of the cmdlets in the script hung indefinitely. What are the methods for dealing with this mode of failure?

I've never actually seen this happen unless I somehow made an infinite loop. What cmdlet and code were you having issues with?

Ashex
Jun 25, 2007

These pipes are cleeeean!!!
I'm finally digging in and figuring out this PowerShell crap. I'm rewriting a VBscript in PS and so far things are going as expected. I ran into some weird behavior where this:

code:
$strMsg = `
				"Name: " + $objitem.Name `
				"Manufacturer: " + $objitem.Manufacturer `
				"Model: " + $objitem.Model `
				"Memory: " + [Math]::Round($objitem.TotalPhysicalMemory/1Gb, 2) + " Gb" `
				"Number of Processors: " + $objitem.NumberOfProcessors
When printed displayed like this:

Manufacturer: LENOVO
Model: 276712U
Memory: 3.9 Gb
Number of Processors: 1
Name: TACO-T400


With the name at the bottom instead of the top. I hopped on irc and asked for help and was told to use Format-List instead which works great. This is what I'm using instead:

code:
$objItem | Format-list Name, Manufacturer, Model, @{ Name = 'Memory'; Expression = { "{0:N2} Gb" -f ($_.TotalPhysicalMemory / 1GB)}}
Now this was written for me and it looks great, but how the dick do I store that in a variable? What I'm doing is building a function for each WMI Class so when I'm doing I can run the set of functions, store the list in a variable then print it to screen and write it to a text file for reference (I'm also generating a Spreadsheet but that's taken care of).

Ashex fucked around with this message at 00:29 on Sep 17, 2011

adaz
Mar 7, 2009

Well to answer your basic question this will store it in a variable:

code:
$newObj = $objItem | Format-list Name, Manufacturer, Model, @{ Name = 'Memory'; Expression = { "{0:N2} Gb" -f ($_.TotalPhysicalMemory / 1GB)}}
However, your FIRST problem you should really probably be using Here-Strings (http://technet.microsoft.com/en-us/library/ee692792.aspx) which format much nicer. So as an example as a here-string:

code:
$strMsg = @"
Name: $objitem.Name 
Manufacturer:  $objitem.Manufacturer 
Model:  $objitem.Model 
Memory: $([math]::Round($objitem.TotalPhysicalMemory/1Gb, 2)) 
Number of Processors: $objitem.NumberOfProcessors
"@
You can also do what I like doing and put all this data in an Object, which makes exporting out to a text file or spreadsheet a bit easier (certainly makes manipulating the data easier than if it is just in string), plus fits in the whole theme of object oriented scripting language. I pipe a null object to Select-Object (http://technet.microsoft.com/en-us/library/dd315291.aspx) to "quick" create a new object with those properties.

code:
$outObj = "" | select-Object name,manufacturer,model,memory,processnum
$outObj.name = $objitem.Name
$outObj.Manufacturer =  $objitem.Manufacturer
$outObj.Model = $objitem.Model
$outObj.memory = [Math]::Round($objitem.TotalPhysicalMemory/1Gb, 2) + " Gb"
$outObj.processnum = $objitem.NumberOfProcessors

# you can then store it into a array
$table = @()
$table += $OutObj

# you can then pipe the table to export-Csv
$table | export-Csv C:\temp\blah.csv -noTypeInformation

Ashex
Jun 25, 2007

These pipes are cleeeean!!!
I feel stupid, for some reason when I just tried doing that things sorta broke.

I actually tried using Here-Strings and I kept getting the following error (as I am now)

code:
The string starting:
At C:\Scripts\ComputerInfoScript.ps1:44 char:13
+         $strMsg =  <<<< @" 
is missing the terminator: "@.
At C:\Scripts\ComputerInfoScript.ps1:86 char:4
+ #    } <<<< 
    + CategoryInfo          : ParserError: (                Name:  $obj...ters)
#    {
#    }:String) [], ParseException
    + FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
This is what I'm using:

code:
		$strMsg = @" 
				Name:  $objitem.Name 
				Manufacturer:  $objitem.Manufacturer 
				Model:  $objitem.Model 
				Memory:  $([Math]::Round($objitem.TotalPhysicalMemory/1Gb, 2))
				Number of Processors:  $objitem.NumberOfProcessors
				"@
I actually built an Excel object so I can insert entries directly to an Excel workbook. One issue I'm having is there's a couple other functions that pull the Processor speed and disks via WMI, when there's multiple processors I get an extra line (i.e 2300 2300 2300 for 3 processors) and I'm not sure how to only pull the first line.
For the Disks I want to record each drive but from how I'm using the Excel it inserts each WMI Property into its own cell so only the last disk reported will be recorded (I need to figure out a way to just add columns when there is more then one line given back, I'm considering a for loop with that property).

Ashex fucked around with this message at 18:19 on Sep 19, 2011

adaz
Mar 7, 2009

Actually it's my fault. The string formatting is wrong, and welcome to when powershell's clever automatic string expansion really fucks things up!

What we need to do is put all the WMI calls into $() (a subexpression) so it expands them first before parsing the rest of the string. This time I actually tested the code before and it does in fact work (I assume you're using win32_computersystem).

code:
$strMsg = @" 
				Name:  $($objitem.Name.ToSTring())
				Manufacturer:  $($objitem.Manufacturer.ToSTring())
				Model:  $($objitem.Model.ToSTring())
				Memory:  $([Math]::Round($($objitem.TotalPhysicalMemory)/1Gb, 2))
				Number of Processors:  $($objitem.NumberOfProcessors)
				"@
As far as the multiple items typically they are stored in an array in WMI so you can test to see if an array is being passed back, a foreach loop would be one way of dealing with them.

Ashex
Jun 25, 2007

These pipes are cleeeean!!!
Alright, I tried just copying your bit straight over and it's still breaking on me, Here's the full function:



Am I missing something?
Apparently I can't even tab indent "@ at the end. Removed all the tabs and it worked.



Edit: Going back to using format-list, I remembered why I was having issues storing it in a variable, when I try doing as you suggested before this happens when I try to print the variable with write-Host:


code:
Microsoft.PowerShell.Commands.Internal.Format.FormatStartData Microsoft.PowerShell.Commands.Internal.Format.GroupStartData Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData Microsoft.PowerShell.Commands.Internal.Format.GroupEndData Microsoft.PowerShell.Commands.Internal.Format.FormatEndData

Ashex fucked around with this message at 20:53 on Sep 19, 2011

adaz
Mar 7, 2009

Ah. Yes, that's because write-host can't convert the custom object to a string which happens a lot with format-list and the various format-commands. I believe it's usually because the sub-objects don't have a .ToSTring() type method but could be wrong, been awhile since I dug into it. If you want to dig in deeper do a $objItem | gm and you can see all different types of objects and types embedded in this one simple object. Luckily enough for us there is Out-String cmdlet which handles this just fine and dandy

code:
Out-String -inputObject $newObj | write-host -backgroundColor "black"

adaz fucked around with this message at 23:34 on Sep 19, 2011

Ashex
Jun 25, 2007

These pipes are cleeeean!!!
Thanks for pointing that out for me, I don't see an easy way to append data to that without piping the gently caress out of it so I'm going to stick with Here-Strings (mainly because I'm more or less done with the script).

I've got one last question (thanks for hanging around to help me), how exactly does one do error handling? I've searched around on the web and there are a few different methods but none of them seem particularly clear.

Now that I've built all the functions, I'm going to stick it in a for loop. I'm under the assumption I'll use something like Catch to check for an error, do I have to write a Catch for each type of error if I want to know what went wrong?

This is the loop I'm talking about :

code:
foreach ($strComputer in $arrComputers)
			{
		$global:strMsg=""
		SysInfo
		ProcInfo
		NicInfo
		DiskInfo
		Write-Host $global:strMsg
		$intRow = $intRow + 1
			}

adaz
Mar 7, 2009

Ashex posted:

Thanks for pointing that out for me, I don't see an easy way to append data to that without piping the gently caress out of it so I'm going to stick with Here-Strings (mainly because I'm more or less done with the script).

I've got one last question (thanks for hanging around to help me), how exactly does one do error handling? I've searched around on the web and there are a few different methods but none of them seem particularly clear.

Now that I've built all the functions, I'm going to stick it in a for loop. I'm under the assumption I'll use something like Catch to check for an error, do I have to write a Catch for each type of error if I want to know what went wrong?

This is the loop I'm talking about :

code:
foreach ($strComputer in $arrComputers)
			{
		$global:strMsg=""
		SysInfo
		ProcInfo
		NicInfo
		DiskInfo
		Write-Host $global:strMsg
		$intRow = $intRow + 1
			}

With V2 of powershell they introduced try/catch statements like you have in the .Net languages. There is a really good blog post on this from the scripting guys that does a better job than I would of explaining this: http://blogs.technet.com/b/heyscriptingguy/archive/2010/03/11/hey-scripting-guy-march-11-2010.aspx

In general with powershell unless you know the exception that is likely to be thrown I tend to just do a generic catch statement, which in its completely most basic simplistic form would be this:

code:
try {
foreach ($strComputer in $arrComputers)
			{
		$global:strMsg=""
		SysInfo
		ProcInfo
		NicInfo
		DiskInfo
		Write-Host $global:strMsg
		$intRow = $intRow + 1
			}
} Catch {
   Write-host $Error
}
For more granular error control you have to trap the exact type of exception that will be thrown, you'd need to check the various .NET methods/properties you are accessing to find out what exceptions they are throwing.

Ashex
Jun 25, 2007

These pipes are cleeeean!!!
Got it, I'm going to add some Catches for WMI. Is there an easy way to find what errors it can throw?

I mostly want to write a catch for access denied (done), unable to connect, and other weird WMI errors that may occur.

Edit: Looks like I've got it all, I want to catch a couple WMI errors like GetWMIManagementException and GetWMICOMException but they're not actual Exceptions :(

Ashex fucked around with this message at 21:57 on Sep 20, 2011

Goonamatic
Sep 17, 2005
cunning linguist
Hey all

Anyone have any knowledge with PSAKE and powershell

Trying to get a build script working

http://stackoverflow.com/questions/7494726/psake-powershell-execute-task-not-running

adaz
Mar 7, 2009

Ashex i've been trying to find an answer to your question that isn't "just cause each of these errors and see what exception they generate" and... I might be bad at MSDN. Because I'm not finding a comprehensive list, I will continue to research it and see what I can find out. Because that's a good thing to know how to do :)

Goonamatic, I've never used psake sorry man.

Ashex
Jun 25, 2007

These pipes are cleeeean!!!
Thanks for looking around, error handling with WMI is a bitch. I couldn't find a good way to handle connection failures so I'm using Test-Connection to make sure each computer is online before it runs a report.

amishpurple
Jul 21, 2006

I'm not insane, I'm just not user-friendly!
Just wanted to drop in and say that I picked up PowerShell in Action, Second Edition (http://www.amazon.com/Windows-PowerShell-Action-Second-Payette/dp/1935182137/ref=pd_sim_b9) and it owns bones. You also get free digital copies of the book (Kindle, PDF, and more).

I just started messing with PowerShell a couple weeks ago and I'm already in love. Wish I would have started a couple years ago...

Ashex
Jun 25, 2007

These pipes are cleeeean!!!
I've run into a slight problem with an incrementing variable that I can't control, basically I have a function that retrieves the disk drives on a computer and enters it into a spreadsheet. The problem is I'm using an excel function that writes one cell at a time so to adjust for multiple drives I have it insert a row to add a new drive, because of this I end up with a blank row at the end if there's only one disk drive.

This is the function:

code:
Function DiskInfo {

	$colitems = Get-WmiObject -Namespace "root\CIMV2" `
	-ComputerName $strComputer -Query "select * from Win32_LogicalDisk where DriveType='3'"
	foreach ($objitem in $colitems) {
		$global:strMsg += @"
			
	Drive:  $($objitem.Name.ToSTring())
	Size: $([Math]::Round($($objitem.Size)/1Gb, 2)) GB
	Free: $([Math]::Round($($objitem.FreeSpace)/1Gb, 2)) GB

"@			

		$Sheet.Cells.Item($global:intRow,8) = $objitem.Name
		$Sheet.Cells.Item($global:intRow,9) = $([Math]::Round($($objitem.Size)/1Gb, 2))
		$Sheet.Cells.Item($global:intRow,10) = $([Math]::Round($($objitem.FreeSpace)/1Gb, 2))
		$global:intRow++
		}
		
	
}
I've tried using a simple conditional counter like if ($i -gt 0) {$introw++} else {$i++} but that didn't work. I added a check at the end of the script to look for empty rows and delete them which is working but it seems rather messy.

adaz
Mar 7, 2009

^^^^^
If I'm understanding your problem right can't you just do this

code:
if($colitems.count -gt 1) {
 # insert row
}else {
 # don't insert row
}

amishpurple posted:

Just wanted to drop in and say that I picked up PowerShell in Action, Second Edition (http://www.amazon.com/Windows-PowerShell-Action-Second-Payette/dp/1935182137/ref=pd_sim_b9) and it owns bones. You also get free digital copies of the book (Kindle, PDF, and more).

I just started messing with PowerShell a couple weeks ago and I'm already in love. Wish I would have started a couple years ago...

I read the first edition of this book and really liked it, I should pick up the second one of these days!.

adaz
Mar 7, 2009

Also Ashex I've been digging into error handling for you, and I did find out some stuff that might be helpful. So the biggest thing we want to know is how to handle the various exceptions being thrown by WMI right? Well to do that we want to find out what type of exception they are throwing.

So say I query a non-existent computer, I get this error back:


What is interesting though is say I want to try and trap this stupid thing, so we can handle it. Well, if I run this code I get nothing back but the error, the Catch block is never executed:

code:
try {
  gwmi -computer bf510700 -class win32_computersystem
} catch {
  write-host "I trapped a loving error!"
}


It's never trapping the error. Why? Because by default powershell only traps terminating errors (http://technet.microsoft.com/en-us/library/dd315350.aspx), and this is considered a non-terminating error. Why? gently caress if I know, they decided early on to make this the default error behavior and it has always been a mystery to me. See what happens when we change the $errorActionPreference = "stop"

code:
$ErrorActionPreference = "Stop"
try
{
gwmi -computer bf510700 -class win32_computersystem

}catch  {
    write-host -backgroundColor white "Trapped a stupid error finally!"
}


Ok so now we can catch our error. That's good. But we need to find out what type of exception it's throwing. If we look at $error[0].psbase.exception.GetType() (last thrown error is always 0 in the $error array) we can see that
it's of a type System.Runtime.InteropServices.ComException (http://msdn.microsoft.com/en-us/library/af1y26ew%28v=vs.80%29.aspx). Now this code SHOULD work, but it in fact completely does not! I'm suspecting it has something to do with trapping a non-terminating error as opposed to terminating, or I'm missing something completely obvious. Compare the results:

code:

$ErrorActionPreference = "Stop"
try {
    gwmi -computer bf510700 -class win32_computersystem
}
catch [System.Runtime.InteropServices.COMException] {
   write-host -backgroundcolor white -foregroundcolor black "WMI error!"
    Write-Host $Error[0].Exception
}
 catch [system.exception] {
    write-host -backgroundcolor white -foregroundcolor black  "Trapped a non-wmi com error or something!"
    Write-Host $Error[0].Exception
}    


And then this classic example:
code:
 try {
    1/$null
 } 
 catch [system.dividebyZeroException] {
    write-host -backgroundcolor white -foregroundcolor black "don't do this"
    Write-Host $Error[0].Exception
 }
 catch  {
    Write-Host -backgroundcolor white -foregroundcolor black "Not a divide by zero error"
    Write-Host $Error[0].Exception
 }

adaz fucked around with this message at 19:58 on Sep 22, 2011

Ashex
Jun 25, 2007

These pipes are cleeeean!!!
I see you have run into the same problem I was having! I wanted to be able to catch the RPC error message but couldn't find a way to do it, I eventually caved and wrote a General Catch that actually explained to the user what to do if they got X error from WMI. I found this article to be somewhat helpful though.

As for using $colitems.count, For some reason I don't get anything back:

code:
PS C:\Users\ashex> $colitems = Get-WmiObject -Namespace "root\CIMV2" -Query "select * from Win32_LogicalDisk where Driv
eType='3'"
PS C:\Users\ashex>
PS C:\Users\ashex> $colitems.count
PS C:\Users\ashex> Write-Host $colitems.count

PS C:\Users\ashex>

adaz
Mar 7, 2009

That makes no sense. If you do a $colitems | gm what are the properties it's showing? Are you running that on a machine without any physical disks?

Ashex
Jun 25, 2007

These pipes are cleeeean!!!
I'm running it on my laptop, if I do $colitems.name I get my C: drive. $colitems|gm spits out all the properties available.


Edit: This is interesting, I ran it against a server 2008 box and I do get a number back.

Edit Edit: Okay this is interesting, if I use a computer with more then one drive:

code:
PS C:\Users\ashex> $colitems.count
2
PS C:\Users\ashex> if ($colitems.count = $null) {Write-host "Nothing"}
"Length" is a ReadOnly property.
At line:1 char:15
+ if ($colitems. <<<< count = $null) {Write-host "Nothing"}
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyAssignmentException

PS C:\Users\ashex> if ($colitems.count) {Write-host "Nothing"}
Nothing
PS C:\Users\ashex>
If I run it against a computer with only one drive:

code:
PS C:\Users\ashex> $colitems.count
PS C:\Users\ashex> 
PS C:\Users\ashex> if ($colitems.count) {Write-host "Nothing"}
PS C:\Users\ashex>
So it looks like I can just check if $colitems.count exists, if it is true I'll add a row.

Edit: Nope, I tried using if ($colitems.count) {$global:intRow++} with no success.

Ashex fucked around with this message at 00:18 on Sep 23, 2011

evil_bunnY
Apr 2, 2003

adaz posted:

Bumping this thread in case there are any more questions and the scripting guys have been running a great series on powershell & active directory this week that everyone should check out:

http://www.games-workshop.com/gws/catalog/productDetail.jsp?catId=cat440342a&prodId=prod840005a
I don't think that's what you meant to post, buddy!

Phone
Jul 30, 2005

親子丼をほしい。
Did you submit your issue to Hey, Scripting Guy!? If not... uh... :tinfoil:

That said, I'm totally stealing the hell out this so I can audit the PCs at my work easily.

Ashex
Jun 25, 2007

These pipes are cleeeean!!!

Phone posted:

Did you submit your issue to Hey, Scripting Guy!? If not... uh... :tinfoil:

That said, I'm totally stealing the hell out this so I can audit the PCs at my work easily.

I'll give you the complete script :P Just send me a pm.

Edit: I sent an email to him, I'll see what I get.

Ashex fucked around with this message at 17:56 on Sep 26, 2011

adaz
Mar 7, 2009

Ashex posted:

I'm running it on my laptop, if I do $colitems.name I get my C: drive. $colitems|gm spits out all the properties available.


Edit: This is interesting, I ran it against a server 2008 box and I do get a number back.

Edit Edit: Okay this is interesting, if I use a computer with more then one drive:

code:
PS C:\Users\ashex> $colitems.count
2
PS C:\Users\ashex> if ($colitems.count = $null) {Write-host "Nothing"}
"Length" is a ReadOnly property.
At line:1 char:15
+ if ($colitems. <<<< count = $null) {Write-host "Nothing"}
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyAssignmentException

PS C:\Users\ashex> if ($colitems.count) {Write-host "Nothing"}
Nothing
PS C:\Users\ashex>
If I run it against a computer with only one drive:

code:
PS C:\Users\ashex> $colitems.count
PS C:\Users\ashex> 
PS C:\Users\ashex> if ($colitems.count) {Write-host "Nothing"}
PS C:\Users\ashex>
So it looks like I can just check if $colitems.count exists, if it is true I'll add a row.

Edit: Nope, I tried using if ($colitems.count) {$global:intRow++} with no success.

What version of powershell are you using? I'm trying to figure out how that could possibly fail. If I write a query to return only 1 result it doesn't return true on the count:


If I write the query to return more than 1 result it successfully finds it:


evil_bunnY posted:

I don't think that's what you meant to post, buddy!

:( mispasting!

Cronus
Mar 9, 2003

Hello beautiful.
This...is gonna get gross.
I'm thinking this is due to how Powershell handles items by default based on their type. The type on that query is a ManagementObject, and not a System.Array type, which I think would give you the expected results.

I also tried it in 2008, but didn't get a count on a single return, I guess it just implies 1 when it doesn't return a result. That is pretty dumb.

So if you wanted to get a legitimate count, you could make a dummy variable and run the object through a quickie loop like so:

code:
$blah = gwmi -class Win32_LogicalDisk | where{$_.deviceid -match "C:"}

$dummycount = 0
$blah | %{$dummycount++}

echo "The true count of items is [$dummycount]"
It's like the SystemManagement objects aren't inheriting count/length properties, and yet when returning multiple items, it clearly works. But testing that goes way beyond my knowledge of the language, I just write stuff!

Ashex
Jun 25, 2007

These pipes are cleeeean!!!
I tried what you suggested and when I tested it out it looked like it would work but when I used it in the script it didn't increment the row for the computer with one drive :(

code:
$intDisk = 0
		$colitems | %{$intDisk++}
		if($intDisk -ne 1) {$global:intRow++}

Adbot
ADBOT LOVES YOU

Wicaeed
Feb 8, 2005
VBScript related question (Don't shoot me!)

I have the following subroutine in a script that uses wbadmin to back up a folder locally, rename it, then transfer the renamed backup folder to a network drive:

code:
Sub get2K3DBackupStatus
	If objFSO.FolderExists (BackupFolder2K3D) Then
		Set BackupFolder2K3DTmp = objFSO.GetFolder(BackupFolder2K3D)
		BackupFolder2K3DInfo = "Confluece D drive backed up on " & BackupFolder2K3DTmp.DateLastModified & "." & " Total Size: " & BackupFolder2K3DTmp.Size /1024\1024 & "MB"
		objFSO.MoveFolder BackupFolder2K3D, "D:\ConfluenceD_" & CurrentDate
		objFSO.CreateFolder "S:\Backup\Confluence_Backup\ConfluenceD_" & CurrentDate
		objFSO.CopyFolder "D:\ConfluenceD_" & CurrentDate , "S:\Backup\Confluence_Backup\ConfluenceD_" & CurrentDate
	Else
		BackupFolder2K3DInfo = "Confluence D drive backup does not exist"
	End If
End Sub

Lately the backup has been running, but the file has not been being transferred to the network share. I can run the script myself and it works fine, but the scheduled task hangs and keeps running over night.

What would be the simplest way to write a text file with whatever error my script is running into at this point?

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply