Powershell excel paste vaule - powershell

I have one excel file with a few sheets in it. Im trying to combine all of them into one sheet. I have code that does this, but the issue im having now is now the sheets have formulas on them. So when it copies to the new sheet it doesnt copy over the values, but copies the formulas. I was reading online you can do this pastespecial, but i cant get it working. Does anyone have code on how can i copy one sheet to another, but keep the values. The one issue is the sheets have differnt amount of rows so im not sure how what the range would be. I have tried many things and i just can figure it out.
This is using the excel.application
To questions, the thing is i cant figure out the .PasteSpecial.
Here is the code now that copies it to a sheet called combine.
$wb = $excel.Workbooks.Open($location)
$newSheetName = 'Combine'
$xlCellTypeLastCell = 11
$targetSheet = $wb.Sheets.Add()
$targetSheet.Name = $newSheetName
$includeHeader = $true
foreach ($sh in $wb.Sheets) {
$statename = $sh.Name
if($sh.name -eq "Available" -or $sh.name -eq "Eligible"){
}else{
$sh.autofiltermode = $false
$sourceRange = $sh.UsedRange
if ($sourceRange.Rows.Count -le 1) { continue }
# if ($sourceRange.Rows.Count -le 1 -or $sh.Name -eq $newSheetName) {continue}
$targetLastCell = $targetSheet.UsedRange.SpecialCells($xlCellTypeLastCell)
if ($includeHeader) {
[Void]$sourceRange.PasteSpecial($targetLastCell)
}
else {
$columnOffset = - $targetLastCell.Column + 1
$targetCell = $targetLastCell.Offset(1, $columnOffset)
$newRowCount = $sourceRange.Rows.Count - 1
[Void]$sourceRange.Offset(1, 0).Resize($newRowCount).Copy($targetCell)
}
$includeHeader = $false
}
}
This does work with headers, but at this point i dont really care about those. I will say UsedRange also misses me up. So right now it i just use the .copy and from reading online i need to use the pasteSpecial.

Related

How to run Break Link option in Powershell for Multiple word document

I have word files in folder D:\xxx.docx, There are images in it but those are externally linked.
Now i want to save the docx file with Break link so that the document becomes document with embedded images.
I Found some code but not sure how to put it properly , can any one help please
$wrd = New-Object -ComObject "Word.Application"
$doc = $wrd.Documents.Open('C:\test.rtf')
$opt = [ref][Microsoft.Office.Interop.Word.WdSaveFormat]::WdFormatRTF
$name= [ref]'C:\test.rtf'
$wrd.ActiveDocument.SaveAs($name, $opt)
$wrd.ActiveDocument.Close()
$wrd.Quit()
$images = $doc.InlineShapes
foreach ($image in $images) {
$linkFormat = $image.LinkFormat
$linkFormat.SavePictureWithDocument = 1
$linkFormat.BreakLink()
}
As per my comment. See this SO Q&A similar to your use case:
Replacing all occurrences of a string in a Word file by an hyperlink
Though the OP there is talking about replacement, it's the same approach for removal.

Iterate through CSV and create an array

I am newbie to Powershell. Need a logic for CSV automation. I have a CSV log file contains large number of API calls.
I need to go row by row and segregate the data, output should be like below. Sum of calls count and average of response time to be updated.
I have written complicated If else conditions for different types of API calls and able to take the scenario name and other values from the csv. My pain starts here, struggling to come to conclusion to move forward. Can i create an array and store all the values then do all the calculation later or write the values in another csv then do all the calculation to find the Count and average response time?
If i choose array, scenario should not be duplicated. For me its really hard to take a decision without knowing the available cmdlets for array and CSV. Please throw some light..
Thanks in advance...
Here is an approach you can use a combination of c# available to Powershell (which can be MUCH more efficient handling larger files and data).
The first component is you need some consistent logic to isolate the API category you want each URL to be assigned. From your screenshots, sometimes it seems you use last segment of the URL but others it is some path in the middle of the resource.
Here is just a quick approach where you pass in an array of categories, and if it can be matched to URI in any way, then that category is used. Otherwise, the URI stands as its own category. Please replace with whatever logic you want here.
function Get-ApiCategory {
param([string[]] $Categories, [string] $Text)
foreach ($c in $Categories) {
if ($Text.IndexOf($c) -gt 0) {
return $c
}
}
return $Text # Not found
}
Then, here is a method that (1) reads the large CSV file row-by-row and uses basic parsing logic (since your source data seems simple enough) without loading the full file into memory, and then (2) exports a CSV file with summary data.
function Write-SummaryToFile {
param([string[]] $Categories, [string] $InputFile, [string] $Output)
# Parse the file line-by-line (optimize for memory)
$result = #{}
$lineNum = 0
Write-Host $InputFile
foreach ($line in [System.IO.File]::ReadLines($InputFile)) {
if ($lineNum++ -lt 1) { continue } # Skip header
$cols = $line.Split(',')
$category = Get-ApiCategory $Categories $cols[0]
$new = #{
Category = $category
Count = [int]$cols[1]
AvgResponse = [double]$cols[2]
}
if ($result.ContainsKey($category)) {
$weighted = $result[$category].AvgResponse * $result[$category].Count
$result[$category].Count += $new.Count
$result[$category].AvgResponse = ($weighted + $new.AvgResponse * $new.Count) / $result[$category].Count;
} else {
$result[$category] = $new
}
}
# Output to file
if (Test-Path $Output) { Remove-Item $Output }
try {
$stream = [System.IO.StreamWriter] $Output
$stream.WriteLine('Scenario,Count,Avg_Response_Time')
$result.Values | ForEach-Object { $stream.WriteLine([string]::Format("{0},{1},{2}", $_.Category, $_.Count, $_.AvgResponse.ToString("0.##"))) }
}
finally {
$stream.Dispose()
}
}
Then, you are able to call these methods in an example like this:
$categories = #('MoveRequestQueue', 'DeliveryDate')
Write-SummaryToFile $categories 'c:\dev\scratch\ps1\test.csv' 'C:\dev\scratch\ps1\Output.csv'

Accessing array outside of a PS function

I am having a hard time figuring out how to get the PSCustomObject/array out of the function. I have tried using $Global:ZipList as well as just passing the variables into an array directly w/o a custom object but no luck. The reason I need this, is I need to then loop through the array/list after I get the filenames and then was going to loop through this list and unzip each file and log it and process it based on the extension in the zip; this is to be used for multiple zips, so I can't predetermine the file extensions without grabbing the filenames in the zip into a list. I would just use a shell however some of the zips are password protected, haven't figured out how to pass a password scripted to the shell com unzip windows feature so stuck with 7z for now. Any help would be greatly appreciated! Thanks
Function ReadZipFile([string]$ZipFileName)
{
[string[]]$ReadZipFile = & 'C:\Program Files\7-Zip\7z.exe' l "$ZipFileName"
[bool]$separatorFound = $false
#$ZipList = #()
$ReadZipFile | ForEach-Object{
if ($_.StartsWith("------------------- ----- ------------ ------------"))
{
if ($separatorFound)
{
BREAK # Second separator; We're done!
}
$separatorFound = -not $separatorFound
}
else
{
if ($separatorFound)
{
[DateTime]$FileCreatedDate = [DateTime]::ParseExact($_.Substring(0, 19),"yyyy'-'MM'-'dd HH':'mm':'ss", [CultureInfo]::InvariantCulture)
[Int]$FileSize = [Int]"0$($_.Substring(26, 12).Trim())"
$ZipFileName = $_.Substring(53).TrimEnd()
$ZipList = [PSCustomObject] #{
ZipFileName=$ZipFileName
FileCreatedDate=$FileCreatedDate
FileSize=$FileSize}
}
}
}
}
$z = ReadZipFile $ZipFileName
$ZipList | Select-Object ZipFileName
To be able to select from array created in the function outside of it. I believe my if statements may be blocking the global variable feature when i tried using global:

How to get creation date in sitecore with powershell

I wrote a script in order to replace the "$date" in release date of many Sitecore items with their creation date (created).
I have a problem to get this field from Sitecore.
I tried this:
$rootItem = Get-Item master:/content
$sourceTemplate = Get-Item "/sitecore/content/.../item 1"
foreach($field in $sourceTemplate.Fields) {
if (($field -ne $null) -And ($field -like '$date')) {
$sourceTemplate.Editing.BeginEdit()
$CreatedDate = .......
$field.Value = [sitecore.dateutil]::ToIsoDate($CreatedDate)
$sourceTemplate.Editing.EndEdit()
}
}
I also tried to get this field by ID but it doesn't work.
Does someone have an idea please?
Thank you
If you want to check Sitecore built-in fields, you need to call $sourceTemplate.Fields.ReadAll(); first.
You should compare value of the field with $date string, not the field itself.
Then just get the string which is stored in the __Created field instead of getting date and then formatting it back to ISO date string.
And the last thing - don't call Editing.BeginEdit() and Editing.EndEdit() mutliple times for the same item - Sitecore runs some havily operations when it's called so make sure you only call it once per every item which needs it.
$sourceTemplate = Get-Item "/sitecore/content/home/test"
$sourceTemplate.Fields.ReadAll();
$editing = $false
foreach($field in $sourceTemplate.Fields) {
if ($field.Value -eq '$date') {
if (!$editing) {
$editing = $true
$sourceTemplate.Editing.BeginEdit();
}
$field.Value = $sourceTemplate.Fields["__Created"].Value
}
}
if ($editing) {
$edited = $sourceTemplate.Editing.EndEdit();
}

How can I quickly find VMs with serial ports in PowerCLI

I have a script that takes about 15 minutes to run, checking various aspects of ~700 VMs. This isn't a problem, but I now want to find devices that have serial ports attached. This is a function I added to check for this:
Function UsbSerialCheck ($vm)
{
$ProbDevices = #()
$devices = $vm.ExtensionData.Config.Hardware.Device
foreach($device in $devices)
{
$devType = $device.GetType().Name
if($devType -eq "VirtualSerialPort")
{
$ProbDevices += $device.DeviceInfo.Label
}
}
$global:USBSerialLookup = [string]::join("/",$ProbDevices)
}
Adding this function adds an hour to the length of time the script runs, which is not acceptable. Is it possible to do this in a more efficient way? All ways I've discovered are variants of this.
Also, I am aware that using global variables in the way shown above is not ideal. I would prefer not to do this; however, I am adding onto an existing script, and using their style/formatting.
Appending to arrays ($arr += $newItem) in a loop doesn't perform well, because it copies all existing elements to a new array. This should provide better performance:
$ProbDevices = $vm.ExtensionData.Config.Hardware.Device `
| ? { $_.GetType().Name -eq 'VirtualSerialPort' } `
| % { $_.DeviceInfo.Label }