Powershell WMI Query failing when executed from Task Scheduler - powershell

i have a strange problem...
i have the following code, which takes the output from Sysinternals Disk Usage tool (link below)
Disk Usage - Sys Internals
so first i get the physical drives into array $Disks, then i enumerate these through the foreach and mess about with them.
my problem lies in this line $Dir = du.exe -q -v $d.DeviceID
$PC = get-content env:COMPUTERNAME
$Disk = gwmi win32_logicaldisk -filter "drivetype=3"
foreach ($d in $Disk)
{
$Dir = du.exe -q -v $d.DeviceID
$Dir[8..($Dir.length-8)] | foreach {
$Size = $_.substring(0,10).replace(",","")/1024
$Path = $_.substring(10)
}
}
$d.DeviceID should be the drive letter (i.e. C:)
then i populate $Dir with the output from DU.exe, but $d.DeviceID is not acting how it is supposed to, running this from a task has this following result (added a line that says $d.DeviceID, to show the output):
B:
Cannot index into a null array.
At C:\DU.ps1:25 char:6
+ $Dir[ <<<< 8..($Dir.length-8)] | foreach {
+ CategoryInfo : InvalidOperation: (System.Object[]:Object[]) [],
RuntimeException
+ FullyQualifiedErrorId : NullArray
C:
Cannot index into a null array.
At C:\DU.ps1:25 char:6
+ $Dir[ <<<< 8..($Dir.length-8)] | foreach {
+ CategoryInfo : InvalidOperation: (System.Object[]:Object[]) [],
RuntimeException
+ FullyQualifiedErrorId : NullArray
D:
Cannot index into a null array.
At C:\DU.ps1:25 char:6
+ $Dir[ <<<< 8..($Dir.length-8)] | foreach {
+ CategoryInfo : InvalidOperation: (System.Object[]:Object[]) [],
RuntimeException
+ FullyQualifiedErrorId : NullArray
running it from the ISE or just from the Shell has no issues, running it on other servers from all methods works.
i do believe the population of the $Dir vairable is the problem, as the du.exe has trouble with the $d.DeviceID
i dont understand why it is just this server/task sheduler that has the issue. i have tried the following:
redefined the array element to $i = $d.deviceID to fix it down - nothing
exported the job from other server (both DCs) an imported - nothing
restarted the winmgmt service - nothing
i think its a permissions issue, but im running this on an AD as THE Dom Admin with top privilages.
please can you guys help on this one, really am stuck...
cheers
Lee

Yet another update based on comment below:
Try:
$cmd = "du.exe `-q `-v $($d.DeviceID)"
$dir = Invoke-Expression $cmd
Updating as per the comment below.
Take this example. This can get the size of every folder and display size and full path to the folder.
Function Get-FolderSize {
Param ($folderPath)
$colItems = (Get-ChildItem $folderPath -recurse | Measure-Object -property length -sum)
return $colItems.sum/1MB
}
$folders = Get-ChildItem -Recurse C:\Scripts
$folders | % {
if ($_.PSIsContainer) {
$size = Get-FolderSize $_.FullName
Write-Host $size
Write-Host $_.FullName
}
}
You can use WMI to get the drive letter and pass it to the script. For example:
$disks = gwmi win32_logicaldisk -filter "drivetype=3"
$disks | % {
$items = Get-ChildItem -Recurse $_.DeviceID -Force
$items | % {
if ($_.PSIsContainer) {
$size = Get-FolderSize $_.FullName
Write-Host $size
Write-Host $_.FullName
}
}
}
So, with this, you dont need DU.exe. You can run this as a script.
--------------OLD ANSWER _-------------------
First thing I would suspect is the path to DU.exe. What is the working directory set on the scheduled task? Is it the place where DU.exe is available?
BTW, what is the goal of this script? Are you just looking at the disk drive size? What are you capturing into $path? I did not have the patience to test your code. But, I feel that this can be easily achieved using just WMI and no other external tools.

Related

How do I write a script to reboot a list of servers, but will EXCLUDE servers I don't want rebooted?

Being new to PowerShell I've been following some of the guidance in these posts to write a script for what's mentioned in the subject.
Here's the script:
Get-Content -Path C:\temp\Domain.txt | Restart-Computer -force | Where-Object { $._Name -notmatch "^(SERVER01)"}
Here's the error:
Restart-Computer : Cannot validate argument on parameter 'ComputerName'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At line:1 char:40
+ ... et-Content -Path C:\temp\Domain.txt | Restart-Computer -force | Where ...
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:PSObject) [Restart-Computer], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.RestartComputerCommand
For reference, the DOMAIN.txt has a list of servers that will periodically change so I want to skip certain servers should they end up on the list.
The error simply means you have an empty line on your text file, you can filter the lines using .Where(..) method and exclude empty or white space lines with the help of [string]::IsNullOrWhiteSpace(..) method. I have changed -notmach for the containment operator -notin.
Note, -notin looks for an exact match within the collection $hostsToExclude.
$hostToExclude = 'server1', 'server2'
(Get-Content -Path C:\temp\Domain.txt).Where({
-not [string]::IsNullOrWhiteSpace($_) -and $_ -notin $hostToExclude
}) | Restart-Computer -Force
It's worth noting that, on your snippet, you're restarting the hosts before filtering the collection. Get-Content should be followed by Where-Object.

Looping Through Files to Run PowerShell Script

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 }

Feeding Get-ChildItem path info from an array - Illegal characters

I need to iterate over an array of folder names, passing them into GCI so I can run operations on their contents. No matter what I do, it keeps giving me this error:
gci : Illegal characters in path.
At C:\Scripts\Arvest_submission_monitoring.ps1:86 char:5
+ gci -path "D:\subftp\$_" -recurse {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (D:\SUBFTP\folder1:String [Get-ChildItem], ArgumentException
+ FullyQualifiedErrorId :
DirArgumentError,Microsoft.PowerShell.Commands.GetChildItemCommand
I've created an array:
$folders = "folder1","folder2","folder3"
And then iterate over it:
$folders | % {
gci -Path "D:\subftp\$_" -Recurse {
#do stuff here
}
}
I've tried many various options such as a regex replace to remove any possible illegal characters (although a $folder.Count shows me there are no invisible characters), I've tried turning $folders into a C# ArrayList but it's still a no go. I've even played around with the path variable itself concatenating it into a single string before use.
The weird thing is if I try and execute it from the command line it works fine. What gives? I'm running v5.
It is caused by the curly brace right at the end of your command gci -Path "D:\subftp\$_" -Recurse {.
Probably you wanted to do this:
$folders | ForEach-Object {
Get-ChildItem -Path "D:\subftp\$_" -Recurse | ForEach-Object {
#do stuff here
}
}

Search for a string in a number of files, and then rename the files with that string

I'm trying to create a PowerShell script that will search through a series of .txt files (.hl7 files to be exact, but they are just txt files) and search within those files to see if they contain a four digit number. If the file does contain that four digit number, it should then rename the file with the string added to the front of the original file name. So test.hl7 should become 8000_test.hl7 if that file includes those 4 digits within it.
After a day of ferocious googling and digging through this website, this is the best I could muster:
$AccountIDs = ("8155", "8156", "8428")
$Path = "C:\Users\ThatsMe\Downloads\messages"
$Files = (Get-ChildItem $Path -Filter "*.hl7")
for ($i = 0; $i -le $Files.Length; $i++) {
if (Get-Content $Files[$i].FullName | Select-String -Pattern $AccountIDs[$i]) {
Rename-Item $Files[$i].FullName -NewName (($AccountIDs[$i]) + "_" + $Files[$i].Name)
}
}
I am getting some interesting results. I currently have four test files in that messages folder, test, test2, test3, and skibbidybop. The very first one, test gets correctly changed to 8156_test. However, the other files aren't touched. Now, when I change the filename of test to ttest, the script completely skips over that file and then renames test2 and test3 to 8156_test2 (which is incorrect) and 8428_test3 respectively. skibbidybop is never touched.
And, of course, the error message from PowerShell:
Select-String : Cannot bind argument to parameter 'Pattern' because it is null.
At line:6 char:61
+ if (Get-Content $Files[$i].FullName | Select-String -Pattern <<<< $AccountIDs[$i]) {
+ CategoryInfo : InvalidData: (:) [Select-String], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SelectStringCommand
Get-Content : Cannot bind argument to parameter 'Path' because it is null.
At line:6 char:16
+ if (Get-Content <<<< $Files[$i].FullName | Select-String -Pattern $AccountIDs[$i]) {
+ CategoryInfo : InvalidData: (:) [Get-Content], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.GetContentCommand
Updated Code
$Path = "C:\Users\ThatsMe\Downloads\messages"
$pattern = '\b(8155|8156|8428)\b'
Get-ChildItem $Path -Filter '*.hl7' |
Select-String -Pattern $pattern |
Group-Object Path |
ForEach-Object {
$id = $_.Group.Matches[0].Groups[0].Value
$filename = $_.Group.Filename | Select-Object -First 1
Rename-Item -Path $_.Name -NewName "${id}_${filename}" -WhatIf
}
This is the error that I receive now:
C:\> C:\Users\ThatsMe\Downloads\messages\changename.ps1
Cannot index into a null array.
At C:\Users\ThatsMe\Downloads\messages\changename.ps1:8 char:38
+ $id = $_.Group.Matches[ <<<< 0].Groups[0].Value
+ CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
What if: Performing operation "Rename File" on Target "Item:
C:\Users\ThatsMe\Downloads\messages\test.hl7 Destination:
C:\Users\ThatsMe\Downloads\messages\_".
Cannot index into a null array.
At C:\Users\ThatsMe\Downloads\messages\changename.ps1:8 char:38
+ $id = $_.Group.Matches[ <<<< 0].Groups[0].Value
+ CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
What if: Performing operation "Rename File" on Target "Item:
C:\Users\ThatsMe\Downloads\messages\test3.hl7 Destination:
C:\Users\ThatsMe\Downloads\messages\_".
The errors you get are caused by two mistakes, one of them a classic off-by-one error. PowerShell arrays are zero-based, meaning that the last index of the array is one less than the number of its elements:
[ 'a', 'b', 'c' ] → count == 3
0 1 2 → last index == 2 == 3-1
Thus your for loop may run while $i is less than $Files.Length (-lt), not less or equal (-le):
for ($i = 0; $i -lt $Files.Length; $i++) {
Also, you cannot use the same index variable for two different arrays ($Files and $AccountIDs) unless you made sure both arrays have the same length or at least that the second array ($AccountIDs) has more elements than the one used to determine the maximum index ($Files). If $AccountIDs has less elements than $Files your code will eventually attempt to access an index beyond the upper boundary of $AccountIDs. Besides, you probably want to check each file for all of the numbers from $AccountIDs anyway. Doing that requires a nested loop with a second index variable.
With that said, you're making this more complicated than it needs to be. You can simply put your IDs in a single regular expression and pipe the list of files into Select-String to check them against that regular expression:
$pattern = '\b(8155|8156|8428)\b'
Get-ChildItem $Path -Filter '*.hl7' |
Select-String -Pattern $pattern |
Group-Object Path |
ForEach-Object {
$id = $_.Group.Matches[0].Groups[0].Value
$filename = $_.Group.Filename | Select-Object -First 1
Rename-Item -Path $_.Name -NewName "${id}_${filename}" -WhatIf
}
The regular expression \b(8155|8156|8428)\b matches any of the given numbers. The \b restrict the match to word boundaries to avoid matching numbers like 81552 or 842893 as well.
The Group-Object statement ensures the uniqueness of the renamed files (so that you don't attempt to rename a file more than once if more than one match is found in it).
.Matches[0].Groups[0].Value extracts the value of the first capturing group of the first match for each file.
The Select-Object -First 1 ensures that even if multiple matches were found in a file you have just one string with the filename, not an array of them.
Remove the -WhatIf switch once you verified that the rename operation would work correctly and re-run the whole statement to actually rename the files.
Edit: For PowerShell v2 you need to adjust the group handling a little bit, because that version doesn't support member enumeration.
Get-ChildItem $Path -Filter '*.hl7' |
Select-String -Pattern $pattern |
Group-Object Path |
ForEach-Object {
$id = $_.Group | Select-Object -Expand Matches -First 1 |
ForEach-Object { $_.Groups[0].Value }
$filename = $_.Group | Select-Object -Expand Filename -First 1
Rename-Item -Path $_.Name -NewName "${id}_${filename}" -WhatIf
}

Rename a list of files

I need to rename all files below pwd named all_v4_0.csv to all_v4_1.csv.
So far, I have worked my way to this piece of PowerShell:
$oldfiles = dir -recurse | ?{$_.Name -eq "all_v4_0.csv"}
foreach ($o in $oldfiles) {
$o.CopyTo Join-Path $o.Directory.ToString() "all_v4_1.csv"
}
But the foreach loop fails with the message that
At line:2 char:15
+ $o.CopyTo Join-Path $o.Directory.ToString() "all_v4_1.csv"
+ ~~~~~~~~~
Unexpected token 'Join-Path' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
What am I doing wrong here?
Update, 20150604
As commented below by Manuel Batsching, the original version can be fixed by adding two layers of parentheses: one to indicate function argument, and one to force evaluation order:
$oldfiles = dir -recurse | ?{$_.Name -eq "all_v4_0.csv"}
foreach ($o in $oldfiles) {
$o.CopyTo((Join-Path $o.Directory.ToString() "all_v4_1.csv"))
}
For the problem at hand, one of the solutions with .FullName.Replace would probably be easier.
PSH's parser is not reading the Join-Path and its arguments as an expression to evaluate and pass result to outer expression. So parentheses to force evaluation order.
But additionally CopyTo is a .NET member, rather than a PSH cmdlet, so it needs to have parentheses around its argument (and no space).
Thus:
$o.CopyTo((Join-Path $o.Directory.ToString() "all_v4_1.csv"))
(Possibly using PSH's Copy-Item cmdlet would be a cleaner option.)
Set the target name separately before doing the copy. The below should work:
$oldfiles = dir -recurse | ?{$_.Name -eq "all_v4_0.csv"}
foreach ($o in $oldfiles) {
$newName = $o.FullName.replace("all_v4_0.csv","all_v4_1.csv")
Copy-Item $o.FullName $newName
}
If you want to keep things simple you can also use string concatenation to create the target path.
Get-ChildItem -Recurse -Include 'all_v4_0.csv' |
ForEach { $_.MoveTo($_.Directory.FullName + '\all_v4_1.csv') }
Simply replace the substring in the new name of the files:
Get-ChildItem -Recurse -Include 'all_v4_0.csv' |
Rename-Item -NewName { $_.Name.Replace('4_0', '4_1') }