Due to restrictions I either need to use VB or PowerShell for this task.
I have an Excel that looks like:
ColumA, ColumB,ColumC,ColumD,ColumE,ColumF
000|Txt,MoreTxt , ColumB,ColumC,ColumD,ColumE,ColumF
I read about import_csv -header, but I'm under to successfully do it. I'll post my script below. The export I expect is:
ColumA, ColumB, ColumC, ColumD, ColumE, ColumF
000, ColumB, ColumC, ColumD, ColumE, ColumF
Only Colum gets modified, and I -only- need the digits from before that pipe. It also has to stay three digits, so 1 becomes 001, etc.
This is the script I modified based on some previous inquiries I saw, and the MS Tutorial.
$file = import-csv "C:\path\to\my\file\test.csv"
foreach ($row in $file){
$tempfile = New-Object psobject -Property #{
ColumA = $row. 'ListName'.substring(0,2)
ColumB = $row. 'ColumB'
ColumC = $row. 'ColumC'
ColumE = $row. 'ColumE'
ColumF = $row. 'ColumF'
}
$expandfile = #()
$expandfile += $tempfile | select ColumA, ColumB, ColumC, ColumD, ColumE, ColumF
}
PS gives me both errors on not liking everything I have in quotes (Which I thought was the column name, but I guess not. And also a parse error on the entire array. Essentially the entire script.
UPDATE
Providing real examples of source.
"Tiam
Name",SiamName,Siam,Ciam,Piam,Liam,Niam,Diam
"002|City, State","City, State - Some text (15092)",1,"3,408",99,"3,408",780,22.89%
"009|City, State","City, State - Some Text (E) (15450)",1,"1,894",81,"1,894",543,28.67%
Edit:
$expandfile = Import-Csv "C:\path\to\my\file\test.csv" | ForEach-Object {
$_."Tiam`r`nName" = $_."Tiam`r`nName".SubString(0,3)
$_
}
Related
questions 1.
The folder name is written in column A of Excel.
In column B of Excel, the file name to be modified for each folder is written.
There are a number of folders to run.
When the folder name in Excel column A is the same, execute the Powershell source in each folder.
Powershell source to run
$nr = 1
dir |
ForEach{ Rename-Item $_ -NewName ( 'B column data must be entered_{0}.jpg' -f $nr++) }
That is, when the cell value of Excel A1 is the same as the folder name
I am trying to change the name of a number of jpg files in the file with the cell value of Excel B1 and add a number afterward.
questions 2.
The folder name is written in column A of Excel.
In column B of Excel, I wrote down the Powershell source values to be applied to each folder.
The reason why you need to apply the Powershell source for each folder is that there are multiple folders in the folder and the names of each folder are different.
That is, when executing Powershell, the Excel column A value is compared, and when the folder name is the same, the Powershell source written in the Excel column B is applied.
In conclusion, questions 1 and 2 will give the same result.
It doesn't matter what you do.
To solve this, we try to execute a For statement or a Foreach statement.
Can you help. please
Since you want to use data from an Excel sheet, here's a function that reads a single Excel sheet column and return it as array
function Import-ExcelColumn {
# returns an array of Excel Column values
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
[string]$Path,
[int]$WorkSheetIndex = 1,
[int]$ColumnIndex = 1,
[switch]$SkipHeader,
[switch]$RemoveEmptyValues
)
# constants from https://learn.microsoft.com/en-us/office/vba/api/excel.xldirection
$xlDown = -4121
$xlUp = -4162
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$workbook = $excel.Workbooks.Open($Path)
$worksheet = $workbook.Worksheets.Item($WorkSheetIndex)
# get the first and last used row indices
$firstRow = $worksheet.Cells($worksheet.UsedRange.Rows.Count, 1).End($xlUp).Row
$lastRow = $worksheet.Cells($firstRow, 1).End($xlDown).Row
if ($SkipHeader) { $firstRow++ }
### write-host "first: $firstRow last: $lastRow"
# collect the values in this column in variable $result
$result = for ($row = $firstRow; $row -le $lastRow; $row++) {
$worksheet.Cells.Item($row, $ColumnIndex).Value2
}
$excel.Quit()
# IMPORTANT: clean-up used Com objects
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($worksheet)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
if ($RemoveEmptyValues) { $result = $result | Where-Object { $_ -match '\S' }
# $result is an array. PowerShell 'unravels' arrays when returned from a function.
# to overcome this, prefix the returned array with a unary comma.
return ,$result
}
Having that function in place at the top of your code (and looking at your previous questions about this), you only need to read ColumnA in which the foldernames to process are stored.
At the moment, you have another column ColumnB that holds the powershell code to do the renaming of the files inside these folders. However, since that code is the same for every folder, you don't need that at all.
The code should rename every .jpg file in the folders to become <directoryName>_<sequenceNumber>.jpg, where is a numeric value starting at 1 and incrementing as you go through the files and <directoryName> is the name of the file's parent directory.
# enter the full path to your Excel file here
$ExcelFile = 'Full\Path\To\The\Excel\File.xlsx'
# this is the folder where all subfolder mentioned in the Excel file are
$rootFolder = 'C:\Image'
# read ColumnA (1st column) from the Excel file and collect the values in an array
$folderList = Import-ExcelColumn -Path $ExcelFile -SkipHeader -RemoveEmptyValues
# loop through the array of subfolder names and process each one
foreach ($dir in $folderList) {
# combine the rootfolder and the subfolder to get a complete path
$path = Join-Path -Path $rootFolder -ChildPath $dir
# initialize the sequence number to 1
$nr = 1
# get a list of FileInfo objects of all .jpg files in that folder
(Get-ChildItem -Path $path -Filter '*.jpg' -File) |
ForEach-Object {
$_ | Rename-Item -NewName ('{0}_{1}.jpg' -f $_.Directory.Name, $nr++ )
}
}
I am trying to write a script in powershell that can loop through Excel sheets from a prepared Excel file and extract a range of values in each sheet, which I then pipe into the import-csv cmdlet. This is the first step in a larger script that acts on the csv files; I am trying to consolidate all the steps into 1 convenient script.
My problem is that I need:
the script to work without Excel installed (rules out COM object Excel.Application)
cannot install powershell modules (rules out the popular ImportExcel).
is usable on xlsx files (rules out jet 4.0 with excel object 8.0)
Doesn't require downloads/admin permissions to directories or has simple workarounds for this.
In short:
Is importing an excel sheet to CSV via a PowerShell script possible with only pre-installed Windows functionalities?
The next best thing would be minimal adjustments, such as bundling a small library with the script that can be easily referenced in the script (would Open XML SDK or EPPlus 4.5.3.3 fall into this category?).
Thank you.
It's possible to work with the raw Excel data, but you will have to "reverse engineer" the format. I was able to get some useful data from a very simple sheet.
To test and play around with this create an empty folder and save an Excel document as Book1xlsx with some values like this:
| Name | Value |
| adf | 5 |
| fgfdg | 4 |
| dfgdsfg | 3 |
Then place this script there, and see the result. If your data is any more advanced with this, you probably have to spend quite a bit of time figuring out how different types and sheets are named, and how to look them up.
unzip Book1.xlsx
[xml]$sheet = Get-Content "xl\worksheets\sheet1.xml"
[xml]$strings = Get-Content "xl\sharedStrings.xml"
$stringsTable = $strings.sst.si.t
$data = $sheet.worksheet.sheetData.row | % {
# Each column for each row is in the "c" variable
# (The ,#() is a hack to avoid powershell from turning everything into a single array)
return ,#($_.c | % {
# There is a "t" property that represents the type.
if ($_.t -like "s") {
# "s" means a string. To get the actual content we need to look up in the strings xml
return $stringsTable[$_.v]
} elseif ($_.t -like "") {
# Empty type means integer, we can return the value as is
return $_.v
}
})
}
# Data will be a 2 dimensional array
# $data[0][0] will refer to A1
# $data[1][0] will refer to A2
# $data[0][1] will refer to B1
$data
Hopefully this will be enough to get you started.
Edit:
Here is also some code to convert the 2 dimensional array into a PSObject you can use with Export-Csv.
$headers = $data[0]
$dataObject = $data | Select-Object -Skip 1 | % {
$row = $_
$index = 0
$object = #{}
foreach ($column in $row) {
$object[$headers[$index]] += $column
$index++
}
return [PSCustomObject]$object
}
$dataObject | Export-Csv ...
I'm trying to write data to specific column in an Excel spreadsheet with PowerShell. I would like to start below last row with data and continiue downwards. On machine I don't have Excel installed so COM won't work for me. I'm currently using Import-Excel to read whole sheet and used Open-ExcelPackage to read specific cell values.
I could do this in CSV file as opposed to .xlsx if it's easier.
Any help would be great!
Download PSExcel module from https://github.com/RamblingCookieMonster/PSExcel Import it using Import-Module.
Then use the following code:
$File = "Path to xlxs file"
$WSName = "SheetName"
$Excel = New-Excel -Path $File
$Worksheet = $Excel | Get-WorkSheet -Name $WSName
$SampleRows = #()
$SampleRows += [PSCustomObject]#{"A" = 1; "B" = 2; "C" = 3; "F" = 4 }
$row_to_insert = $SampleRows.count
$Worksheet.InsertRow($Worksheet.Dimension.Rows,$row_to_insert)
$WorkSheet.Cells["$($Worksheet.Dimension.Start.Address -replace ""\d"")$($Worksheet.Dimension.End.Row):$($Worksheet.Dimension.End.Address)"].Copy($WorkSheet.Cells["$($Worksheet.Dimension.Start.Address -replace ""\d"")$($Worksheet.Dimension.End.Row - $row_to_insert):$($Worksheet.Dimension.End.Address -replace ""\d"")$($Worksheet.Dimension.End.Row - $row_to_insert)"]);
$WorkSheet.Cells["$($Worksheet.Dimension.Start.Address -replace ""\d"")$($Worksheet.Dimension.End.Row):$($Worksheet.Dimension.End.Address)"] | % {$_.Value = ""}
ForEach ($Row in $SampleRows) {
ForEach ($data in $Row.PSObject.Properties.Name) {
$WorkSheet.Cells["$data$($Worksheet.Dimension.Rows)"].Value = $SampleRow.$data
}
}
$Excel | Close-Excel -Save
This code adds 1 row after the last row in the selected worksheet and adds values to this row from $SampleRows.... I think you got the idea. if you need add more rows to $SampleRows array.
I have a txt file clients.txt that contains
c:\report1.xlm
c:\report2.xml
I want this to run against each xml
$ws = $wb.Sheets.Item('Report')
$ws.UsedRange.RemoveDuplicates($Columns)
$page = "Report"
Are you trying to substitute 'Report' with the xml reference from clients.txt?
If so:
$reports = get-content c:\clients.txt
$reports | ForEach-Object {
$ws = $wb.Sheets.Item($_)
$ws.UsedRange.RemoveDuplicates($Columns)
$page = "Report"
}
I'm not 100% sure what you are attempting to do.
I just recently joined an IAM team, and this month had to send out hundreds of emails to people notifying them of an account expiration (they are asked to either request for an extension or termination of the account). Thankfully, there's already a script made to do that part, but for dealing with the responses there is not. There's an excel spreadsheet where I record what is to happen to each account. I was hoping to make a script that can go through each of the responses and mark in the desired field in the spreadsheet accordingly. I've been having trouble with the part of the script where I modify the value under the desired field for the user.
I'm fairly new to PowerShell, so I'm not sure what the issue is. I already spent a few hours looking online and found quite a few possible solutions, but none of them have worked for me. A common problem is apparently using an older excel file, but it's fresh and it's Excel 2016. Another one is not having the correct file type, but I checked and that's not it either. The line of code in question is $extend.Cells.Item($modifyCell.Cells.Row) = "$data".
Any ideas what the problem could be?
Code:
# Path to .msg files
$msgDir = "C:\Users\me\Desktop\Test"
# Array to store results
$msgArray = New-Object System.Collections.Generic.List[object]
# Loop throuch each .msg file
Get-ChildItem "$msgDir" -Filter *.msg |
ForEach-Object {
# Open .msg file
$outlook = New-Object -comobject outlook.application
$msg = $outlook.Session.OpenSharedItem($_.FullName)
# Add .msg file Subject and Body to array
$msgArray.Add([pscustomobject]#{Subject=$msg.Subject;Body=$msg.Body;})
$msg.Close(0) # Close doesn't always work, see KB2633737 -- restart ISE/PowerShell
}
# Loop though / parse each message
ForEach ($message in $msgArray) {
$subject = $message.subject
$body = $message.body
$regex = [regex] '\s*(\w*)\s*\|$'
If ($body -match $regex) {
$username = $body
}
$parse = $body | Select-String -Pattern "Please extend"
If ($parse -eq "Please extend") {
$data = "Y"
}
}
# Open Excel
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $True
$OpenFile = $Excel.Workbooks.Open("C:\Users\me\Desktop\test.xlsx")
$Workbook = $OpenFile.Worksheets
$Worksheet = $Workbook.Item(1)
# Get the values for each column
$samacctname = $Worksheet.Cells | where {$_.value2 -eq "SAM Account Name"} | select -First 1
$extend = $Worksheet.Cells | where {$_.value2 -eq "Extend"} | select -First 1
# Get the values for each row in SAM Account Name
$userValues = #()
for($i=2; $samacctname.Cells.Item($i).Value2 -ne $null; $i++ ){
$userValues += $samacctname.Cells.Item($i)
}
# Get the values where the cell value of SAM Account matches the username
$modifyCell = $userValues | where {$_.Value2 -eq $username}
# Modify the Extend cell using the username's row position
$extend.Cells.Item($modifyCell.Cells.Row) = "$data"
# Save the file
$OpenFile.Save()
Edit 1: I went back into my code and first tried to hard-code the data value I was trying to add to the cell, but I still got the same error. I then tried hard-coding it right when I call the line $extend.Cells.Item($modifyCell.Cells.Row) = "Y" and it works as it should. So how I'm trying to use regex to pull the username is likely not right. Probably how I'm pulling the data as well.