Pick last item from list in Powershell - list

I'm trying to map a drive letter using this line of code which will give me a list of drives available from d to z.
ls function:[d-z]: -n | ? { !(test-path $_) }
I'd like to then pick the last letter, not random, from the list. How would I go about doing that? New to Powershell, thanks for the help.

You can use Select-Object -Last 1 at the end of that pipeline.

you can just start at the back of the list and go up.
last item: $array[-1]
Second to last: $array[-2]
and so on.

If you look for a much more verbose, but (in my opinion) readable-improved version:
# Get all drives which are used (unavailable)
# Filter for the "Name" property ==> Drive letter
$Drives = (Get-PSDrive -PSProvider FileSystem).Name
# Create an array of D to Z
# Haven't found a more elegant version...
$Letters = [char[]]([char]'D'..[char]'Z')
# Filter out, which $Letters are not in $Drives (<=)
# Again, filter for their letter
$Available = (Compare-Object -ReferenceObject $Letters -DifferenceObject $Drives | Where {$_.SideIndicator -eq "<="}).InputObject
# Get the last letter
$LastLetter = $Available[-1]

Try this:
ls function:[d-z]: -n|?{!(test-path $_)} | Select-Object -Last 1

Another option that doesn't require trying all paths from D-Z is to parse Get-Psdrive. Here's an example:
$lettersInUse = Get-Psdrive | ? { $_.Name.Length -eq 1 } | % { $_.Name }
$lastDriveLetter = [Char]'Z'
while ($lettersInUse -contains $lastDriveLetter) {
$lastDriveLetter = [Char]($lastDriveLetter - 1)
}
$lastDriveLetter

In case you have an array
$newArray = #(git tag --list)
$lastmember = $newArray[$newArray.Count – 1]
In case you have a list
$newArray | Select-Object -Last 1

Related

Is there a way to extract only the value from "free" field of Get-PSDrive?

I'm trying to take the value of free space from a drive, this is necessary to do an automatict procedure in a database server.
I got this script:
$query_drive_mount_point = #"
select distinct
convert(varchar(512), b.volume_mount_point) as [volume],
convert(varchar(512), b.logical_volume_name) as [logical_volume]
from sys.master_files as [a]
CROSS APPLY sys.dm_os_volume_stats(a.database_id, a.[file_id]) as [b]
"#
[regex]$get_drive = '\w\:\\'
[regex]$get_drive_name = '\w'
[regex]$get_drive_space = '\d'
$mount_point = Invoke-Sqlcmd -ServerInstance "$server_ip\$sql_engine,$sql_port" -Username "$sql_user" -Password "$sql_password" -Database Master -Query "$query_drive_mount_point"
$get_disk = $get_drive.Matches($mount_point) | Foreach-Object {$_.Value}
$get_disk_name = $get_drive_name.Matches($get_disk) | Foreach-Object {$_.Value}
$size_bytes_string = Get-PSDrive $get_disk_name | Select-Object -Property Free
[int]$size_bytes = $get_drive_space.Matches($size_bytes_string) | ForEach-Object {$_.Value}
$size_giga = ( ( ( $size_bytes )/1024 )/1024 )/1024
This code runs without problem until this line:
[int]$size_bytes = $get_drive_space.Matches($size_bytes_string) | ForEach-Object {$_.Value}
It throws this error:
There was not found overload for "Matches" and args numbers is "1".
En línea: 1 Carácter: 1
+ $size_bytes = $get_drive_space.Matches($size_bytes_string) | ForEach- ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
The last error is a traduction made by me, the os is in spanish.
Again, the objective is to store only the free space value.
You need to select the value from the property you need, so this line:
$size_bytes_string = Get-PSDrive $get_disk_name | Select-Object -Property Free to
Should be changed to the following:
$size_bytes_string = Get-PSDrive $get_disk_name | Select-Object -ExpandProperty Free
Alternatively you can format it as follows:
($size_bytes_string = Get-PSDrive $get_disk_name).Free
If you have PS version 5:
PS C:\> (($PSVersionTable).PSVersion)
Major Minor Build Revision
----- ----- ----- --------
5 1 18362 145
if not version 5, try to import the storage module.
You can get this information easier by using the below commandlet:
Get-disk # the command name
Get-Command -Module storage -Verb get -Noun disk # module
onliner code to select the size of the first hdd (disk 0):
[math]::Round((Get-Disk -Number 0).Size /1GB)
- Edited :
=========================================================================
if you load this assembly, you can get all the information you need,
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
$sql = New-Object ('Microsoft.SqlServer.Management.Smo.Server')
You can run this commandlet to find what information you can get :
(($sql | gm) | measure).count
For example :
write-host $dbs.Parent
$dbs=$s.Databases
I believe you need a property called filegroup to get information about ldf&mdf files.

Compare two strings in powershell, regex * match, output DataSpace_ID field from string1 based on match, into switches listing string in own column

I have a list of switches in CSV and a list of data spaces where these switches are. In my list of Data Spaces, I have a DataSpace_ID field which represents its associated DataSpace_Name.
My list of switches has a Host_Name and IP_Address fields. What I want is using PowerShell and regex matching using Wildcards, I want to match the DataSpace field example, "ABC-COM" to the switch listing Host_Name which would be ABC-COM-3750-SW1. I only want to match up to ABC-COM...
Then for my result I want the output, based on the matches found, to associate the DataSpace_ID value found and include it in the output of the switch listing.
Let's say I match ABC-COM = DATASPACE_ID 1 and DEF-COM = DataSpace_ID 2, and my switch data is:
Host_Name IP_Address
ABC-COM-3750-SW1 IP 192.168.1.2
ABC-COM-3750-SW2 IP: 192.168.1.3
DEF-COM-3750-SW1 IP: 192.168.3.5
DEF-COM-3750-SW2 IP: 192.168.3.6
So, in the end you would have this output from the switch listing based on comparison of the dataspace listing, except it would add the DataSpace_ID Column from the other comparison listing of data space names... Switch listing Output would look like this:
DataSpace_ID Host_Name IP_Address
1 ABC-COM-3750-SW1 IP 192.168.1.2
1 ABC-COM-3750-SW2 IP: 192.168.1.3
2 DEF-COM-3750-SW1 IP: 192.168.3.5
2 DEF-COM-3750-SW2 IP: 192.168.3.6
Here is my latest code revised based on some of your input, I am not getting errors any longer, however my output is not returning any results either.
clear-host
$hash.clear()
$dataSpacesExport = Import-Csv -Path .\DataSpaces_Export.csv -Header 'DataSpace_ID', 'DataSpace_Name' -Delimiter ","
$accessSwitchesForExport = Import-Csv -Path .\AccessSwitchesForExport.csv -Header 'Host_Name', 'IP_Address' -Delimiter ","
# create hashtable
$hash = #{}
# Create Regex criteria
$re = [regex]".+(?=-\d+)"
$dataSpacesExport | ConvertFrom-Csv | % { $hash.Add($_,”$_”) }
# output
$accessSwitchesForExport | ConvertFrom-Csv |
Select-Object #{ n = "DataSpace_ID"; e = { $hash[$re.Match($_.Host_Name).Value] } },* |
Where-Object { $_.DataSpace_ID -ne $null }
My CSV files as some have asked for, example data would be:
DataSpaces and switches output examples are below in the post. DataSpaces contain a DataSpace_ID and DataSpace_Name, and switches csv contain a Host_Name and IP_Address fields.
Output, like below, based on comparison of two csv's should show:
Matching DataSpace_ID with matching Host_Name, and its associated IP Address in final table.
This is a solution using a hash table.
$dataSpacesExport = #"
DataSpace_ID,DataSpace_Name
1,ABC-COM
2,DEF-COM
"#
$accessSwitchesForExport = #"
Host_Name,IP_Address
ABC-COM-3750-SW1,IP: 192.168.1.2
ABC-COM-3750-SW2,IP: 192.168.1.3
DEF-COM-3750-SW1,IP: 192.168.3.5
DEF-COM-3750-SW2,IP: 192.168.3.6
GHI-COM-3750-SW2,IP: 192.168.3.6
"#
$re = [regex]".+(?=-\d+)"
# create hashtable
$id = #{}
$dataSpacesExport | ConvertFrom-Csv | ForEach-Object { $id[$_.DataSpace_Name] = $_.DataSpace_ID }
# output
$accessSwitchesForExport | ConvertFrom-Csv |
Select-Object #{ n = "DataSpace_ID"; e = { $id[$re.Match($_.Host_Name).Value] } },* |
Where-Object { $_.DataSpace_ID -ne $null }
The output is as follows.
DataSpace_ID Host_Name IP_Address
------------ --------- ----------
1 ABC-COM-3750-SW1 IP: 192.168.1.2
1 ABC-COM-3750-SW2 IP: 192.168.1.3
2 DEF-COM-3750-SW1 IP: 192.168.3.5
2 DEF-COM-3750-SW2 IP: 192.168.3.6
The following code is another solution. In this case, you do not need a regular expression.
$dataSpaces = $dataSpacesExport | ConvertFrom-Csv
$accessSwitchesForExport | ConvertFrom-Csv | ForEach-Object {
foreach ($ds in $dataSpaces) {
if (!$_.Host_Name.StartsWith($ds.DataSpace_Name)) { continue }
[pscustomobject]#{
DataSpace_ID = $ds.DataSpace_ID
Host_Name = $_.Host_Name
IP_Address = $_.IP_Address
}
break
}
}
Thank you everyone for your help! I used bits and pieces of the recommendations above to come up with the following result which works perfectly and generates that data needed.
#Set Present Working Directory for path to save data to.
#Clear any Hash Table Data prior to start of script //
$id.clear()
#Import current listing of Data Spaces and Access switches from CSV format //
$dataSpacesExport = import-csv -Header DataSpace_ID, DataSpace_Name -Path ".\DataSpaces_Export.csv"
$accessSwitchesForExport = import-csv -Header Host_Name, Device_IP -Delimiter "," -Path ".\AccessSwitchesForExport.csv"
#Regex text matching criteria //
$re = [regex]".+(?=-\d+)"
# create hashtable to store output //
$id=#{}
# Inject DataSpaces listing into Script for processing via hash table $id //
$dataSpacesExport | % {$id[$_.DataSpace_Name] = $_.DataSpace_ID}
# output - Compare Access Switch listing to DataSpaces Hashtable information, produce output to out-file sw_names.txt //
$accessSwitchesForExport |
Select-Object #{ n = "DataSpace_ID"; e = { $id[$re.Match($_.Host_Name).Value] } },* |
Where-Object { $_.DataSpace_ID -ne $null } | Out-File ./sw_names.txt -Force
Output is as expected and is now working.

Split Strings in a Value column with Powercli

This is what I wrote to get output with powercli;
Get-VM -name SERVERX | Get-Annotation -CustomAttribute "Last EMC vProxy Backup"|select #{N='VM';E={$_.AnnotatedEntity}},Value
This is the output
VM Value
-- -----
SERVERX Backup Server=networker01, Policy=vmbackup, Workflow=Linux_Test_Production, Action=Linux_Test_Production, JobId=1039978, StartTime=2018-10-31T00:00:27Z, EndTime=2018-10-31T00:12:45Z
SERVERX1 Backup Server=networker01, Policy=vmbackup, Workflow=Linux_Test_Production, Action=Linux_Test_Production, JobId=1226232, StartTime=2018-12-06T00:00:29Z, EndTime=2018-12-06T00:0...
SERVERX2 Backup Server=networker01, Policy=vmbackup, Workflow=Linux_Test_Production, Action=Linux_Test_Production, JobId=1226239, StartTime=2018-12-05T23:58:27Z, EndTime=2018-12-06T00:0...
But I would like retrieve only "starttime" and "endtime" values
Desired output is;
VM Value
-- -----
SERVERX StartTime=2018-10-31T00:00:27Z, EndTime=2018-10-31T00:12:45Z
SERVERX1 StartTime=2018-12-06T00:00:29Z, EndTime=2018-1206T00:11:14Z
SERVERX2 StartTime=2018-12-05T23:58:27Z, EndTime=2018-12-06T00:11:20Z
How can I get this output?
This would be better suited in Powershell forum as this is just data manipulation.
Providing your output is always the same number of commas then
$myannotation = Get-VM -name SERVERX | Get-Annotation -CustomAttribute "Last EMC
vProxy Backup"|select #{N='VM';E={$_.AnnotatedEntity}},Value
$table1 = #()
foreach($a in $myannotation)
$splitter = $a.value -split ','
$splitbackupstart = $splitter[5]
$splitbackupend = $splitter[6]
$row = '' | select vmname, backupstart, backupend
$row.vmname = $a.AnnotatedEntity # or .vm would have to try
$row.backupstart = $splitbackupstart
$row.backupend= $splitbackupend
$table1 += $row
}
$table1
Untested. If you format of the string is going to change over time then a regex to search for starttime will be better.

How to group and show multiple directory names with Powershell Get-ChildItem

I would like to pass multiple directories, then get a count of how many files were created yesterday in each directory.
This is what I have cobbled together so far:
$DirNames = #('d:\Work\Test1','d:\Work\Test2')
Get-ChildItem $DirNames | Where-Object { $_.CreationTime -ge (get-date).addDays(-1) } |
Group {$_.LastWriteTime.ToString("yyyy-MM-dd")} | Sort Name -Desc |
Format-Table Name,Count -auto
Current Output:
Name Count
---- -----
2014-03-25 4
It works fine for one directory, but I'm get confused how to show the the directory name in the output listing. The above count is for both directories, what I would like to see is:
Desired Output:
Directory Date Count
------------ ---------- -----
D:\Work\Test1 2014-03-25 2
D:\Work\Test2 2014-03-25 2
try this:
ls $env:temp| group {$_.lastwritetime.tostring('yyyy-MM-dd')} |
ft name,count,#{
'n' = 'Directory';
'e' = { $_ |select -expa group | select -first 1 -expa psparentpath | Convert-Path }
}
or you can use new pipelinevariable parameter with version 4

use perl to get information using regexs

All,
I have the following output from a command that I run in Perl. However, I am only interested in capturing the components and their respective statuses. (I.e. the "component" and "status" columns)
I've been thinking about how to approach this using regular expressions. I would like to assume that i will not really know the names of the components as in the future there may be additional components in the column. I don't care about the two middle columns (process-type or pid).
Any suggestions would be helpful.
my $consoleStatus = opmnctl status 2>&1;
-------------------+--------------------+---------+---------
component | process-type | pid | status
-------------------+--------------------+---------+---------
serverpro | logloaderd | N/A | Down
www-daemon | www-daemon | 10000 | Alive
OXQA | oqa | 99894 | Alive
SDFW | OC4X_SECURITY | 27683 | Alive
FTP_Servers | HTTP_Server | 21252 | Alive
OID | OID | 27207 | Alive
DSA | DSA | N/A | Down
Regards,
Assuming the layout of your output doesn't change, the component name doesn't have spaces, and the possible status are only 'Alive' and 'Down', you could use the given regex to match each line:
/^(\S+)\s+\|.+\|\s+(Alive|Down)$/
Below, I wrote a code that gets the input from STDIN, and prints out the component and it's status:
while(<STDIN>) {
if( $_ =~ /^(\S+)\s+\|.+\|\s+(Alive|Down)$/ ) {
print "$1 -> $2\n";
}
}
You might be able to use opmnctl options to simplify what the Perl has to process, maybe:
opmnctl status -noheaders -fsep '|' -fmt %cmp%sta
I'd suggest using split, and split on the pipe | characters that delimit the fields.
Here's a short snippet that may give you some ideas. If you can use some opmnctl options, you'd be able to simplify this.
use strict;
use warnings;
use Data::Dumper;
my %component_status;
LINE: for ( split( /\n/, $consoleStatus ) ) {
# Skip the furniture in the opmnctl output
next LINE if m/^component\W/ || m/^-/;
# field 0 is the component, field 3 the status.
my ( $component, $status ) = (split( / +\| */, $_ ))[0,3];
$component_status{$component} = $status;
}
warn Dumper( \%component_status );
Result:
$VAR1 = {
'DSA' => 'Down',
'FTP_Servers' => 'Alive',
'SDFW' => 'Alive',
'serverpro' => 'Down',
'OID' => 'Alive',
'OXQA' => 'Alive',
'www-daemon' => 'Alive'
};