Renaming files with unnecessary zeros in Powershell - powershell

I've been looking into batch renaming files with Powershell and I've made some good progress. To put it simply I'm looking to remove all extra zeros from the beginning of my files. So far I have a folder of images named as such:
0001_random_name.jpg
0002_random_name.jpg
0003_random_name.jpg
All the way up to 900~. I created a Powershell script that takes the first four characters and adds the .jpg extension back. Here is that script:
Get-ChildItem 'G:\InvaluableNumbered' | rename-item -newname { $_.name.substring(0,4) + ".jpg" }
This renames the files to
0001.jpg
0002.jpg
0003.jpg
For this project I need to name them
1.jpg
2.jpg
3.jpg
All the way up to 968.jpg. Is there any way I can use the script that I wrote and then have another command that removes all 0s up to where it hits the first number greater than zero?
Thank you for your time.

You can use TrimStart string method to achieve desired result:
$_.name.substring(0,4).TrimStart('0')
Other possibility could be cast value to [int] and then back to [string]:
[string][int]$_.name.substring(0,4)

Related

Rename files in a folder using powershell, keeping the start and end of string in original filename

Currently trying to create a script that renames specific files within a chosen folder so that the resulting renamed files look like the following:
Original Filename: 45.09 - WrapperA12_rev1.DXF
Resultant Filename: 45.09_1.DXF
So the rev number is included as a suffix to the base filename, the extension is kept and the first 5 characters of the filename is kept (including the ".").
I can get fairly close by removing the hyphens, spaces and letters from the original filename using the -replace argument, but the resultant filename using the example above would be "45.0912_1", where the file extension is ".0912_1". This makes sense, but any attempt I've made to append the file extension (".DXF") to the filename hasn't worked.
$listdxf=gci -path $pathfolder -Filter *.DXF | Select-Object
$prenameDXF=$listdxf|rename-item -WhatIf -newname {$_.name -replace('[a-z]') -replace('-') -
replace('\s','')}
$prenameDXF
Any feedback on how I would go about doing this would be greatly appreciated.
For further clarification; the original filenames will always have the 4 numbers and the dot at the start of the filename - these need to be kept for the output name, the only other number I want is the number at the end of the filename that will always refer to the revision number, however this number may be variable (i.e; it could be 0 or 0.1,1,1.1 etc.). The Rev number will ALWAYS follow the underscore in the original filename. All other numbers and letters etc. in the original filename need to be removed. I'm assuming the solution might include assigning a variable to just return the first 4 numbers (i.e; XX.XX) as a substring maybe, while assigning a variable to the last few characters that follow the "_". Then maybe combine the two and add the ".DXF" file extension.
LATEST UPDATE: Following the responses here, I've been able to get the functionality nearly exactly where I need it to be.
I've been using the regex provided below, and with some slight changes adapted it to allow for some other things (to allow for spaces after "rev" and to allow for the rev number to be separated by a dot if present, i.e; rev1.1 etc.), but currently struggling to find a way of simply returning "0" if no "rev" is present in the file name. For example, if a filename is as follows: 31.90 - SADDLE SHIM.DXF - I wish for the rename regex script to return 31.90_0. The expression I'm currently using is as follows: '(\d{2}\.\d{2}).*?rev(\s?\d+\.\d+|\s?\d+).*(?=\.DXF)', '$1_$2'
I have tried putting a pipeline (if) after the capture block following the "rev" and then putting (0) in a new capture block, but that's not working. Any feedback on this would be greatly appreciated. Thanks again for the replies.
It looks like this regex could do the trick to rename your files with your desired format: (?<=\.\d+)\s.+(?=_rev)|rev.
Get-ChildItem -Filter *-*_rev*.dxf |
Rename-Item -NewName { $_.Name -replace '(?<=\.\d+)\s.+(?=_rev)|rev' }
However the above assumes all files will start with some digits followed by a dot followed by more digits and may or may not be 5 digits including dots. It also assumes there will be a white space after the remaining digits. It also assumes the files will end with rev followed by more digits after it's dxf extension.
This regex could work too (?<=^[\d.]{5})\s.+(?=_rev)|rev, however this one assumes only will capture the first 5 digits including one or more dots.
Per your update, you could try using switch with the -regex option. $Matches will contain the matches and you can reference the match groups by using the group number as the key (e.g. $Matches[1]). You may also reference as a property (e.g., $Matches.1)
Get-ChildItem c:\temp\powershell\testrename -File |
Rename-Item -NewName {
switch -Regex ($_.Name) {
'(\d{2}\.\d{2}).*?rev(\s?\d+\.\d+|\s?\d+).*(?=\.DXF)' {
"$($Matches.1)_$($Matches.2).DXF"
break
}
'(\d{2}\.\d{2}).*(?=\.DXF)' {
"$($Matches.1)_0.DXF"
break
}
default {
$_
}
}
} -WhatIf
Remove -WhatIf once done testing to perform rename action

Bulk Move Sequential Numbers (Suffix) - Images, JPEG, RAW etc

I need to move a sequential number or sometimes a random ID with letters.
As an example:
Australia_Brisbane_NP_©_Hello_World_1163
Australia_Brisbane_NP_©_Hello_World_1164
Australia_Brisbane_NP_©_Hello_World_1165
Australia_Brisbane_NP_©_Hello_World_AHDFGF
Australia_Brisbane_NP_©_Hello_World_GHRADQ
Australia_Brisbane_NP_©_Hello_World_QGASFS
What I need to do is have ©_Hello_World at the end and move the ID behind the ©, Example below:
Australia_Brisbane_NP_1165_©_Hello_World
Australia_Brisbane_NP_AHDFGF_©_Hello_World
The ideal script would allow me to specify between 1-15 characters at the end of the word without effecting the extension and move the 1-15 characters behind _©.
I have tried searching for a lot of different scripts however either they do not work or they are too complicated for me to adapt them to what is required.
I am unable to use any external software and as such I have to stick to PowerShell.
The basic "change filename" script is:
Get filenames Get-ChildItem -LiteralPath 'C:\Path\To\Files'
Pipe the results into Rename-Item
Use the property -NewName with a scriptblock {}
In the scriptblock, code to calculate the new name from the old name
Extract the filename out from the path and the extension
Change it
Put the path and the extension back on
I have tried searching for a lot of different scripts however either they do not work or they are too complicated for me to adapt them to what is required.
Text processing is all about details, details make code more complicated, and small details can invalidate whole approaches.
It's not at all clear to my why you say:
The ideal script would allow me to specify between 1-15 characters at the end of the word without effecting the extension and move the 1-15 characters behind _©.
Why would you benefit from specifying the character count, instead of having the ideal script move "all of them"?
This script should do it:
$count = Read-Host -Prompt 'How many characters to move?'
Get-ChildItem -LiteralPath 'C:\Path\To\Files' |
Rename-Item -NewName {
$newName = $_.BaseName -replace "(©_Hello_World)_(.{$($count)})", '$2_$1'
"$($_.DirectoryName)/$($newName + $_.Extension)"
}
Nb. it will move the count you asked for, even if there are more characters there.
If you don't need to specify the count, and just take all to the extension, then replace (.{$($count)}) with (.*), and remove the Read-Host line.

Need to batch convert a large quantity of text files from ANSI to Unicode

I have a lot of ANSI text files that vary in size (from a few KB up to 1GB+) that I need to convert to Unicode.
At the moment, this has been done by loading the files into Notepad and then doing "Save As..." and selecting Unicode as the Encoding. Obviously this is very time consuming!
I'm looking for a way to convert all the files in one hit (in Windows). The files are in a directory structure so it would need to be able to traverse the full folder structure and convert all the files within it.
I've tried a few options but so far nothing has really ticked all the boxes:
ansi2unicode command line utility. This has been the closest to what I'm after as it processes files recursively in a folder structure...but it keeps crashing whilst running before it's finished converting.
CpConverter GUI utility. Works OK to a point but struggles with multiple files in a folder structure - only seems to be able to handle files in one folder
There's a DOS command that works OK on smaller files but doesn't seem to be able to cope with large files.
Tried GnuWin sed utility but it crashes every time I try and install it
So I'm still looking! If anyone has any recommendations I'd be really grateful
Thanks...
OK, so in case anyone else is interested, I found a way to do this using PowerShell:
Get-ChildItem "c:\some path\" -Filter *.csv -recurse |
Foreach-Object {
Write-Host (Get-Date).ToString() $_.FullName
Get-Content $_.FullName | Set-Content -Encoding unicode ($_.FullName + '_unicode.csv')
}
This recurses through the entire folder structure and converts all CSV files to Unicode; the converted files are written to the same locations as the originals but with "unicode" appended to the filename. You can change the value of the -Encoding parameter if you want to convert to something different (e.g. utf-8).
It also outputs a list of all the files converted along with a timestamp against each

Powershell : Quickly count containers

I think we all know the PsIsContainer method to check if the current file is a folder or not. But in my project I need a way to quickly know the number of folders in a folder. All I need is to quickly get their number. I want to write in a .txt lines which would look like C:\folder;12. It would mean in the folder, with the -recurse argument, there would be 12 folders.
To explain why, I need to save the progress of my work when i cut off the program which is used to analyse some folders. When a folder's analysed, the result is written in a second .txt. For example, if a folder is called C:\folder\folder1, folder will be analysed and then folder1 will be too. Which makes folder appear 2 times in the file because the full name always is written. What i want to do is to count the number of lines where C:\folder is written. If it equals the number next it's path in the first .txt, it means the file already has been analysed and the function doesnt need to do it again.
Does someone have a solution ? Or maybe an another idea to save the progress ? Cause i really have the feeling this is taking too long to do this.
Thank you for your help.
Another approach, which i find much faster is using cmd built-in 'dir' command
of course this is in case you don't need the subfolders(which you can then run the function in a foreach loop, or change the function if this is the case)
Function Get-FolderCount($path)
{
$Dir = cmd /c dir $path /a:d
Return ($Dir[-1] -csplit 'Dir' -replace '\s')[0]
}
I use this as well for measuring folder size with /s switch and take the total size which is much faster then powershell, also much faster then run it on interactive shell...

Rename Files with Index(Excel)

Anyone have any ideas on how to rename files by finding an association with an index file?
I have a file/folder structure like the following:
Folder name = "Doe, John EO11-123"
Several files under this folder
The index file(MS Excel) has several columns. It contains the names in 2 columns(First and Last). It also has a column containing the number EO11-123.
What I would like to do is write maybe a script to look at the folder names in a directory, compare/find an associated value in the index file(like that number EO11-123) and then rename all the files under the folder using a 4th column value in the index.
So,
Folder name = "Doe, John EO11-123", index column1 contains same value "EO11-123", use column2 value "111111_000000" and rename all the files under that directory folder to "111111_000000_0", "111111_000000_1", "111111_000000_2" and so on.
This possible with powershell or vbscript?
Ok, I'll answer your questions in your comment first. Importing the data into PowerShell allows you to make an array in powershell that you can match against, or better yet make a HashTable to reference for your renaming purposes. I'll get into that later, but it's way better than trying to have PowerShell talk to Excel and use Excel's search functions because this way it's all in PowerShell and there's no third party application dependencies. As for importing, that script is a function that you can load into your current session, so you run that function and it will automatically take care of the import for you (it opens Excel, then opens the XLS(x) file, saves it as a temp CSV file, closes Excel, imports that CSV file into PowerShell, and then deletes the temp file).
Now, you did not state what your XLS file looks like, so I'm going to assume it's got a header row, and looks something like this:
FirstName | Last Name | Identifier | FileCode
Joe | Shmoe | XA22-573 | JS573
John | Doe | EO11-123 | JD123
If that's not your format, you'll need to either adapt my code, or your file, or both.
So, how do we do this? First, download, save, and if needed unblock the script to Import-XLS. Then we will dot source that file to load the function into the current PowerShell session. Once we have the function we will run it and assign the results to a variable. Then we can make an empty hashtable, and for each record in the imported array create an entry in the hashtable where the 'Identifier' property (in your example above that would be the one that has the value "EO11-123" in it), make that the Key, then make the entire record the value. So, so far we have this:
#Load function into current session
. C:\Path\To\Import-XLS.ps1
$RefArray = Import-XLS C:\Path\To\file.xls
$RefHash = #{}
$RefArray | ForEach( $RefHash.Add($_.Identifier, $_)}
Now you should be able to reference the identifier to access any of the properties for the associated record such as:
PS C:\> $RefHash['EO11-123'].FileCode
JD123
Now, we just need to extract that name from the folder, and rename all the files in it. Pretty straight forward from here.
Get-ChildItem c:\Path\to\Folders -directory | Where{$_.Name -match "(?<= )(\S+)$"}|
ForEach{
$Files = Get-ChildItem $_.FullName
$NewName = $RefHash['$($Matches[1])'].FileCode
For($i = 1;$i -lt $files.count;$i++){
$Files[$i] | Rename-Item -New "$NewName_$i"
}
}
Edit: Ok, let's break down the rename process here. It is a lot of piping here, so I'll try and take it step by step. First off we have Get-ChildItem that gets a list of folders for the path you specify. That part's straight forward enough. Then it pipes to a Where statement, that filters the results checking each one's name to see if it matches the Regular Expression "(?<= )(\S+)$". If you are unfamiliar with how regular expressions work you can see a fairly good breakdown of it at https://regex101.com/r/zW8sW1/1. What that does is matches any folders that have more than one "word" in the name, and captures the last "word". It saves that in the automatic variable $Matches, and since it captured text, that gets assigned to $Matches[1]. Now the code breaks down here because your CSV isn't laid out like I had assumed, and you want the files named differently. We'll have to make some adjustments on the fly.
So, those folder that pass the filter will get piped into a ForEach loop (which I had a typo in previously and had a ( instead of {, that's fixed now). So for each of those folders it starts off by getting a list of files within that folder and assigning them to the variable $Files. It also sets up the $NewName variable, but since you don't have a column in your CSV named 'FileCode' that line won't work for you. It uses the $Matches automatic variable that I mentioned earlier to reference the hashtable that we setup with all of the Identifier codes, and then looks at a property of that specific record to setup the new name to assign to files. Since what you want and what I assumed are different, and your CSV has different properties we'll re-work both the previous Where statement, and this line a little bit. Here's how that bit of the script will now read:
Get-ChildItem c:\Path\to\Folders -directory | Where{$_.Name -match "^(.+?), .*? (\S+)$"}|
ForEach{
$Files = Get-ChildItem $_.FullName
$NewName = $Matches[2] + "_" + $Matches[1]
That now matches the folder name in the Where statement and captures 2 things. The first thing it grabs is everything at the beginning of the name before the comma. Then it skips everything until it gets tho the last piece of text at the end of the name and captures everything after the last space. New breakdown on RegEx101: https://regex101.com/r/zW8sW1/2
So you want the ID_LName, which can be gotten from the folder name, there's really no need to even use your CSV file at this point I don't think. We build the new name of the files based off the automatic $Matches variable using the second capture group and the first capture group and putting an underscore between them. Then we just iterate through the files with a For loop basing it off how many files were found. So we start with the first file in the array $Files (record 0), add that to the $NewName with an underscore, and use that to rename the file.