1. OCAU Merchandise is available! Check out our 20th Anniversary Mugs, Classic Logo Shirts and much more! Discussion in this thread.
    Dismiss Notice

Pablo's Powershell Pow-Wow

Discussion in 'Business & Enterprise Computing' started by PabloEscobar, Sep 18, 2014.

  1. OP
    OP
    PabloEscobar

    PabloEscobar Member

    Joined:
    Jan 28, 2008
    Messages:
    14,678
    Hi Pablo, I hear you say, Your shitty HTML tables are great for me, but my boss is straight from PHB Business school, and to him, data doesn't exist unless its in the form an an excel spreadsheet, Am i stuck copy/pasting data for all eternity, or is there something that can be done (short of murder in the first). that will help me.


    Well, I say, I'm glad you asked. Powershell can interact with many windows systems using Com objects. Office is one such system. These are a little bit harder to use the native powershell, but none the less, they are still objects, and you can still interrogate them using get-member to find out what methods and properties they have.

    We will be using our WMI query from the previous episodes, because its familiar to us... I promise this will be the second last article that uses it :).

    So lets start playing with Excel from Powershell

    Code:
    # Create Our New Excel
    $excel = new-object -comobject Excel.Application
    $excel.visible = $true
    $workbook = $excel.workbooks.add()
    
    #Delete the two spare Sheets
    $S2 = $workbook.sheets | where-object {$_.name -eq "Sheet2"}
    $s3 = $workbook.sheets | where-object {$_.name -eq "Sheet3"}
    $s2.delete()
    $s3.delete()
    
    $s1 = $workbook.sheets | where-object {$_.name -eq 'Sheet1'}
    $s1.name = "Index"
    
    
    We start by creating a new Object ($Excel), we set it to visible, (so we can actually watch our script do stuff in Excel) and we Add workbook to our $Excel object, using the add()

    Now we get the spare worksheets that a new workbook comes with. because everything is an object, we can use our powershell knowledge (of where-object) to work with them. So we grab the Objects names Sheet2 and Sheet3, store them in variables $S2 and $S3, and then call the delete method on them, they are now Gone, after that, we grab Sheet1, Store it in $S1 and set its 'name' property to "Index"

    If we run this script now, we end up with Excel running and visible, with a workbook open, and a worksheet named "index" ready to rock. Lets fill it with our wad of information.

    Code:
    $Computers = get-content "C:\Scripts\Computers.txt" 
    
    $excelRow = 2
    
    foreach ($Computer in $Computers)
      	{
    	$s1.Cells.Item($excelRow, 1) = "$Computer" 
    	$DiskInfoCollection = Get-WmiObject Win32_LogicalDisk -ComputerName $Computer -filter "DriveType = 3" 
    
    	$excelRow ++
    
    	$NewSheet = $workbook.sheets.add()
    	$NewSheet.Cells.Item(1,1) = "Volume Name"
    	$NewSheet.Cells.Item(1,2) = "Device ID"
    	$NewSheet.Cells.Item(1,3) = "FreeGB"
    	$NewSheet.Cells.Item(1,4) = "TotalGB"
    	$NewSheet.Cells.Item(1,5) = "Percent Free"	
    	$diskinfoRow = 2
    	# Write-Host $DiskInfoCollection.VolumeName
    	# $DiskInfoCollection | Get-Member
    	foreach ($DiskInfo in $DiskInfoCollection)
    		{
    		$NewSheet.Cells.Item($diskinfoRow,1) = $DiskInfo.Volumename
    		$NewSheet.Cells.Item($diskinfoRow,2) = $DiskInfo.DeviceID
    		$NewSheet.Cells.Item($diskinfoRow,3) = [math]::round($DiskInfo.FreeSpace/ 1GB,2)
    		$NewSheet.Cells.Item($diskinfoRow,4) = [math]::round($DiskInfo.Size / 1GB,2)
    		$NewSheet.Cells.Item($diskinfoRow,5) = [math]::round(($DiskInfo.FreeSpace/$DiskInfo.size)*100,2)
    		$diskinfoRow ++
    		}
    		
    	$NewSheet.Name = "$Computer"
    	}
    	#$workbook.Saveas("C:\Scripts\BadReport.xls")
    
    So, familar to regular readers will be our fetching of computers.txt, we are also setting a variable here ($excelRow) to determine where on the index worksheet, we start to write out computer names

    We've got our primary Computers loop, but this time, as well as running the WMI query, it is also plugging some information into our spreadsheet

    Code:
    $s1.Cells.Item($excelRow, 1) = "$Computer" 
    $s1 is our worksheet, which we plug into Cell Row2, Column 1 (A2 in excel speak) the value "$Computer".

    Next up, we run our WMI query, and store the output in an object, because we are going to work with it later.

    Code:
    $excelRow++
    is similar to the += we discussed above, it just increments the $excelRow variable by one. This means that the next time the loop runs, its writing to Row 3 (Because we started $excelRow at 2) instead of overwriting what was already there.

    Now, we are back in excel land.

    Code:
    		$NewSheet = $workbook.sheets.add()
    	$NewSheet.Cells.Item(1,1) = "Volume Name"
    	$NewSheet.Cells.Item(1,2) = "Device ID"
    	$NewSheet.Cells.Item(1,3) = "FreeGB"
    	$NewSheet.Cells.Item(1,4) = "TotalGB"
    	$NewSheet.Cells.Item(1,5) = "Percent Free"	
    	$diskinfoRow = 2
    
    We create a new worksheet using the add() method, and then we create some column headers, because we have $excel.visible = true we can actually see this happening on our workbook. We also initilize a new row variable, so that our disk information doesn't overwrite out nice clean titles.

    Code:
    	foreach ($DiskInfo in $DiskInfoCollection)
    		{
    		$NewSheet.Cells.Item($diskinfoRow,1) = $DiskInfo.Volumename
    		$NewSheet.Cells.Item($diskinfoRow,2) = $DiskInfo.DeviceID
    		$NewSheet.Cells.Item($diskinfoRow,3) = [math]::round($DiskInfo.FreeSpace/ 1GB,2)
    		$NewSheet.Cells.Item($diskinfoRow,4) = [math]::round($DiskInfo.Size / 1GB,2)
    		$NewSheet.Cells.Item($diskinfoRow,5) = [math]::round(($DiskInfo.FreeSpace/$DiskInfo.size)*100,2)
    		$diskinfoRow ++
    		}
    
    Here we have a Nested Loop (the Disk Loop), this one loops over our WMI query, grabbing the info for each drive, and adding it to the newly created worksheet. We haven't had to use a nested loop to get the information so far, because both Format-Table, and ConvertTo-HTML understand collections of objects and have internal logic to display them nicely. Other applications (or even some cmdlets) might not, which will result in the nested loop being a requirement.

    Much the same as above, we are adding Items to our Worksheet ($NewSheet) The items we are adding are the same as previous scripts, but because we aren't passing them down the pipeline, we can't use $_.<property>. At the end of the loop, we increment the $diskinfoRow counter so that the next drive is written on a new line, instead of over the old one.

    We then close out the Disk Loop

    Code:
    	$NewSheet.Name = "$Computer"
    	}
    
    	$workbook.Saveas("C:\Scripts\BadReport.xls")
    
    We close it up by rename our worksheet to the computer name that we have operated on, and then close out the Computer Loop and use the SaveAs method on the Workbook to save it.

    From this, we end up with an Excel Spreadsheet that contains a workbook for every Computer, and an Index page that lists them. Suitable for the Most Pointy Haired of bosses


    Full Script - Hopefully not for future Reference any more :)

    Code:
    # Create Our New Excel
    $excel = new-object -comobject Excel.Application
    $excel.visible = $true
    $workbook = $excel.workbooks.add()
    
    #Delete the two spare Sheets
    $S2 = $workbook.sheets | Where-Object {$_.name -eq "Sheet2"}
    $s3 = $workbook.sheets | Where-Object {$_.name -eq "Sheet3"}
    $s2.delete()
    $s3.delete()
    
    $s1 = $workbook.sheets | where {$_.name -eq 'Sheet1'}
    $s1.name = "Index"
    
    #Now Our Excel Sheet is Ready to be Filled
    
    $Computers = get-content "C:\Scripts\Computers.txt" 
    
    $excelRow = 2
    
    foreach ($Computer in $Computers)
      	{
    	
    	$s1.Cells.Item($excelRow, 1) = "$Computer"
    
    	$DiskInfoCollection = Get-WmiObject Win32_LogicalDisk -ComputerName $Computer -filter "DriveType = 3" 
    	$excelRow ++
    	$NewSheet = $workbook.sheets.add()
    	$NewSheet.Cells.Item(1,1) = "Volume Name"
    	$NewSheet.Cells.Item(1,2) = "Device ID"
    	$NewSheet.Cells.Item(1,3) = "FreeGB"
    	$NewSheet.Cells.Item(1,4) = "TotalGB"
    	$NewSheet.Cells.Item(1,5) = "Percent Free"	
    	$diskinfoRow = 2
    	foreach ($DiskInfo in $DiskInfoCollection)
    		{
    		$NewSheet.Cells.Item($diskinfoRow,1) = $DiskInfo.Volumename
    		$NewSheet.Cells.Item($diskinfoRow,2) = $DiskInfo.DeviceID
    		$NewSheet.Cells.Item($diskinfoRow,3) = [math]::round($DiskInfo.FreeSpace/ 1GB,2)
    		$NewSheet.Cells.Item($diskinfoRow,4) = [math]::round($DiskInfo.Size / 1GB,2)
    		$NewSheet.Cells.Item($diskinfoRow,5) = [math]::round(($DiskInfo.FreeSpace/$DiskInfo.size)*100,2)
    		$diskinfoRow ++
    		}
    		
    	$NewSheet.Name = "$Computer"
    	}
    $workbook.Saveas("C:\Scripts\BadReport.xls")
    
     
  2. Embercide

    Embercide Member

    Joined:
    Jun 17, 2002
    Messages:
    1,819
    Location:
    Brisbane
    So a question regarding foreach and the available variables(terminology?)

    Consider this:
    Code:
    $VMs = Get-VM | Where {$_.Name -like "NVR*"}
    
    Foreach ($VM in $VMs)
          {
    	   $myNetworkAdapter = Get-VM -Name $VMs | Get-NetworkAdapter -Name "Network adapter 2"
    	   Set-NetworkAdapter -NetworkAdapter $myNetworkAdapter -Connected:$true -StartConnected:$true -confirm:$false
          }
    
    I understand $VMs contains all of the output from Get-VM.
    What I don't know is where the author found the $VM variable inside $VMs

    How can I find what other variables are available in $VMs ?
    Also what is a tidy way of exiting the 'foreach' loop once each $VM has been touched by the loop? Or will it end naturally?
     
    Last edited: Nov 17, 2014
  3. OP
    OP
    PabloEscobar

    PabloEscobar Member

    Joined:
    Jan 28, 2008
    Messages:
    14,678
    I've been a bit tardy with this thread, I can't promise It will change, I can't even promise I'll try... but if you do post something in it, I promise I will try to answer it.

    Code:
    $VMs = Get-VM | Where {$_.Name -like "TEST*"}
    
    $VMs is a "collection" of objects of type "Microsoft.HyperV.PowerShell.VirtualMachine" which doesn't really mean much to you...

    To see a friendly output of what it contains, just run the command without assigning the output to a variable, powershell is smart enough to know that you just want the output on the screen, and will give you a selection of default properties for each "item" in the "collection". Alternatively you can pipe the Variable to "Write-Ouput"

    Code:
    PS C:\Users\Administrator> Get-VM | Where {$_.Name -like "TEST*"}
    
    Name           State   CPUUsage(%) MemoryAssigned(M) Uptime   Status            
    ----           -----   ----------- ----------------- ------   ------            
    Test-Win10     Running 0           2688              04:12:46 Operating normally
    TEST-Win2012r2 Off     0           0                 00:00:00 Operating normally
    Test-Win7      Off     0           0                 00:00:00 Operating normally
    Test-XP        Off     0           0                 00:00:00 Operating normally
    
    we can see what properties and methods our collection of VMs has using Get-Member

    Code:
    C:\Users\Administrator> $VMs = Get-VM | Where {$_.Name -like "TEST*"}
    get-member -InputObject $VMs
    
    
       TypeName: System.Object[]
    
    Name           MemberType            Definition                                           
    ----           ----------            ----------                                           
    [B]Count          AliasProperty         Count = Length           [/B]                            
    Add            Method                int IList.Add(System.Object value)                   
    Address        Method                System.Object&, mscorlib, Version=4.0.0.0,             
    <snip>                    
    Length         Property              int Length {get;}                                    
    LongLength     Property              long LongLength {get;}                               
    Rank           Property              int Rank {get;}                                      
    SyncRoot       Property              System.Object SyncRoot {get;}          
    
    So something like

    Code:
    $VMs = Get-VM | Where {$_.Name -like "TEST*"}
    write-host "There are $($VMs.Count) Machines in the Collection"
     ---
    There are 4 Machines in the Collection
    
    Is a nice output, we know there are 4 items in the collection, from when we just ran the command by itself

    Moving along

    Code:
    $VMs = Get-VM | Where {$_.Name -like "TEST*"}
    
    Foreach ($VM in $VMs)
          {
    	  write-host $vm.VMName
          }
    
    $VM isn't a variable 'inside' $VMs

    $VM is created (and updated) by the ForEach statement... in plain english what this is doing is

    For Each entry in the $VMs collection,
    Grab One, and load it (The Object) into the $VM variable
    Then do whatever is inside the Braces {...} to it
    and THEN grab the next one and load it into the $VM variable do the same {...} thing to it.
    Keep doing this until their are no more to grab.

    and then go on with your life.


    The loop will exit when there are no more objects in the $VMs variable left to grab.
     
    Last edited: Nov 17, 2014
  4. Embercide

    Embercide Member

    Joined:
    Jun 17, 2002
    Messages:
    1,819
    Location:
    Brisbane
    Cheers, that makes perfect sense. :thumbup:
    Here I was thinking $VM was a particular property but it sounds like I could rename that to $Hamsters and it will still work!
     
  5. OP
    OP
    PabloEscobar

    PabloEscobar Member

    Joined:
    Jan 28, 2008
    Messages:
    14,678
    Spot on, convention says that if $items is your collection, then when you run through it using foreach , $item should be your variable, but its by no means a requirement.

    You could change it to $Hamsters definatley, you then just need to change everything that referenced individual objects within the script block {...} to reference $Hamsters.<property>... because you are changing the name of variable that the object you want to perform operations on.
     
  6. freaky_beeky

    freaky_beeky Member

    Joined:
    Dec 2, 2004
    Messages:
    1,172
    Location:
    Brisbane
    Hey Pablo,

    I don't have time (at the moment unfortunately :( ) to write something pretty to build on your previous example (which is great by the way), but this might be helpful to yourself and others.

    Code:
    # http://www.excelmashup.com/
    # Add the pre and post content lines to the headers of the ConvertTo-HTML cmdlet to get an Excel Interactive View of the output.
    
    $PRECONTENT = @"
    <a href="#" name="MicrosoftExcelButton" data-xl-tableTitle="PowerShell Rocks" data-xl-buttonStyle="Standard" data-xl-fileName="Book1" data-xl-attribution="Data provided by PowerShell Baby!" ></a>
    "@
    $POSTCONTENT = @"
    <script type="text/javascript" src="http://r.office.microsoft.com/r/rlidExcelButton?v=1&kip=1"></script>
    "@
    
    # Example
    Get-Service | ConvertTo-Html -PreContent $PRECONTENT -PostContent $POSTCONTENT | Out-File $HOME\Cool.html
    Invoke-Expression $HOME\Cool.html
    
    <#
    This Sample Code is provided for the purpose of illustration only and is not intended to be used in a production environment.  
    THIS SAMPLE CODE AND ANY RELATED INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, 
    INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.  
    We grant You a nonexclusive, royalty-free right to use and modify the Sample Code and to reproduce and distribute the object 
    code form of the Sample Code, provided that You agree: (i) to not use Our name, logo, or trademarks to market Your software 
    product in which the Sample Code is embedded; (ii) to include a valid copyright notice on Your software product in which the 
    Sample Code is embedded; and (iii) to indemnify, hold harmless, and defend Us and Our suppliers from and against any claims or 
    lawsuits, including attorneys’ fees, that arise or result from the use or distribution of the Sample Code.
    #>
    
    I should note that this is not my code, but something that was linked to me once and thought I should share :)

    EDIT:
    To make it a little more pretty I modified the HTML a little bit to hide the table so the page only shows the button

    Modify the following to Here-Strings
    Code:
    $PRECONTENT = @"
    <a href="#" name="MicrosoftExcelButton" data-xl-tableTitle="PowerShell Rocks" data-xl-buttonStyle="Standard" data-xl-fileName="Book1" data-xl-attribution="Data provided by PowerShell Baby!" ></a>
    <div style="display:none">
    "@
    
    $POSTCONTENT = @"
    </div>
    <script type="text/javascript" src="http://r.office.microsoft.com/r/rlidExcelButton?v=1&kip=1"></script>
    "@
    
     
    Last edited: Nov 18, 2014
  7. GiantGuineaPig

    GiantGuineaPig Member

    Joined:
    Oct 23, 2006
    Messages:
    4,027
    Location:
    Adelaide
    I thought the exact same thing when looking at something else, took me a while to realise the variable was being set, rather than read from somewhere!
     
  8. richard0296

    richard0296 Member

    Joined:
    Jul 28, 2009
    Messages:
    2,568
    Location:
    sydney
    subbed, cheers :thumbup:
     
  9. somebloke

    somebloke Member

    Joined:
    Feb 4, 2002
    Messages:
    217
    Location:
    Perth
    Reasons I hate computers #19897FFE1A and why Empirical learning is bad

    Code:
    PS C:\Users\me> get-psdrive
    
    Name           Used (GB)     Free (GB) Provider     [COLOR="Red"] Root[/COLOR]
    ----           ---------     --------- --------      ----
    <snip>
    Y                3869.63        630.24 FileSystem    [COLOR="red"]\\server\share[/COLOR]
    
    PS C:\Users\me>Get-psdrive | ? {$_.root -eq "\\server\share"}
    PS C:\Users\me>
    PS C:\Users\me> Get-psdrive y
    
    Name           Used (GB)     Free (GB) Provider      [COLOR="red"]Root[/COLOR]
    ----           ---------     --------- --------      ----
    Y                3869.63        630.24 FileSystem    [COLOR="red"]\\server\share[/COLOR]
    
    PS C:\Users\me> Get-psdrive y | select [COLOR="Red"]root[/COLOR]
    
    [COLOR="Red"]Root[/COLOR]
    ----
    [COLOR="red"]Y:\[/COLOR]
    
    PS C:\Users\me> Get-psdrive y | fl *
    
    
    Used            : 4154987970560
    Free            : 676712808448
    CurrentLocation :
    Name            : Y
    Provider        : Microsoft.PowerShell.Core\FileSystem
    Root            : Y:\
    Description     :
    Credential      : System.Management.Automation.PSCredential
    [COLOR="red"]DisplayRoot     : \\server\share[/COLOR]
    why, why!!!
     
  10. freaky_beeky

    freaky_beeky Member

    Joined:
    Dec 2, 2004
    Messages:
    1,172
    Location:
    Brisbane
    I can see how this could be frustrating.
    I believe it is dependent on how the drive was mapped which should be able to be detected by the provider.

    You could try this bit of dirty (you may need to swap the logic for Root/DisplayRoot as I don't have anything to test this properly)

    Code:
    Get-PSDrive | ForEach-Object{if($_.DisplayRoot -match '\\\\'){$_.DisplayRoot}else{$_.Root}}
    
    The other thing you could try is finding out which provider does what and from there, doing an improved version of the above.
     
  11. OP
    OP
    PabloEscobar

    PabloEscobar Member

    Joined:
    Jan 28, 2008
    Messages:
    14,678
    Ambiguous use of property names sucks balls.

    Whatever get-psdrive runs internally to pretty up its output (which seems like its piping 5 "most" relevant things to FT) seems to doing some data munging, because it is showing Used and Free in GB, when the actually properties are just Big numbers. (UInt64).

    Today's lesson, always use "| FL *" when working out what property to filter on.
     
  12. freaky_beeky

    freaky_beeky Member

    Joined:
    Dec 2, 2004
    Messages:
    1,172
    Location:
    Brisbane
    Solid advice as always.

    Grabbing the data from WMI is much more consistent, however this will not be as "complete" an answer as what PowerShell can retrieve natively as it's limited by user context and the provider that mapped the drive.

    Code:
    Get-WmiObject -Query "select * from Win32_LogicalDisk where DriveType = 4" | Select ProviderName
    
     
    Last edited: Dec 2, 2014
  13. @kernelhack

    @kernelhack Member

    Joined:
    Feb 9, 2013
    Messages:
    60
    Location:
    Brisbane, QLD
    I truly believe Powershell is probably one of the best applications that Microsoft has developed. Simple and elegant and powerful.

    Being a mixed environment user of Windows/Linux in the workplace, I got sick of forgetting which shell I was in sometimes. I created Powershell-Bash and now I can use partial PS syntax within Linux, which essentially performs the native Linux command.
     
  14. somebloke

    somebloke Member

    Joined:
    Feb 4, 2002
    Messages:
    217
    Location:
    Perth
    I initially was searching for a mapped drive using get-wmiobject but could not figure out the syntax for the ProviderName filter

    So i used some GoogleFu, which told me to use get-psdrive, which I poked at with a stick
     
  15. OP
    OP
    PabloEscobar

    PabloEscobar Member

    Joined:
    Jan 28, 2008
    Messages:
    14,678
    If you're primaily a windows shop, but have a few *nix machines around the place, you'll be pleased to know Microsoft are trying to extend the DSC side of things out in that direction.

    http://blogs.technet.com/b/privatec...19/powershell-dsc-for-linux-step-by-step.aspx


    I'm worried that they will 'excel' it... because it makes so many things available, people will start to use it in places where there is probably a better tool for the job.

    Sure I can do my "Database" work with a bunch of Excel workbooks, but an actual database would be a better tool

    in the November WMF5 preview, they've built on Classes, and Debugging, essentailly making it easier to use powershell as a fully fledged programming langauge (Because it is, I guess :)). At what point does something move "out" of powershell, and "in" to a "real" language?

    It should be the same as above, As Freaky points out, Network Drives have a DriveType of 4 - http://msdn.microsoft.com/en-us/library/aa394173(v=vs.85).aspx

    so we can query using

    Code:
     Get-WmiObject -Query "select * from Win32_LogicalDisk where DriveType = 4" 
    This gets all our Network Drives with WMI

    to get all our mapped drives, and then we can filter them using where-object

    Code:
     Get-WmiObject -Query "select * from Win32_LogicalDisk where DriveType = 4" | Where-Object {$_.ProviderName -eq "\\server\share"} 
    This gets all our Network Drives with WMI, and then users powershell to filter for the one we want


    HOWEVER...

    If you were trying to filter for your share within your WMI query, you'd need some quotes, and the backslashes escaped (with moar backslashes)

    Code:
    Get-WmiObject -Query "select * from Win32_LogicalDisk where ProviderName = '[B]\[/B]\[B]\[/B]\server[B]\[/B]\share'"  
    This gets a single network drive with WMI


    Its probably more efficient to only fetch what you want with WMI, but (for me at least) its a metic fuckton easier to grab it all and use powershell, that way I only need to remember one set of operation operators and one set of rules about what needs quoting/escaping.

    Depending on the size of the environment and your requirements, YMMV.
     
    Last edited: Dec 2, 2014
  16. somebloke

    somebloke Member

    Joined:
    Feb 4, 2002
    Messages:
    217
    Location:
    Perth
    I can't believe how close I was to getting that correct... *siiiiigh* ah well, as long as i remember
     
  17. Embercide

    Embercide Member

    Joined:
    Jun 17, 2002
    Messages:
    1,819
    Location:
    Brisbane
    Another noob question i'm sure :Paranoid:

    Code:
    PS C:\Windows> dir *.exe | %{ [b]$_.VersionInfo[/b] }
    
    ProductVersion   FileVersion      FileName
    --------------   -----------      --------
    6.1.7600.16385   6.1.7600.1638... C:\Windows\bfsvc.exe
    6.1.7600.16385   6.1.7600.1638... C:\Windows\explorer.exe
    6.1.7600.16385   6.1.7600.1638... C:\Windows\fveupdate.exe
    
    I don't fully comprehend these two things:
    • $_.something -- What is the $_. referred to as? And is this used whenever you want a specific property of something?
    • .VersionInfo -- is this a property related to the DIR command or a property of whatever is being outputted (in this example a file property). For *any* command, how can I find out all the different types of $_.something there are?
     
  18. Ravennoir

    Ravennoir Member

    Joined:
    May 1, 2007
    Messages:
    6,510
    Location:
    Melbourne
    My understanding is that "$_" is the variable for the object being passed through (in this case from the pipe). you could do the same thing by breaking it into two lines

    Code:
    $dir = dir *.exe
    
    $dir.VersionInfo
    the .whatever is the information within that object

    I may be incorrect in my explanation, so happy for someone to correct me :)
     
  19. OP
    OP
    PabloEscobar

    PabloEscobar Member

    Joined:
    Jan 28, 2008
    Messages:
    14,678
    Nope, pretty spot on, although I don't like referencing $dir as a 'variable that refers to an object' $dir IS the object

    Aliases can make it very difficult to understand whats going on, especially when you are learning.


    So our shorthand command now looks like.

    get-childitem *.exe | foreach-object{$_.versioninfo}

    $_ refers to each individual OBJECT that is passed along the pipeline by get-childitem

    so get-childitem returns multiple objects, and they are passed down the pipeline one at a time to get your output.


    Code:
    $files = Get-ChildItem *.exe
    
    
    foreach($file in $files)
    {
    $file.VersionInfo
    }
    
    When written this way, $files stores the collection of objects, and then foreach iterates over them.



    $_ is referred to as "Dollar Underscore" It's used in Pipeline operations to represent the *current* object being passed
    .VersionInfo is a Property of whatever object is before it $_.VersionInfo represents the VersionInfo property of the current object

    Powershell cmdlets generate objects with properties that can then be referenced - The second post in the thread and the first page have a bit more discussion on how to find out all the different types of $Object.Something that an object can have - http://forums.overclockers.com.au/showpost.php?p=16315591&postcount=2
     
    Last edited: Dec 5, 2014
  20. somebloke

    somebloke Member

    Joined:
    Feb 4, 2002
    Messages:
    217
    Location:
    Perth
    layman explanation spoiled below. Read pablo's post
    $_ is the variable for the current variable in the pipe

    $a | do-something $_ AND $a | do-something $a are essentially the same. However, as MS are smarts, their commands don't need you to do that... so thats why you can just type $a | do-something.

    Also, its much faster to type than $Some_var_for_some_function.some_property

    .something ... this is a property of the data returned by the command. "the microsoft" way of determining what properties or data exist for a command (object, var, whatever) is to pipe it to get-member, however that will just show you a list, which you have to figure out how to use.

    Code:
    PS C:\Users\somebloke> get-psdrive | Get-Member
    
    
       TypeName: System.Management.Automation.PSDriveInfo
    
    Name            MemberType     Definition
    ----            ----------     ----------
    CompareTo       Method         int CompareTo(System.Management.Automation.PSDriveInfo drive), int CompareTo(System.O...
    Equals          Method         bool Equals(System.Object obj), bool Equals(System.Management.Automation.PSDriveInfo ...
    GetHashCode     Method         int GetHashCode()
    GetType         Method         type GetType()
    ToString        Method         string ToString()
    Credential      Property       pscredential Credential {get;}
    CurrentLocation Property       string CurrentLocation {get;set;}
    Description     Property       string Description {get;set;}
    DisplayRoot     Property       string DisplayRoot {get;}
    Name            Property       string Name {get;}
    Provider        Property       System.Management.Automation.ProviderInfo Provider {get;}
    Root            Property       string Root {get;}
    Free            ScriptProperty System.Object Free {get=## Ensure that this is a FileSystem drive...
    Used            ScriptProperty System.Object Used {get=## Ensure that this is a FileSystem drive...
    
    an easy way of seeing the data is to use Format-List (or FL for short) *. This shows all the properties and their value

    Code:
    PS C:\Users\somebloke> Get-PSDrive | fl *
    
    
    Used            :
    Free            :
    CurrentLocation :
    Name            : Alias
    Provider        : Microsoft.PowerShell.Core\Alias
    Root            :
    Description     : Drive containing a view of the aliases stored in session state.
    Credential      : System.Management.Automation.PSCredential
    DisplayRoot     :
    
    Used            : 94573780992
    Free            : 405164953600
    CurrentLocation : Users\er, me
    Name            : C
    Provider        : Microsoft.PowerShell.Core\FileSystem
    Root            : C:\
    Description     : OSDisk
    Credential      : System.Management.Automation.PSCredential
    DisplayRoot     :
    
    As you can see, the list is smaller, because its only showing you the properties, not methods.. which is another thing completely
     
    Last edited: Dec 5, 2014

Share This Page

Advertisement: