Powershell: The right commandline to set a ListTemplate via powershell - list

I have an issue with a bit of code to create a word document, fill this with some lines of text, creating a list (numbering, 1., 1.1, 1.1.1, etc) and then creating an index. ($i is part of a for loop)
This works amazingly well when I just use the following line of code:
$paragraphs[0].Item($i).range.ListFormat.ApplyNumberDefault(1)
The output is then:
1., a., i.
For some reason it defaults to 'single level' lists if I put down:
$paragraphs[0].Item($i).range.ListFormat.ApplyNumberDefault(0)
Resulting in the output:
1., 2., 3.
However, using the below code obviously doesn't work, because I need a ListTemplate object to apply to the format, but I can't find any specific way to create that object in Powershell. There's some VBA examples, but I seem incapable of translating this to Powershell.
$paragraphs[0].Item($i).range.ListFormat.ApplyListTemplate('wdStyleListBullet2')
The intended end-result has to be 1., 1.1., 1.1.1. ...
(Obviously the bullet2 style is just an example, the question is how do I create the ListTemplate object in Powershell).
#Function to create a or multiple paragraphs, to prevent absurd paragraph clutter
function CreateParagraph($Selection, $count)
{
for ($i = 0;$i -lt $count;$i++){
$Selection.TypeParagraph()
}
}
#Function to create numbered lists based on a selected range of paragraphs
function NumberParagraphs($Selection, $paragraphs, $countstart, $countend, $indent)
{
$x = $false
$template = $word.ListGalleries[[Microsoft.Office.Interop.Word.WdListGalleryType]::WdBuiltinStyle].ListTemplates(2)
$template
for ($i = $countstart;$i -le $countend;$i++)
{
if (($paragraphs[0].Item($i).range.text -ne $null) -and ($paragraphs[0].Item($i).range.text -ne "") -and ($paragraphs[0].Item($i).range.text.length -gt 1))
{
#Set the listtemplate style here
#$paragraphs[0].Item($i).range.ListFormat.ApplyNumberDefault(1)
$paragraphs[0].Item($i).range.ListFormat.ApplyListTemplate($template)
}
if ($x -eq $false)
{
$indent
if ($indent -eq -1)
{
$paragraphs[0].Item($i).range.ListFormat.ListLevelNumber = 1
}
else
{
$paragraphs[0].Item($i).range.ListFormat.ListLevelNumber = $indent
}
}
$x = $true
}
}
#create Word object, create a new Word document
$Word = New-Object -ComObject Word.Application
$Word.Visible = $True
$Document = $Word.Documents.Add()
$Selection = $Word.Selection
$Range = $Selection.Range
#Add table of content
$Toc = $Document.TablesOfContents.Add($range)
#Create sample headers (Office language must be US or EN(?))
CreateParagraph $Selection 1
$Selection.Style = 'Heading 1'
$Selection.TypeText("Hello")
CreateParagraph $Selection 1
$Selection.Style = "Heading 2"
$Selection.TypeText("Report compiled at $(Get-Date).")
CreateParagraph $Selection 1
$Selection.Style = 'Heading 2'
$Selection.TypeText("Report compiled at $(Get-Date).")
CreateParagraph $Selection 1
$Selection.Style = 'Heading 2'
$Selection.TypeText("Report compiled at $(Get-Date).")
CreateParagraph $Selection 1
$Selection.Style = 'Heading 2'
$Selection.TypeText("Report compiled at $(Get-Date).")
CreateParagraph $Selection 1
$Selection.Style = 'Heading 2'
$Selection.TypeText("Report compiled at $(Get-Date).")
$Paragraphs = $Document.Range().Paragraphs
#create numbered lists.
NumberParagraphs $Selection $Paragraphs 2 2 1
NumberParagraphs $Selection $Paragraphs 3 3 2
NumberParagraphs $Selection $Paragraphs 4 5 -1
NumberParagraphs $Selection $Paragraphs 6 7 2
#Refresh table of content
$toc.Update()

After spending most of the day questioning my own sanity, I decided to go basically reverse engineer my own actions. Obviously one would expect that the $word object would contain all references required, which it does. I tested this earlier myself; It does contain the full range of templates under galleries. I had seen that before.
So I went back, revisiting what I had already attempted and what I had not and it turns out I had somehow ignored one obvious answer:
$paragraphs[0].Item($i).range.ListFormat.ApplyListTemplate($Word.ListGalleries::ListTemplates[15])
Now the only thing that might be an issue, is when, as Cindy says, the order or count of templates differentiates from one to the other workstation. I might have to build a solution for that, but that's of later concern.

You have a working Powershell script that automated Word. You'd like to use the following snippet in that script:
$paragraphs[0].Item($i).range.ListFormat.ApplyListTemplate('wdStyleListBullet2')
But, you can't quite get it to work?
I cooked up the following:
$word = New-Object -ComObject word.application
$word.Visible = $false
$doc = $word.documents.add()
$doc.paragraphs.add()
$template = $word.ListGalleries[[Microsoft.Office.Interop.Word.WdListGalleryType]::WdBuiltinStyle].ListTemplates(2)
$doc.paragraphs(1).range.ListFormat.ApplyListTemplate($template)
It's kind of what you want. I just don't know parameter to provide to ListTemplates(). It takes a number. I'm not sure which number ties to 'wdStyleListBullet2'. You have to figure that out. Unfortunately, ComObject's don't provide the same reflective abilities as .NET objects. :-(
But, to your question, that's how you'd call the ApplyListTemplate() function.

Related

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.

Rename files in Powershell with a reference file

Sorry for previous confusion...
I've spent several hours today trying to write a powershell script that will pull a client ID off a PDF from system #1 (example, Smith,John_H123_20171012.pdf where the client ID is the H#### value), then look it up in an Excel spreadsheet that contains the client ID in system 1 and system 2, then rename the file to the format needed for system 2 (xxx_0000000123_yyy.pdf).
One gotcha is that client # is 2-4 digits in system 2 and always preceeded by 0's.
Using Powershell and regular expressions.
This is the first part I am trying to use for my initial rename:
Get-ChildItem -Filter *.pdf | Foreach-Object{
$pattern = "_H(.*?)_2"
$OrionID = [regex]::Match($file, $pattern).Groups[1].value
Rename-Item -NewName $OrionID
}
It is not accepting "NewName" because it states it is an empty string. I have run:
Get-Variable | select name,value,Description
And new name shows up as a name but with no value. How can I pass the output from the Regex into the rename?
Run this code line by line in debugger, you will understand how this works.
#Starts an Excel process, you can see Excel.exe as background process
$processExcel = New-Object -com Excel.Application
#If you set it to $False you wont see whats going on on Excel App
$processExcel.visible = $True
$filePath="C:\somePath\file.xls"
#Open $filePath file
$Workbook=$processExcel.Workbooks.Open($filePath)
#Select sheet 1
$sheet = $Workbook.Worksheets.Item(1)
#Select sheet with name "Name of some sheet"
$sheetTwo = $Workbook.Worksheets.Item("Name of some sheet")
#This will store C1 text on the variable
$cellString = $sheet.cells.item(3,1).text
#This will set A4 with variable value
$sheet.cells.item(1,4) = $cellString
#Iterate through all the sheet
$lastUsedRow = $sheet.UsedRange.Rows.count
$LastUsedColumn = $sheet.UsedRange.Columns.count
for ($i = 1;$i -le $lastUsedRow; $i++){
for ($j = 1;$j -le $LastUsedColumn; $j++){
$otherString = $sheet.cells.item($i,$j).text
}
}
#Create new Workbook and add sheet to it
$newWorkBook = $processExcel.Workbooks.Add()
$newWorkBook.worksheets.add()
$newSheet = $newWorkBook.worksheets.item(1)
$newSheet.name="SomeName"
#Close the workbook, if you set $False it wont save any changes, same as close without save
$Workbook.close($True)
#$Workbook.SaveAs("C:\newPath\newFile.xls",56) #You can save as the sheet, 56 is format code, check it o internet
$newWorkBook.close($False)
#Closes Excel app
$processExcel.Quit()
#This code is to remove the Excel process from the OS, this does not always work.
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($processExcel)
Remove-Variable processExcel
I ended up using a utility called "Bulk Rename Utility" and Excel. I can run the various renaming regex's through BRU and add the reference .txt file after some Excel formatting.

RegEx PowerShell match

I have the following website http://www.shazam.com/charts/top-100/australia which displays songs, I want to capture the songs using RegEx & PowerShell. The PowerShell code below is what I have so far:
$ie = New-Object -comObject InternetExplorer.Application
$ie.navigate('http://www.shazam.com/charts/top-100/australia')
Start-Sleep -Seconds 10
$null = $ie.Document.body.innerhtml -match 'data-chart-position="1"(.|\n)*data-track-title=.*content="(.*)"><a href(.|\n)*data-track-artist=\W\W>(.|\n)*<meta\scontent="(.*)"\sitemprop';$shazam01artist = $matches[5];$shazam01title = $matches[2]
data-chart-position
data-track-title
data-track-artist
Each of the songs listed have the 3 values (above) associated with each of them, I want to capture the Artist & Title for each song based on the different chart positions (numbers). So a regular expression to find the actual chart position, then the trailing Artist & Title.
If I run the RegEx separately for Artist & Title (code below), it finds them, however it only finds the first Artist & Title. I need to find the Artist & Title for each song based on the different chart position.
$null = $ie.Document.body.innerhtml -match 'data-track-artist=\W\W>(.|\n)*<meta\scontent="(.*)"\sitemprop';$shazam01artist = $matches[2]
$null = $ie.Document.body.innerhtml -match 'data-track-title=.*content="(.*)"><a href';$shazam01title = $matches[1]
$shazam01artist
$shazam01title
Using regex to parse partial HTML is an absolute nightmare, you might want to reconsider that approach.
Invoke-WebRequest returns a property called ParsedHtml, that contains a reference to a pre-parsed HTMLDocument object. Use that instead:
# Fetch the document
$Top100Response = Invoke-WebRequest -Uri 'http://www.shazam.com/charts/top-100/australia'
# Select all the "article" elements that contain charted tracks
$Top100Entries = $Top100Response.ParsedHtml.getElementsByTagName("article") |Where-Object {$_.className -eq 'ti__container'}
# Iterate over each article
$Top100 = foreach($Entry in $Top100Entries){
$Properties = #{
# Collect the chart position from the article element
Position = $Entry.getAttribute('data-chart-position',0)
}
# Iterate over the inner paragraphs containing the remaining details
$Entry.getElementsByTagName('p') |ForEach-Object {
if($_.className -eq 'ti__artist') {
# the ti__artist paragraph contains a META element that holds the artist name
$Properties['Artist'] = $_.getElementsByTagName('META').item(0).getAttribute('content',0)
} elseif ($_.className -eq 'ti__title') {
# the ti__title paragraph stores the title name directly in the content attribute
$Properties['Title'] = $_.getAttribute('content',0)
}
}
# Create a psobject based on the details we just collected
New-Object -TypeName psobject -Property $Properties
}
Now, let's see how Tay-Tay's doing down under:
PS C:\> $Top100 |Where-Object { $_.Artist -match "Taylor Swift" }
Position Title Artist
-------- ----- ------
42 Bad Blood Taylor Swift Feat. Kendrick Lamar
Sweet!

Perl manipulation of the cisco switch commands

I have a script which helps me to login to a cisco switch nad run the mac-address table command and save it to an array #ver. The script is as follows:
#!/usr/bin/perl
use strict;
use warnings;
use Net::Telnet::Cisco;
my $host = '192.168.168.10';
my $session = Net::Telnet::Cisco->new(Host => $host, -Prompt=>'/(?m:^[\w.&-]+\s?(?:\(config[^\)]*\))?\s?[\$#>]\s?(?:\(enable\))?\s*$)/');
$session->login(Name => 'admin',Password => 'password');
my #ver = $session->cmd('show mac-address-table dynamic');
for my $line (#ver)
{
print "$line";
if ($line =~ m/^\*\s+\d+\s+(([0-9a-f]{4}[.]){2}[0-9a-f]{4})\s+/ ){
my $mac_addr = $1;
print ("$mac_addr \n");
}
}
$session->close();
It get the following results:
Legend: * - primary entry
age - seconds since last seen
n/a - not available
vlan mac address type learn age ports
------+----------------+--------+-----+----------+--------------------------
* 14 782b.cb87.b085 dynamic Yes 5 Gi4/39
* 400 c0ea.e402.e711 dynamic Yes 5 Gi6/17
* 400 c0ea.e45c.0ecf dynamic Yes 0 Gi11/43
* 400 0050.5677.c0ba dynamic Yes 0 Gi1/27
* 400 c0ea.e400.9f91 dynamic Yes 0 Gi6/3
Now, with the above script I am trying to get the mac address and store it in $mac_addr. But I am not getting the desired results. Please can someone guide me. Thank you.
I'm not clear when you say you're not getting the desired results. I did notice that you are first printing your $line and then printing $mac_addr afterwards, besides that your expression seems to match.
Your regular expression matching your desired data.
If you simply just want the matches, you could do..
for my $line (#ver) {
if (my ($mac_addr) = $line =~ /((?:[0-9a-f]{4}\.){2}[0-9a-f]{4})/) {
print $mac_addr, "\n";
}
}
Output
782b.cb87.b085
c0ea.e402.e711
c0ea.e45c.0ecf
0050.5677.c0ba
c0ea.e400.9f91
If you want to print out the mac addresses, you can do the following:
/^\*/ and print +(split)[2], "\n" for #ver;
Note that this splits the line (implicitly on whitespace) if it begins with *; the mac address is the second element in the resulting list (in case you still need to set $mac_addr).
Hope this helps!

Pick last item from list in Powershell

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