I'd like to batch rename files in a folder, prefixing the each filename line counts and "_" into the new names. example
a.txt
b.txt
c.txt
d.txt
to
1000_a.txt
32_b.txt
199_c.txt
8_d.txt
Bonus: pad the line counts with leading zeroes to
1000_a.txt
0032_b.txt
0199_c.txt
0008_d.txt
what i tried: from command that add suffix
Dir | Rename-Item -NewName { $_.basename "_" (Get-Content $_).length "_" $_.extension}
to
Dir | Rename-Item -NewName { (Get-Content $_).length "_" $_.basename $_.extension}
but it give error
Rename-Item : The input to the script block for parameter 'NewName' failed. Cannot convert value "a" to type "System.Int32". Error: "Input string was not in a correct format."
Thanks
CodePudding user response:
What you are doing is almost fine, the error comes from trying to concatenate an int with a string, PowerShell attempts type conversion of all elements to the type of the leftmost object in the operation:
The operation that PowerShell performs is determined by the Microsoft .NET type of the leftmost object in the operation. PowerShell tries to convert all the objects in the operation to the .NET type of the first object. If it succeeds in converting the objects, it performs the operation appropriate to the .NET type of the first object. If it fails to convert any of the objects, the operation fails.
Since the leftmost object in this case (the .Length property) is of the type int PowerShell attempts to convert the rest to int and it fails, for example:
PS /> 1 'A'
InvalidArgument: Cannot convert value "A" to type "System.Int32". Error: "Input string was not in a correct format."
This would be easily fixed by type casting the returned value to sring:
-NewName { [string](Get-Content $_).Length "_" $_.BaseName $_.Extension }
Or for example using string formatting or the -f format operator:
-NewName { '{0}_{1}{2}' -f (Get-Content $_).Length, $_.BaseName, $_.Extension }
As for "the bonus", with string formatting see How do I control the number of integral digits?
In this case you can use {0:0000}, as an example:
(0..1000).ForEach({'{0:0000}' -f $_})
On the other hand, if you have many lengthy files, [System.IO.File]::ReadAllLines(...) is likely to be faster than Get-Content:
-NewName { '{0:0000}_{1}' -f [System.IO.File]::ReadAllLines($_).Length, $_.Name }
# OR
$io = [System.IO.File]
-NewName { '{0:0000}_{1}' -f $io::ReadAllLines($_).Length, $_.Name }
