Looping Through Files to Run PowerShell Script - powershell

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,

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
}

When working with PowerShell, there are two things to remember.
PowerShell is object-oriented.
PowerShell uses pipelines.
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.
Final Answer
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.
Get PowerShell - Microsoft Store
ForEach-Object (Microsoft.PowerShell.Core) - PowerShell | Microsoft Docs
What's New in PowerShell 7.0 - PowerShell | Microsoft Docs
Interpreting Error Messages
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.
Debugging
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.
Pipelines
The two things to understand about pipelines are that:
Cmdlets are connected with pipes | (ASCII 124) and
An automatic variable is used to represent the current object being processed: $_
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 }

Related

PowerShell script to set "date created" to specific times according to a csv file

I have some thousands of files in a folder that I need to bulk edit their creation time to a specific order.
I have prepared a csv file with all file names and the preferred creation times, like this:
filename;filecreationTime;
file1.mp4;10/11/2022 2:50;
file2.mp4;10/11/2022 2:49;
file3.mp4;10/11/2022 2:49;
etc
I have used this suggestion to a similar previous question: https://stackoverflow.com/a/36348448/20467894 and created a code like this:
Set-Location 'path to files'
Import-Csv -Path 'path to csv file' |
ForEach-Object { (Get-Item $_.filename).CreationTime = (Get-date $_.filecreationTime) }
The outcome is this error, for each line of the csv:
Get-Item : Cannot bind argument to parameter 'Path' because it is null.
At line:2 char:32
+ ForEach-Object { (Get-Item $_.filename).CreationTime = (Get-date ...
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-Item], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.GetItemComm
and
What am I doing wrong?
EDIT: After Theos' comment, it run once in a sample subset of my files but never run again. Now it can indeed read the filenames, but it brings a new error:
Get-date$_.filecreationTime : The term 'Get-date$_.filecreationTime' is not recognized as the name of a cmdlet, functio
n, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:2 char:57
+ ... (Get-Item $_.filename).CreationTime = (Get-date$_.filecreationTime) }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Get-date$_.filecreationTime:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Thanks everyone for your suggestions.
As pointed out by Theo, the problem was that in my language and regional formats the delimiter of the csv files is set to ";" instead of ",", as the latter is used as decimal...
So I had to insert an additional argument into the import csv (that was not obvious by the error expression), in order to clarify the non default delimiter:
-Delimiter ';'
So, the correct code for me was the following:
Set-Location 'path to files'
Import-Csv -Delimiter ';' -Path 'path to csv file' |
ForEach-Object { (Get-Item $_.filename).CreationTime = (Get-date $_.filecreationTime) }

“Rename-item: Cannot rename because item at ... does not exist” from multiple subfolders in Powershell [duplicate]

I modified PowerShell script from PowerShell - Batch change files encoding To UTF-8.
# Modified version of https://stackoverflow.com/q/18684793
[Threading.Thread]::CurrentThread.CurrentUICulture = 'en-US'
$Encoding = New-Object System.Text.UTF8Encoding($True) # If UTF8Encoding($False), It will be UTF-8 without BOM
$source = "C:\Users\AKULA\Desktop\SRC" # source directory
$destination = "C:\Users\AKULA\Desktop\DST" # destination directory
if (!(Test-Path $destination)) {
New-Item -Path $destination -ItemType Directory | Out-Null
}
# Delete all previously generated file
Get-ChildItem -Path $destination -Include * -File -Recurse | ForEach-Object {$_.Delete()}
# Recursively convert all files into UTF-8
foreach ($i in Get-ChildItem $source -Force -Recurse -Exclude "desktop.ini") {
if ($i.PSIsContainer) {
continue
}
$name = $i.Fullname.Replace($source, $destination)
$content = Get-Content $i.Fullname
if ($null -ne $content) {
[System.IO.File]::WriteAllLines($name, $content, $Encoding)
} else {
Write-Host "No content from: $i"
}
}
But after using it, I've found that PS cannot handle [ or ] well.
I made some test files that has diversity in name/content.
Get-Content : An object at the specified path C:\Users\AKULA\Desktop\SRC\FILENAME[[[[[[]]]]]]]].txt does not exist, or
has been filtered by the -Include or -Exclude parameter.
At C:\Users\AKULA\Desktop\Convert_to_UTF-8.ps1:24 char:16
+ $content = Get-Content $i.Fullname
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (System.String[]:String[]) [Get-Content], Exception
+ FullyQualifiedErrorId : ItemNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Since I cannot embed images in question, here is link of IMGUR album.
Full image list: https://imgur.com/a/aN1RG2L
These are what I've tested:
Test files have different names. Their name contains space, ',
[]. Also made up different language(Japanese, Korean).
These files have same content, encoded with UCS-2 BE BOM(UTF-16 BE) so
that I can check if it has re-encoded to UTF-8.
How can I make my script handle [ or ] in file name well?
tl;dr
Indeed, use of the -LiteralPath parameter is the best solution (in PowerShell (Core) v6+, you can shorten to -lp):
$content = Get-Content -LiteralPath $i.Fullname
-LiteralPath ensures that $i.Fullname is taken verbatim (literally); that is, [ and ] in the path are interpreted as themselves rather than having special meaning, as they would have as a -Path argument, due to being interpreted as a wildcard expression - note that -Path is positionally implied if you only pass a value (a string) as the first argument, as you did (Get-Content $i.FullName)
Note: This answer analogously applies to all cmdlets that have both -Path and -LiteralPath parameters, such as Set-Content, Out-File, and Set-Location.
As for what you tried:
$content = Get-Content $i.Fullname
is effectively the same as:
$content = Get-Content -Path $i.Fullname
That is, the (first) positional argument passed to Get-Content is implicitly bound to the
-Path parameter.
The -Path parameter accepts wildcard expressions to allow matching paths by patterns; in addition to support for * (any run of characters) and ? (exactly 1 character), [...] inside a wildcard pattern denotes a character set or range (e.g., [12] or [0-9]).
Therefore an actual path that contains [...], e.g., foo[10].txt, is not recognized as such, because the [10] is interpreted as a character set matching a single character that is either 1 or 0; that is foo[10].txt would match foo0.txt and foo1.txt, but not a file literally named foo[10].txt.
When (implicitly) using -Path, it is possible to escape [ and ] instances that should be interpreted verbatim, namely via the backtick (`), but note that this can get tricky to get right when quoting and/or variable references are involved.
If you know a path to be a literal path, it is best to form a habit of using -LiteralPath (which in PowerShell Core you can shorten to -lp).
However, if your path contains literal [ and ] and you also need wildcard matching, you must use `-escaping - see this answer.
There are at least two situations where the solution's good advice doesn't hold, unfortunately.
Selective error handling
Get-Content -LiteralPath "nobox[]" gives an error message and exception type as if wildcards are involved:
Get-Content : An object at the specified path box[] does not exist, or has been filtered by the -Include or -Exclude parameter.
At line:1 char:1
+ Get-Content -Path "nobox[]"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (System.String[]:String[]) [Get-Content], Exception
+ FullyQualifiedErrorId : ItemNotFound,Microsoft.PowerShell.Commands.GetContentCommand
whereas without the brackets, we get:
Get-Content : Cannot find path 'nobox' because it does not exist.
At line:1 char:1
+ Get-Content -LiteralPath "nobox"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (nobox:String) [Get-Content], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Therefore, to silently deal with an optional file, something like:
try {
$lines = Get-Content -LiteralPath $path -ErrorAction Stop
}
catch [System.Management.Automation.ItemNotFoundException] {
$lines = #()
}
chokes on paths with brackets.
Creating a hard or symbolic link
A minor and a major caveat:
The Path parameter, the name of the new item, "works like the LiteralPath parameter of other cmdlets", says the documentation of New-Item clearly, and that seems true and makes sense. Though I wish we could clarify that by writing -LiteralPath.
The Value parameter, the target of the link (also known as Target secretly in v5 and openly later), does not accept wildcard characters according to the same documentation, but that's a lie. The command:
New-Item -ItemType "HardLink" -Path "whatever" -Target "*"
makes Powershell squeal "Cannot set the location because path '*' resolved to multiple containers.".
So you always need the escapes for the target. If you have a file named "f[]", then this will display an error:
New-Item -ItemType "HardLink" -Path "whatever" -Target "f[]"
and this will create a link:
New-Item -ItemType "HardLink" -Path "f[2]" -Target ([WildcardPattern]::Escape("f[]"))
Same for ItemType "SymbolicLink".

Folder name issue with Get-child Item Output [duplicate]

I modified PowerShell script from PowerShell - Batch change files encoding To UTF-8.
# Modified version of https://stackoverflow.com/q/18684793
[Threading.Thread]::CurrentThread.CurrentUICulture = 'en-US'
$Encoding = New-Object System.Text.UTF8Encoding($True) # If UTF8Encoding($False), It will be UTF-8 without BOM
$source = "C:\Users\AKULA\Desktop\SRC" # source directory
$destination = "C:\Users\AKULA\Desktop\DST" # destination directory
if (!(Test-Path $destination)) {
New-Item -Path $destination -ItemType Directory | Out-Null
}
# Delete all previously generated file
Get-ChildItem -Path $destination -Include * -File -Recurse | ForEach-Object {$_.Delete()}
# Recursively convert all files into UTF-8
foreach ($i in Get-ChildItem $source -Force -Recurse -Exclude "desktop.ini") {
if ($i.PSIsContainer) {
continue
}
$name = $i.Fullname.Replace($source, $destination)
$content = Get-Content $i.Fullname
if ($null -ne $content) {
[System.IO.File]::WriteAllLines($name, $content, $Encoding)
} else {
Write-Host "No content from: $i"
}
}
But after using it, I've found that PS cannot handle [ or ] well.
I made some test files that has diversity in name/content.
Get-Content : An object at the specified path C:\Users\AKULA\Desktop\SRC\FILENAME[[[[[[]]]]]]]].txt does not exist, or
has been filtered by the -Include or -Exclude parameter.
At C:\Users\AKULA\Desktop\Convert_to_UTF-8.ps1:24 char:16
+ $content = Get-Content $i.Fullname
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (System.String[]:String[]) [Get-Content], Exception
+ FullyQualifiedErrorId : ItemNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Since I cannot embed images in question, here is link of IMGUR album.
Full image list: https://imgur.com/a/aN1RG2L
These are what I've tested:
Test files have different names. Their name contains space, ',
[]. Also made up different language(Japanese, Korean).
These files have same content, encoded with UCS-2 BE BOM(UTF-16 BE) so
that I can check if it has re-encoded to UTF-8.
How can I make my script handle [ or ] in file name well?
tl;dr
Indeed, use of the -LiteralPath parameter is the best solution (in PowerShell (Core) v6+, you can shorten to -lp):
$content = Get-Content -LiteralPath $i.Fullname
-LiteralPath ensures that $i.Fullname is taken verbatim (literally); that is, [ and ] in the path are interpreted as themselves rather than having special meaning, as they would have as a -Path argument, due to being interpreted as a wildcard expression - note that -Path is positionally implied if you only pass a value (a string) as the first argument, as you did (Get-Content $i.FullName)
Note: This answer analogously applies to all cmdlets that have both -Path and -LiteralPath parameters, such as Set-Content, Out-File, and Set-Location.
As for what you tried:
$content = Get-Content $i.Fullname
is effectively the same as:
$content = Get-Content -Path $i.Fullname
That is, the (first) positional argument passed to Get-Content is implicitly bound to the
-Path parameter.
The -Path parameter accepts wildcard expressions to allow matching paths by patterns; in addition to support for * (any run of characters) and ? (exactly 1 character), [...] inside a wildcard pattern denotes a character set or range (e.g., [12] or [0-9]).
Therefore an actual path that contains [...], e.g., foo[10].txt, is not recognized as such, because the [10] is interpreted as a character set matching a single character that is either 1 or 0; that is foo[10].txt would match foo0.txt and foo1.txt, but not a file literally named foo[10].txt.
When (implicitly) using -Path, it is possible to escape [ and ] instances that should be interpreted verbatim, namely via the backtick (`), but note that this can get tricky to get right when quoting and/or variable references are involved.
If you know a path to be a literal path, it is best to form a habit of using -LiteralPath (which in PowerShell Core you can shorten to -lp).
However, if your path contains literal [ and ] and you also need wildcard matching, you must use `-escaping - see this answer.
There are at least two situations where the solution's good advice doesn't hold, unfortunately.
Selective error handling
Get-Content -LiteralPath "nobox[]" gives an error message and exception type as if wildcards are involved:
Get-Content : An object at the specified path box[] does not exist, or has been filtered by the -Include or -Exclude parameter.
At line:1 char:1
+ Get-Content -Path "nobox[]"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (System.String[]:String[]) [Get-Content], Exception
+ FullyQualifiedErrorId : ItemNotFound,Microsoft.PowerShell.Commands.GetContentCommand
whereas without the brackets, we get:
Get-Content : Cannot find path 'nobox' because it does not exist.
At line:1 char:1
+ Get-Content -LiteralPath "nobox"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (nobox:String) [Get-Content], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Therefore, to silently deal with an optional file, something like:
try {
$lines = Get-Content -LiteralPath $path -ErrorAction Stop
}
catch [System.Management.Automation.ItemNotFoundException] {
$lines = #()
}
chokes on paths with brackets.
Creating a hard or symbolic link
A minor and a major caveat:
The Path parameter, the name of the new item, "works like the LiteralPath parameter of other cmdlets", says the documentation of New-Item clearly, and that seems true and makes sense. Though I wish we could clarify that by writing -LiteralPath.
The Value parameter, the target of the link (also known as Target secretly in v5 and openly later), does not accept wildcard characters according to the same documentation, but that's a lie. The command:
New-Item -ItemType "HardLink" -Path "whatever" -Target "*"
makes Powershell squeal "Cannot set the location because path '*' resolved to multiple containers.".
So you always need the escapes for the target. If you have a file named "f[]", then this will display an error:
New-Item -ItemType "HardLink" -Path "whatever" -Target "f[]"
and this will create a link:
New-Item -ItemType "HardLink" -Path "f[2]" -Target ([WildcardPattern]::Escape("f[]"))
Same for ItemType "SymbolicLink".

Trying to use built-in PS1 methods Get-Date, Convert-Path, rename-item getting errors

I am new to PowerShell and trying to write my first script to copy files from one directory to another and then rename the files in the $DESTINATION directory once they are copied over there.
I am executing the script below from C:\Scripts directory on my machine.
The first foreach loop works great and my files land in the $DESTINATION.
But when I to through the 2nd loop I get the errors below
Any help/direction would be appreciated. Thanks.
Here is my code:
# Define variables.
$Source = "C:\ETLFILES\WinSCP\FilesETL\*.*"
$Destination = "C:\ETLFILES\WinSCP\SFE_Archive\"
$DestinationFiles = "C:\ETLFILES\WinSCP\SFE_Archive\*.*"
# Create the $sourceFileList variable to loop through
$sourceFileList = Get-ChildItem -path $Source
# Loop through the $soureFileList and copy the items to the $Destination.
foreach ($item in $sourceFileList) {
Copy-Item -Path $Source -Destination $Destination
}
# Create the $destinationFileList variable to loop through
$destinationFileList = Get-ChildItem -path $DestinationFiles
# Loop through the $destinationFileList and rename the files with appended DateTime stamp.
foreach ($itemDest in $destinationFileList) {
$Date = (Get-Date).ToString("yyyyMMdd_HHmmss")
$newFileName = $Date + "_" + $itemDest
Rename-Item $itemDest $newFileName
}
Here are the errors I'm getting and I think that I need to change the -path as it is looking to where the script is executing from and not looking at the $DestinationFiles directory like it looked to the $Source above:
PS C:\Scripts> C:\Scripts\ArchiveSFE_files.ps1
Rename-Item : Cannot rename the specified target, because it represents a path or device name.
At C:\Scripts\ArchiveSFE_files.ps1:21 char:5
+ Rename-Item $itemDest $newFileName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Rename-Item], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.RenameItemCommand
Rename-Item : Cannot rename the specified target, because it represents a path or device name.
At C:\Scripts\ArchiveSFE_files.ps1:21 char:5
+ Rename-Item $itemDest $newFileName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Rename-Item], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.RenameItemCommand
Rename-Item : Cannot rename the specified target, because it represents a path or device name.
At C:\Scripts\ArchiveSFE_files.ps1:21 char:5
+ Rename-Item $itemDest $newFileName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Rename-Item], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.RenameItemCommand
Rename-Item : Cannot rename the specified target, because it represents a path or device name.
At C:\Scripts\ArchiveSFE_files.ps1:21 char:5
+ Rename-Item $itemDest $newFileName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Rename-Item], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.RenameItemCommand
PS C:\Scripts>
PowerShell can be a bit tricky if you're used to text-based languages and tools, because everything in PowerShell is a .NET object.
This line:
$newFileName = Get-Date + "_" + $item # .ToString("yyyyMMdd-HHmmss") + "_" + $item
will most certainly result in an error. Get-Date returns a [datetime] object.
Although [datetime] does support the + operator, it expects an argument of the type [timespan] - and the string "_" can't be converted to a timespan.
What you want is a string representing the current date. Either use the ToString() statement that you've commented out, or use the -Format parameter to produce a formatted string instead of a [datetime] object:
$newFileName = (Get-Date).ToString("yyyyMMdd-HHmmss") + "_" + $item
or
$newFileName = (Get-Date -Format "yyyyMMdd-HHmmss") + "_" + $item
I'm not a betting man, but I'll bet the error you're having is this one.
Get-Date : Cannot bind parameter 'Date'. Cannot convert value "+" to type "System.DateTime".
Error: "String was not recognized as a valid DateTime."
This is because the code you've got here is trying to add a character to a PowerShell DateTime object, and that simply does not fly. You can pick a single property though and concatenate characters onto that, however.
But, I noticed that you commented out some lines in your code, and am guessing that you'd like to get the date in this format: "yyyyMMdd-HHmmss".
If that's so, this little snippet will do that for you:
# Define logic to rename and copy .
foreach ($item in $sourceFileList) {
$newFileName = $(Get-Date -UFormat %Y%m%d-%H%m%S) + "_" + $item
"the item $($item.BaseName) would become $newFileName"
}
This will output to the screen the new name for the file, like this:
the item q1 would become 20160921-110907_q1.png
the item q2 would become 20160921-110907_q2.png
the item q3 would become 20160921-110907_q3.png
You can comment out that line once you're happy with the new name. And the best part? Just drop this into your code inplace of your current $newFileName line and it will work with your previous code.
If I'm wrong, let me know the error you're getting and I'll help you get this sorted.
Looks like you are trying to write a file with : in the time part of the Get-Date when you try to rename to $newFileName. This will kick an error out.
$Date = Get-Date.ToString("yyyyMMdd_HHmmss")
$newFileName = $Date + "_" + $item
Include the above and that should prevent that problem.
thanks for everyone's help and input. I really appreciate it. I was finally able to resolve my problem whereby in the variable assigned in the foreach loop the FULL PATH was being defined that is what was generating the errors above. Once I used the .NAME property the issue was resolved. Thanks again to everyone.

Path variable does not seem to be consistent in a single Powershell script

I'm running what I think is a relatively simple script:
$txtPath = "c:\users\xxxxxx\desktop\cgc\tx\"
$srcfiles = Get-ChildItem $txtPath -filter "*.txt*"
ForEach($txtfile in $srcfiles) {
Write-Host $txtfile
Get-Content $txtfile
}
and I get the following output:
Automatic_Post-Call_Survey_-_BC,_CC.txt
Get-Content : Cannot find path 'C:\users\x46332\desktop\cgc\Automatic_Post-Call_Survey_-_BC,_CC.txt' because it does no
t exist.
At C:\users\x46332\desktop\cgc\testcount2.ps1:34 char:13
+ Get-Content <<<< $txtfile
+ CategoryInfo : ObjectNotFound: (C:\users\x46332...ey_-_BC,_CC.txt:String) [Get-Content], ItemNotFoundEx
ception
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
This is the output from Write-Host $txtfile followed immediately by Get-Content $txtfile and get-content seems to be the heart of my issue.
When I comment out the Get-Content line, the script generates a list of the filenames to the console. This suggests to me that the $txtPath is properly defined. However, I add Get-Content for the SAME file/same variable and for some reason, the \tx portion of the path disappears from the search string. My filename prints, but then Get-Content can't find the path for the filename is just printed.
I suspect that the "directory doesn't exist" error isn't really that the directory doesn't exist. So what should I be looking at? There's not a lot of space in my code for an error to hide, but I can't find it...thoughts?
Get-Content needs the full path e.g.:
Get-Content $txtFile.FullName
When you specify Get-Content $txtFile, PowerShell attempts to coerce the argument $txtFile to the required argument Path and to do so, it coerces the FileInfo object to a string. This process yields just the name of the file.
Another way to do this is:
$txtFile | Get-Content