Reputation: 1062
I am trying to loop through an unknown number of Excel files in a folder and rename the 1st worksheet in each file to a specific name.
What I have so far is:
Clear-Host
$file = Get-ChildItem -Path "C:\PowerShell\BA" -Name -Include *.xlsx
Write-Output $file
This does list out all of the Excel files in the folder. I am then trying to run one of the commands from the ImportExcel module to rename the first worksheet to "Sheet1".
foreach ($i in $file )
{
$xl= Open-ExcelPackage $file
$sheet1 = $xl.Workbook.Worksheets[1]
$sheet1.Name ="Sheet1"
Close-ExcelPackage $xl
}
But when I run this code, I get the following error for each of the files in the folder:
WARNING: Could not find C:\WINDOWS\system32\11.25.2020_JH_BDX.xlsx 11.25.2020_JH_COV.xlsx 11.25.2020_JH_MISC.xlsx bx_1_coverage_report_2020-11-25 Final V.1 .xlsx bx_2_misc_report_2020-11-25 Final V.1 .xlsx bx_3_bordereau_report_2020-11-25 Final. V.1 .xlsx ic
at_cov_20201126053019.xlsx icat_misc_20201126053024.xlsx
Cannot index into a null array.
At line:8 char:1
+ $sheet1 = $xl.Workbook.Worksheets[1]
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
Close-ExcelPackage : Cannot bind argument to parameter 'ExcelPackage' because it is null.
At line:10 char:20
+ Close-ExcelPackage $xl
+ ~~~
+ CategoryInfo : InvalidData: (:) [Close-ExcelPackage], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Close-ExcelPackage
If I run in 32-bit instead of 64-bit, it looks like it is looking for the files here:
WARNING: Could not find C:\Users\1192643\11.25.2020_JH_BDX.xlsx 11.25.2020_JH_COV.xlsx 11.25.2020_JH_MISC.xlsx bx_1_coverage_report_2020-11-25 Final V.1 .xlsx bx_2_misc_report_2020-11-25 Final V.1 .xlsx bx_3_bordereau_report_2020-11-25 Final. V.1 .xlsx icat_
cov_20201126053019.xlsx icat_misc_20201126053024.xlsx
I'm not sure why it is looking in C:\WINDOWS\system32
or my User director for the Excel files. I have tried to ensure it looks in the correct folder by adding the full path in the foreach
block with the following:
foreach ($i in $file )
{
$xl= Open-ExcelPackage "C:\PowerShell\BA\"$file
$sheet1 = $xl.Workbook.Worksheets[1]
$sheet1.Name ="Sheet1"
Close-ExcelPackage $xl
}
But that does not work either. Could anyone help me understand what I am missing here? I am on version 5.1.17763.1490.
Updating the $xl
variable to $xl= Open-ExcelPackage $i.fullname
gives the following errors - so it seems to have the right path now, but it doesn't like the code.
Open-ExcelPackage : Cannot bind argument to parameter 'Path' because it is null.
At C:\PowerShell\BA\RenameWorksheet.ps1:23 char:24
+ $xl= Open-ExcelPackage $i.fullname
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Open-ExcelPackage], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Open-ExcelPackage
Cannot index into a null array.
At C:\PowerShell\BA\RenameWorksheet.ps1:24 char:1
+ $sheet1 = $xl.Workbook.Worksheets[1]
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
The property 'Name' cannot be found on this object. Verify that the property exists and can be set.
At C:\PowerShell\BA\RenameWorksheet.ps1:25 char:1
+ $sheet1.Name ="Sheet1"
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
Close-ExcelPackage : Cannot bind argument to parameter 'ExcelPackage' because it is null.
At C:\PowerShell\BA\RenameWorksheet.ps1:26 char:20
+ Close-ExcelPackage $xl
+ ~~~
+ CategoryInfo : InvalidData: (:) [Close-ExcelPackage], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Close-ExcelPackage
To test the code, I was able to update the worksheet name in a specific .xlsx file by using:
$xl= Open-ExcelPackage "C:\PowerShell\BA\11.25.2020_JH_MISC.xlsx"
$sheet1 = $xl.Workbook.Worksheets[1]
$sheet1.Name ="Sheet1"
Close-ExcelPackage $xl
Thanks,
Upvotes: 0
Views: 801
Reputation:
When working with PowerShell, there are two things to remember.
Here we have a variable and a loop. Both can be eliminated by switching to a pipeline.
Take care to read the help file for Get-ChildItem.
As Doug mentioned, the -Name
parameter:
-Name
Gets only the names of the items in the location. The output is a string object that can be sent down the pipeline to other commands. Wildcards are permitted.
Here we go from a FileInfo
object to a String
. So we lose valuable information like the location of each file: $_.FullName
This contributed to the issue with $i.fullname
Also, -Include
requires a special wildcard in the -Path
.
-Include
Specifies, as a string array, an item or items that this cmdlet includes in the operation. The value of this parameter qualifies the Path parameter. Enter a path element or pattern, such as "*.txt". Wildcard characters are permitted. The Include parameter is effective only when the command includes the contents of an item, such as C:\Windows\*, where the wildcard character specifies the contents of the C:\Windows directory.
Get-ChildItem
won't produce any output unless you follow this pattern or (for some reason) include the -Name
parameter. This might be why you started using the -Name
parameter in the first place.
Get-ChildItem -Path "C:\PowerShell\BA\*" -Include "*.xlsx" |
ForEach-Object {
$xl = Open-ExcelPackage $_.FullName
$sheet1 = $xl.Workbook.Worksheets[1]
$sheet1.Name ="Sheet1"
Close-ExcelPackage $xl
}
If you have a large number of files and are able to install PowerShell 7, it might be possible to use the -Parallel
parameter to speed up processing.
Get-ChildItem -Path "C:\PowerShell\BA\*" -Include "*.xlsx" |
ForEach-Object -Parallel {
$xl = Open-ExcelPackage $_.FullName
$sheet1 = $xl.Workbook.Worksheets[1]
$sheet1.Name ="Sheet1"
Close-ExcelPackage $xl
} -ThrottleLimit 5
If you're not a local admin, it is possible to get PowerShell 7 from the Store.
Either C:\WINDOWS\system32\
or $env:USERPROFILE
is the default working directory when PowerShell is loaded.
Looking at our path:
C:\WINDOWS\system32\11.25.2020_JH_BDX.xlsx
We have one default directory and one custom file.
This is an indication that we're only providing the file name to our function. E.g. $_.Name
In our case, the root cause of this error is Get-ChildItem -Name
which produces only file names as strings. It would be necessary to qualify those file names per Walter. Or to remove -Name
and use the FullName
property of FileInfo per Doug / Itchydon.
Open-ExcelPackage : Cannot bind argument to parameter 'Path' because it is null.
$xl= Open-ExcelPackage $i.fullname
Here there are two clues.
One, we just changed
$xl= Open-ExcelPackage $file
to
$xl= Open-ExcelPackage $i.fullname
So, $i.fullname
must be the issue.
Two, we can trace the logic of the error message backward.
it is null
-> parameter 'Path'
-> Open-ExcelPackage Cannot bind
-> Open-ExcelPackage $i.fullname
The two most likely possibilities are that $i
or $i.FullName
never existed in the first place.
If we check, we see that $i
has no FullName
property.
Set a breakpoint for the $xl= Open-ExcelPackage $i.fullname
line in your script file.
For example,
1 # C:\users\michael\desktop\vim\demo 1.ps1
2 $file = Get-ChildItem -Path "C:\users\michael\desktop\vim" -Name -Include *.out
3
4
5 foreach ($i in $file )
6 {
7 $xl= Open-ExcelPackage $i.fullname
8 $sheet1 = $xl.Workbook.Worksheets[1]
9 $sheet1.Name ="Sheet1"
10 Close-ExcelPackage $xl
11 }
> # from the shell
> Set-PSBreakpoint -Script '.\demo 1.ps1' -Line 7
ID Script Line Command Variable Action
-- ------ ---- ------- -------- ------
0 demo 1.ps1 7
> & '.\demo 1.ps1'
Entering debug mode. Use h or ? for help.
Hit Line breakpoint on 'C:\Users\Michael\desktop\vim\demo 1.ps1:7'
At C:\Users\Michael\desktop\vim\demo 1.ps1:7 char:1
+ $xl= Open-ExcelPackage $i.fullname
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> $i | gm
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
Clone Method System.Object Clone(), System.Object ...
CompareTo Method int CompareTo(System.Object value), i...
Contains Method bool Contains(string value)
...
> # We have a string.
> $i | Get-Member | Where-Object { $_.name -match "^f" }
> # No match for properties starting with the letter 'f'.
> # So, strings don't have a 'FullName' property.
> q # to quit the debugger
Cannot bind argument to parameter 'Path' ...
Cannot index into a null array.
The property 'Name' cannot be found on this object...
Close-ExcelPackage : Cannot bind argument ...
Oftentimes resolving the first error will either fix the code or make the larger issue apparent.
For example, we know that $sheet1 = $xl.Workbook.Worksheets[1]
(which depends on $xl being defined) won't work if $xl= Open-ExcelPackage $i.fullname
didn't work.
There isn't much point in looking at the second, third, fourth, etc errors until the first one is resolved.
The two things to understand about pipelines are that:
|
(ASCII 124) and$_
This gives us the basic tools to connect cmdlets cmdlet | cmdlet
and to refer to the properties of the current object being processed ForEach-Object { $_.FullName }
Upvotes: 0
Reputation: 18950
Don't reference $file inside the loop. If you are looking for a handle on one of the files, use $i, because that's the name you chose in the setup of your foreach. Note that I have put the $i inside the quoted string.
foreach ($i in $file )
{
$xl= Open-ExcelPackage "C:\PowerShell\BA\$i"
$sheet1 = $xl.Workbook.Worksheets[1]
$sheet1.Name ="Sheet1"
Close-ExcelPackage $xl
}
Upvotes: 1