1

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

Ulala Lalalu
  • 13
  • 1
  • 2

1 Answers1

1

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 }
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • 1
    Very well-written and detailed explanation, i learned a lot. The command i use for the internal becomes `Dir | Rename-Item -NewName { '{0:00000}_{1}' -f $io::ReadAllLines("C:\test\$_").Length, $_.Name } ` since ReadAllLines($_) default to %HOMEPATH%. – Ulala Lalalu Feb 07 '22 at 06:13