Reversing a file name based on delimiter then truncating part - powershell

I need to rename many hundreds of files to follow a new naming convention, but I'm having awful trouble. This really needs to be scripted in powershell or VBS so we can automate the task in a regular basis.
Original File Name
Monday,England.txt
New File Name
EnglanMo
Convention Rules:
The file name is reversed around the delimiter (,) to England,Monday and then truncated to 6/2 char
Englan,Mo
The Delimiter is then removed
englanmo.txt
Say we had Wednesday,Spain.txt spain being 5 char, this is not subject to any reduction
SpainWe.txt
All the txt files can be accessed in one directory, or from a CSV, whatever is easiest.

Without having the exact details of your file paths, where it'll run, etc. you'll have to adapt this to point at the appropriate path(s).
$s= "Monday,England.txt";
#$s = "Wednesday,Spain.txt";
$nameparts = $s.split(".")[0].split(",");
if ($nameparts[1].length -gt 6) {
$newname = $nameparts[1].substring(0,6);
} else {
$newname = $nameparts[1];
}
if ($nameparts[0].length -gt 2) {
$newname += $nameparts[0].substring(0,2);
} else {
$newname += $nameparts[0];
}
$newname = $newname.toLower() + "."+ $s.split(".")[1];
$newname;
get-item $s |rename-item -NewName $newname;
I'm certain this isn't the most efficient/elegant way to do this, but it works with both of your test cases.

Use Get-ChildItem to grab the files, then on files that match your criteria, use regular expressions to capture the first two characters of the day of the week and the first six characters of the location, then use those captures to create a new filename. This is my best guess. Use -WhatIf on the Move-Item cmdlet until you get the regular expression and the destination path correct.
Get-ChildItem C:\Path\To\Files *.txt |
Where-Object { $_.BaseName -matches '^([^,]{2})[^,]*,(.{1,6})' } |
Move-Item -WhatIf -Destination {
$newFileName = '{0}{1}.txt' -f $matches[1],$matches[2]
Join-Path C:\Path\To\Files $newFileName.ToLower()
}

I think you should be able to achieve this by splitting the string into arrays in powershell and then recording the array to get your reverse.
For example:
$fileNameExtension = "Monday,England.txt";
$fileName = $fileNameExtension.split("."); // gets you an array [Monday,England][txt]
$fileparts = $fileName.split(","); // gets you an array [Monday][England]
//Create the new substring parts, notice you can now pull items from the array in any order you need,
//You will need to check the length before using substringing
$part1 = $fileparts[1].substring(0,5);
$part2 = $fileparts[0].substring(0,2);
//Now construct the new file name by rebuilding the string
$newfileName = $part1 + $part2 + “.” + $fileName[1];

Related

Powershell - how to return results of filenames that don't have a "partner"

I'm attempting to find files in a folder of filenames that look like the following:
C:\XMLFiles\
in.blahblah.xml
out.blahblah.xml
in.blah.xml
out.blah.xml
I need to return results of only files that do not have it's "counterpart". This folder contains thousands of files with randomized "center" portions of the file names....the commonality is in/out and ".xml".
Is there a way to do this in Powershell? It's an odd ask.
Thanks.
Your question is a little vague. I hope I got it right. Here is how I would do it.
$dir = 'my_dir'
$singleFiles = [System.Collections.Generic.HashSet[string]]::new()
Get-ChildItem $dir -Filter '*.xml' | ForEach-Object {
if ($_.BaseName -match '^(?<prefix>in|out)(?<rest>\..+)') {
$oppositeFileName = if ($Matches.prefix -eq 'in') {
'out'
}
else {
'in'
}
$oppositeFileName += $Matches.rest + $_.Extension
$oppositeFileFullName = Join-Path $_.DirectoryName -ChildPath $oppositeFileName
if ($singleFiles.Contains($oppositeFileFullName)) {
$singleFiles.Remove($oppositeFileFullName) | Out-Null
}
else {
$singleFiles.Add($_.FullName) | Out-Null
}
}
}
$singleFiles
I'm getting all the XML files from the directory and I'm iterating the results. I check the base name of the file (the name of the file doesn't include the directory path and the extension) if they match a regex. The regex says: match if the name starts with in or out followed by at least 1 character.
The $Matches automatic variable contains the matched groups. Based on these groups I'm building the name of the counter-part file: i.e. if I'm currently on in.abc I build out.abc.
After that, I'm building the absolute path of the file counter-part file and I check if it exists in the HashSet. if It does, I remove it because that means that at some point I iterated that file. Otherwise, I'm adding the current file.
The resulting HashSet will contain the files that do not have the counter part.
Tell me if you need a more detailed explanation and I will go line by line. It could be refactored a bit, but it does the job.

Renaming text files based on the first word in the file in Powershell

I found many similar examples but not this exact goal. I have a fairly large number of text files that all have a similar first word (clipxx) where xx is a different number in each file.
I want to rename each file using the first word in the file. Here is what I have tried using Powershell. I get an error that I cannot call a method on a null valued expression.
Get-ChildItem *.avs | ForEach-Object { Rename-Item = Get-Content ($line.Split(" "))[0] }
I'd do this in three parts:
Get the list of files you want to change.
Create and map the new name for each file.
Rename the files.
phase-1: get the list of files you want to change.
$files = Get-ChildItem *.avs
phase-2: map the file name to a new name
$file_map = #()
foreach ($file in $files) {
$file_map += #{
OldName = $file.Fullname
NewName = "{0}.avs" -f $(Get-Content $file.Fullname| select -First 1)
}
}
phase-3: make the name change
$file_map | % { Rename-Item -Path $_.OldName -NewName $_.NewName }
Changing things in a list that you're enumerating can be tricky. That's why I recommend breaking this up.
Here is me running this on my machine...
And, here is what was in my files...
Good Luck

How can I replace an entire file name using a CSV index in Powershell?

When the .mp3 files are downloaded from the call recording website they are not in our desired format. I want to replace the entire filename and move the files into a new folder. The default format is:
incoming#_outgoing#_yyyymmdd_hhmmss
I have a Search and Replace CSV which uses data from the call recording website:
Replace: Telephone call reference (TelCall_yyyymmdd_Surname)
Search: Exact call time from call recording software (yyyymmdd_hhmmss)
I have written a script that searches for the exact time and date in the filename and replaces it with the desired format:
$folder = "C:\Users\R\Desktop\ClientCallsTest"
$csv = "C:\Users\R\Desktop\20180104-0224_Client_Calls_PS_CSV.csv"
$keywords = #{}
Import-Csv $csv | ForEach-Object {
$keywords[$_.Search] = $_.Replace
}
Get-ChildItem $folder -Recurse | ForEach-Object {
$newname = $_.Name
foreach ($word in $keywords.Keys) {
$newname = $newname.Replace($word, $keywords[$word])
}
if ($_.Name -ne $newname) {
Rename-Item -Path $_.FullName -NewName $newname
}
}
It indeed does rename the file, but only replaces the string in the CSV table. My desired format is just TelCall_yyyymmdd_Surname.
How would I modify this code to replace the entire file name (deleting the incoming/outgoing numbers at the beginning), rather than just the string in the lookup table? I'm sure it's a quick change but I'm stumped.
You can use the string Split() function to split your original name into parts, like:
$nameParts = "incoming#_outgoing#_yyyymmdd_hhmmss" -split "_"
will give you an array like:
PS C:\>$nameParts
incoming#
outgoing#
yyyymmdd
hhmmss
you can then assemble your now string like:
$newName = "TelCal_" + $nameParts[2] + "_" + $surename
where $surename contains your data from the csv file

Only convert files with the string "DUPLICATE" in the name

I'm trying to make a script that converts PDF's to Tif.
It copies the right files from one folder to another (thanks to the communities previous help).
Next it converts all of the pdfs to tiff.
Lastly it converts the tiff to tif (name change)
What I want to do now is to only convert pdf's with "DUPLICATE" in its file name to tiff. And finally remove the "DUPLICATE" from the new tiff's filename.
Does anyone know how to do that?
gci X:\IT\PDFtoTIFF\1 -filter {VKF*} | Move-Item -destination X:\IT\PDFtoTIFF\2
$tool = 'C:\Program Files (x86)\GPLGS\gswin32c.exe'
$pdfs = get-childitem . -recurse | where {$_.Extension -match "pdf"}
foreach($pdf in $pdfs)
{
$tiff = $pdf.FullName.split('.')[0] + '.tiff'
if(test-path $tiff)
{
"tiff file already exists " + $tiff
}
else
{
'Processing ' + $pdf.Name
$param = "-sOutputFile=$tiff"
& $tool -q -dNOPAUSE -sDEVICE=tiffg4 $param -r300 $pdf.FullName -c quit
}
}
Dir *.tiff | rename-item -newname { $_.name -replace ".tiff",".tif" }
More details:
The script needs to work like this:
All file in the folder \itgsrv028\invoices$\INST that start with vkf need to be moved to this folder: \itgsrv028\invoices$\INST\V3
(This is currently working in the script)
Only convert the files with “Duplicaat” in it’s name to Tiff
Rename VKF_320150309DUPLICAAT.Tiff to 320150309.tif
Example:
These files in the folder:
VKF_320150309.PDF
VKF_320150309DUPLICAAT.PDF
Need to become:
VKF_320150309.PDF
VKF_320150309DUPLICAAT.PDF
320150309.TIF (Converted from: VKF_320150309DUPLICAAT.PDF)
About using only "DUPLICAAT": You have to change your filtering a bit, to include a match for "DUPLICAAT" in there, like this:
$pdfs = get-childitem . -recurse | where {$_.Extension -match "pdf" -and $_.basename -match "DUPLICAAT"}
About building a new name for the TIFF: You can use group placeholders in a regular expression to retrieve your valuable part from the middle of known characters. With your VKF_320150309DUPLICAAT.PDF as an example, you can convert it to a proper TIFF file name with this construction:
$tiff="$($pdf.directory)\$($pdf.basename -replace "VKF_([\w\s]+)DUPLICAAT",'$1').tiff"
This combines a -replace operator over a string, a replacement of $(expression) with its evaluated value in a string and combining proper extension string with path separator within a formatted string. This resolves as follows:
This is a string, as indicated by double quotes wrapping.
$(expression) at first occurrence evaluates to the value of $pdf.directory which contains path to parent without a trailing backslash. With $pdf equal to X:\IT\PDFtoTIFF\2\VKF_320150309DUPLICAAT.PDF this will return X:\IT\PDFtoTIFF\2.
The $(expression) at the second occurrence evaluates to $pdf.basename -replace "VKF_(\w+)DUPLICAAT",'$1'. With the same PDF this equals to "VKF_320150309DUPLICAAT"-replace "VKF_(\w+)DUPLICAAT",'$1'. The round braces regexp portion in the expression matches "320150309" and this value is assigned to $1 which is then placed instead of the whole matched region. Thus your name gets stripped of both "VKF_" and "DUPLICAAT" letters in one go.
The two returned strings get formed into one with a backslash in between and trailing .tiff, resulting in a X:\IT\PDFtoTIFF\2\320150309.tiff.
Hope this would help you in building better scripts that play with strings in Powershell.

Renaming a new folder file to the next incremental number with powershell script

I would really appreciate your help with this
I should first mention that I have been unable to find any specific solutions and I am very new to programming with powershell, hence my request
I wish to write (and later schedule) a script in powershell that looks for a file with a specific name - RFUNNEL and then renames this to R0000001. There will only be one of such 'RFUNELL' files in the folder at any time. However when next the script is run and finds a new RFUNNEL file I will this to be renamed to R0000002 and so on and so forth
I have struggled with this for some weeks now and the seemingly similar solutions that I have come across have not been of much help - perhaps because of my admittedly limited experience with powershell.
Others might be able to do this with less syntax, but try this:
$rootpath = "C:\derp"
if (Test-Path "$rootpath\RFUNNEL.txt")
{ $maxfile = Get-ChildItem $rootpath | ?{$_.BaseName -like "R[0-9][0-9][0-9][0-9][0-9][0-9][0-9]"} | Sort BaseName -Descending | Select -First 1 -Expand BaseName;
if (!$maxfile) { $maxfile = "R0000000" }
[int32]$filenumberint = $maxfile.substring(1); $filenumberint++
[string]$filenumberstring = ($filenumberint).ToString("0000000");
[string]$newName = ("R" + $filenumberstring + ".txt");
Rename-Item "$rootpath\RFUNNEL.txt" $newName;
}
Here's an alternative using regex:
[cmdletbinding()]
param()
$triggerFile = "RFUNNEL.txt"
$searchPattern = "R*.txt"
$nextAvailable = 0
# If the trigger file exists
if (Test-Path -Path $triggerFile)
{
# Get a list of files matching search pattern
$files = Get-ChildItem "$searchPattern" -exclude "$triggerFile"
if ($files)
{
# store the filenames in a simple array
$files = $files | select -expandProperty Name
$files | Write-Verbose
# Get next available file by carrying out a
# regex replace to extract the numeric part of the file and get the maximum number
$nextAvailable = ($files -replace '([a-z])(.*).txt', '$2' | measure-object -max).Maximum
}
# Add one to either the max or zero
$nextAvailable++
# Format the resulting string with leading zeros
$nextAvailableFileName = 'R{0:000000#}.txt' -f $nextAvailable
Write-Verbose "Next Available File: $nextAvailableFileName"
# rename the file
Rename-Item -Path $triggerFile -NewName $nextAvailableFileName
}