I am renaming the files of a directory. The way to rename them is to add a letter to the beginning of the file name, depending on the option a user chooses ("F" or "O") A possible solution to this exercise is the following:
$Path="C:\app\SandBox\"
Get-ChildItem -Path $Path -Filter *.xlsx | ForEach-Object {
$opt = Read-Host "Do you want modify (F) this file or not (O)?"
$name=$_.name
$PathFinal=$Path $name
if ($opt -eq "F") {
$newName="F" $name
Rename-Item -NewName $newName -Path $PathFinal
}
if ($opt -eq "O") {
$newName="F" $name
Rename-Item -NewName $newName -Path $PathFinal
}
}
The loop iterates as many times as there are files in the directory. However, when I try to change the code as follows:
$Path="C:\app\SandBox\"
Get-ChildItem -Path $Path -Filter *.xlsx | ForEach-Object {
$name=$_.name
$opt = Read-Host "$name - Do you want modify (F) this file or not (O)?"
if ($opt -eq "O") {
$name=$_.Name
Rename-Item -NewName "O$name" -Path $Path$name
}
if ($opt -eq "F") {
$name=$_.Name
Rename-Item -NewName "F$name" -Path $Path$name
}
}
It turns out that, in some cases, the loop iterates one more time! If I have two files in the folder, sometimes the loop oterates three. Which may be due?
It should iterate twice, but iterate three. I can't think of what it could be due to, since when channeling it should pass only two files.
CodePudding user response:
It should iterate twice, but iterate three. I can't think of what it could be due to, since when channeling it should pass only two files.
Get-ChildItem
works against the file system by asking for the first file matching a given filter, and then it continues asking the OS "what's the next matching file name after fileX
", until the OS says "no more files".
In this case, A.xlsx
is renamed to FA.xlsx
on the first iteration, and the OS is thus able to answer the question after the next iteration: "what's the next matching file name after B.xlsx
" with "Next file is FA.xlsx
".
To enumerate only the files already in the directory when you start the script, place the call to Get-ChildItem
in a subexpression or nested pipeline:
$(Get-ChildItem -Path $Path -Filter *.xlsx) | ForEach-Object { ... }
This will force PowerShell to wait for Get-ChildItem
to finish executing before sending the first output item to ForEach-Object
- and since Get-ChildItem
is already "done" by the time the renaming starts, you don't risk seeing the same file multiple times.