Windows cmd command for stripping versions from filenames? - powershell

Need Windows cmd command to rename files to names without version numbers, e.g.:
filename.exa.1 => filename.exa
filename_a.exb.23 => filename_a.exb
filename_b.exc.4567 => filename_b.exc
Filenames are variable in number of characters, and the primary extension is always 3 characters.
I once had a Solaris script "stripv" to accomplish this. I could enter "stripv *" in a directory and get a nice clean set of non-versioned files. If the command would result in duplicate filenames because multiple versions exist, then it would just skip the operation altogether.
TIA

Don't know how to do it in CMD, but here is some Powershell that would work for you:
# Quick way to get an array of filenames. You could also create a proper array,
# or read each line into an array from a file.
$filepaths = #"
C:\full\path\to\filename.exa.1
C:\full\path\to\filename_a.exb.23
\\server\share\path\to\filename_b.exc.4567
"# -Split "`n"
# For each path in $filepaths
$filepaths | Foreach-Object {
$path = $_
# Split-Path -Leaf gets only the filename
# -Replace expression just means to match on the ".number" at the end of the
# filename and replace it with an empty string (effectively removing it)
$newFilename = ( Split-Path -Leaf $path ) -Replace '\.\d+$', ''
# Warning output
Write-Warning "Renaming '${path}' to '${newFilename}'"
# Rename the file to the new name
Rename-Item -Path $path -NewName $newFilename
}
Basically, this code creates an array of full paths to files. For each path, it strips the filename from the full path and replaces the .number pattern at the end with nothing, which removes it from the filename. Now that we have the new filename, we use Rename-Item to rename the file to the new name.

Supply the folder name to this script block's $Folder variable, and it will enumerate the items within that folder, locate the last '.' character within the file name, and rename it as everything prior to the '.'.
E.g.: Filename.123.wrcrw.txt.123 would be renamed as Filename.123.wrcrw.txt or in your case, your files would lose the extraneous characters from the final '.' onwards. If the new name for the file already exists, it will write a warning stating that it could not rename the file, and continue on without trying.
$Folder = "C:\ProgramData\Temp"
Get-ChildItem -Path $Folder | Foreach {
$NewName = $_.Name.Substring(0,$_.Name.LastIndexOf('.'))
IF (!(Test-Path $Folder\$NewName))
{
Rename-Item $Folder\$_ -NewName $NewName
}
Else
{
Write-Warning "$($_.Name) cannot be renamed, $NewName already exists."
}
}
This should effectively mimic the behaviour you described for stripv *. This could easily be turned into a function with name stripv and added to your PowerShell profile to make it available at the command-line interactively and used in the same way as your Solaris script.

Related

Powershell Change path in variable

i need you help again :D
I have created a function to put the error logs in a file who take the name of my script (i call multiples scripts so it's very helpful), here is my function :
function ExportLog{
$path = Get-Location
$LogFile = [io.path]::ChangeExtension($MyInvocation.ScriptName,"log")
Write-Host $LogFile
$timestamps = Get-Date
$string_err = $_ | Out-String
$ExportError = "[" + $timestamps.DateTime + "]`n" + $string_err + "`n"| Out-File -FilePath $LogFile -Append
Read-Host “Appuyez sur ENTRER pour quitter...”}
This works fine but the log file created or edited is in the path of my script.
My question is how can i add \log\ in the path who is in my variable $LogFile ?
I tried to use Join-Path, but it just add path like this : C:\import\Modif_CSV.log\Logs ... I wan't to add the Logs folder before the name of the file ^^
Ty for help :)
You can split the current script filename from the full path and change the extension with:
$LogFileName = [IO.Path]::ChangeExtension((Split-Path $PSCommandPath -Leaf), 'log')
Next combine the current script path with the subfolder 'log' and with the new filename
$LogFullName = [IO.Path]::Combine($PSScriptRoot, 'log', $LogFileName)
Theo's helpful answer shows a .NET API-based solution that works in both Windows PowerShell and PowerShell (Core) 7+.
Here's a PowerShell (Core) 7+ solution that showcases new features (relative to Windows PowerShell):
$dir, $name = $PSCommandPath -split '\\', -2
Join-Path $dir log ((Split-Path -LeafBase $name) + '.log')
-split '\\', -2 splits the path into two strings by \: the last \-separated token, preceded by everything before the last \, thereby effectively splitting a file path into its directory path and file name. That is, -split now accepts a negative number as the count of tokens to return, with -$n meaning: return $n-1 tokens from the right of the input string (albeit in left-to-right order), and save any remaining part of the string in the return array's first element; e.g., 'a/b/c/d' -split '/', -3 yields 'a/b', 'c', 'd'
Split-Path -LeafBase returns a file path's file-name base, i.e. the file name without its extension.
Join-Path now accepts an open-ended number of child paths to join to the parent path; e.g., Join C:\ dir subdir now works to create C:\dir\subdir, whereas in Windows PowerShell you had to nest calls: Join-Path (Join-Path C:\ dir) subdir
Note: It would be handy if Split-Path supported returning all components of a given path in a single operation; GitHub issue #6606 proposes an -All switch that returns an object whose properties reflect all the constituent parts of the path, which would enable the following simplified solution:
# WISHFUL THINKING, as of PowerShell 7.2
$pathInfo = Split-Path -All $PSCommandPath
Join-Path $pathInfo.Parent log ($pathInfo.LeafBase + '.log')

Search and replace files and folders names with txt file support

I have many folders and inside these different files. Each folder and their children files have the same name and different extension, so in the ABC folder there are the ABC.png, ABC.prj, ABC.pgw files, in the DEF folder there are the DEF.png, DEF.prj, DEF.pgw files and so on.
With a script I have created a txt file with the list of png file names. Then I put in row 2 a new name for the name in row1, in row 4 a new name for the name in row 3, and so on.
Now I'm searching a powershell script that:
- scan all folder for the name in row 1 and replace it with name in row2
- scan all folder for the name in row 3 and replace it with name in row4 and so on
I have try with this below, but it doesn't work.
Have you some suggestions? Thank you
$0=0
$1=1
do {
$find=Get-Content C:\1\Srv\MapsName.txt | Select -Index $0
$repl=Get-Content C:\1\Srv\MapsName.txt | Select -Index $1
Get-ChildItem C:\1\newmaps -Recurse | Rename-Item -NewName { $_.name -replace $find, $repl} -verbose
$0=$0+2
$1=$1+2
}
until ($0 -eq "")
I believe there are several things wrong with your code and also the code Manuel gave you.
Although you have a list of old filenames and new filenames, you are not using that in the Get-ChildItem cmdlet, but instead try and replace all files it finds.
Using -replace uses a Regular Expression replace, that means the special character . inside the filename is regarded as Any Character, not simply a dot.
You are trying to find *.png files, but you do not add a -Filter with the Get-ChildItem cmdlet, so now it will return all filetypes.
Anyway, I have a different approach for you:
If your input file C:\1\Srv\MapsName.txt looks anything like this:
picture1.png
ABC_1.png
picture2.png
DEF_1.png
picture3.png
DEF_2.png
The following code will use that to build a lookup Hashtable so it can act on the files mentioned in the input file and leave all others unchanged.
$mapsFile = 'C:\1\Srv\2_MapsName.txt'
$searchPath = 'C:\1\NewMaps'
# Read the input file as an array of strings.
# Every even index contains the file name to search for.
# Every odd index number has the new name for that file.
$lines = Get-Content $mapsFile
# Create a hashtable to store the filename to find
# as Key, and the replacement name as Value
$lookup = #{}
for ($index = 0; $index -lt $lines.Count -1; $index += 2) {
$lookup[$lines[$index]] = $lines[$index + 1]
}
# Next, get a collection of FileInfo objects of *.png files
# If you need to get multiple extensions, remove the -Filter and add -Include '*.png','*.jpg' etc.
$files = Get-ChildItem -Path $searchPath -Filter '*.png' -File -Recurse
foreach ($file in $files) {
# If the file name can be found as Key in the $lookup Hashtable
$find = $file.Name
if ($lookup.ContainsKey($find)) {
# Rename the file with the replacement name in the Value of the lookup table
Write-Host "Renaming '$($file.FullName)' --> $($lookup[$find])"
$file | Rename-Item -NewName $lookup[$find]
}
}
Edit
If the input text file 'C:\1\Srv\MapsName.txt' does NOT contain filenames including their extension, change the final foreach loop into this:
foreach ($file in $files) {
# If the file name can be found as Key in the $lookup Hashtable
# Look for the file name without extension as it is not given in the 'MapsName.txt' file.
$find = [System.IO.Path]::GetFileNameWithoutExtension($file.Name)
if ($lookup.ContainsKey($find)) {
# Rename the file with the replacement name in the Value of the lookup table
# Make sure to add the file's extension if any.
$newName = $lookup[$find] + $file.Extension
Write-Host "Renaming '$($file.FullName)' --> '$newName'"
$file | Rename-Item -NewName $newName
}
}
Hope that helps
The problem in your snippet is that it never ends.
I tried it and it works but keeps looping forever.
I created a folder with the files a.txt, b.txt and c.txt.
And in the map.txt I have this content:
a.txt
a2.md
b.txt
b2.md
c.txt
c2.md
Running the following script I managed to rename every file to be as expected.
$0=0
$1=1
$find=Get-Content D:\map.txt | Select -Index $0
while($find) {
$find=Get-Content D:\map.txt | Select -Index $0
$repl=Get-Content D:\map.txt | Select -Index $1
if(!$find -Or !$repl) {
break;
}
Get-ChildItem D:\Files -Recurse | Rename-Item -NewName { $_.name -replace $find, $repl} -verbose
$0=$0+2
$1=$1+2
}

Renaming a file name to exclude the first couple parts Powershell

I have over a million files like such: First_Last_MI_DOB_ and lots more information. Is there a way I can run a rename script that can remove just the first, last, Mi, and DOB from the file name, but keep the stuff after that? Thank you.
Edited from my answer to this question: Parse and Switch Elements of Folder Names using Powershell
# Path to folder
$Path = '.\'
# Regex to match "ID_000000..."
$Regex = 'ID_\d+.*$'
# Get all objects in path
Get-ChildItem -Path $Path |
# Select only objects that are not directory and name matches regex
Where-Object {!$_.PSIsContainer -and $_.Name -match $Regex} |
# For each such object
ForEach-Object {
# Rename object
Rename-Item -Path $_.FullName -NewName $Matches[0]
}
UPDATE #1 : It seems that you need to write a regex that will match a required part of the name and then use it in to rename a document.
Assuming that file name is x-John_Doe_._DOB_01-11-1990_M_ID_000000_TitleofDocument_DateofDocument_Docpagenu‌​mber_, here is couple of the examples:
Regex (https://regex101.com/r/gI0fZ2/2): (ID_\d+.*)$ - will match ID_{ONE_OR_MORE_DIGITS}{ANY_CHARACTERS}
Result:ID_000000_TitleofDocument_DateofDocument_Docpagenu‌​mber_
Regex (https://regex101.com/r/gI0fZ2/1): \d{4}_(M|F)_(.*)$ - will match {4_DIGITS}_M_{or}_F_ and capture everything after that in capture group.
Result:
1st match - M
2nd match (the one to use) - ID_000000_TitleofDocument_DateofDocument_Docpagenu‌​mber_
UPDATE #2:
All the names in each file are different, a long with different ID's.
For example: John_Doe_DOB_01/01/01_ID_000000 and the next file name
could be: John_Smith_DOB_01/02/01_ID_100000 and so on. I am thinking I
would just want to read the file name in as a string, split it by _
and then make the new file name the stuff from [4] and after. Is there
a way to do that?
Sure, you can do that, but I'd recommend a regex approach, because it would work for every filename that has ID_0xxxx string, no matter of what. I've modified my initial example with first regex, so it should work for you.
But if you'd like to try splitting approach, here is how to do it:
# Path to folder
$Path = '.\'
# Filename separator
$Separator = '_'
# Get all objects in path
Get-ChildItem -Path $Path |
# Select only objects that are not directory and name matches regex
Where-Object {!$_.PSIsContainer} |
# For each such object
ForEach-Object {
# Generate new name
$NewName = ($_.Name -split $Separator | Select-Object -Skip 4) -join $Separator
# Rename object
Rename-Item -Path $_.FullName -NewName $NewName
}

Powershell Copying files with varying folders in the path

Right up front apologies for my lack of knowledge with Powershell. Very new to the language . I need to copy some files located in a certain path to another similar path. For example:
C:\TEMP\Users\<username1>\Documents\<varyingfoldername>\*
C:\TEMP\Users\<username2>\Documents\<varyingfoldername>\*
C:\TEMP\Users\<username3>\Documents\<varyingfoldername>\*
C:\TEMP\Users\<username4>\Documents\<varyingfoldername>\*
etc....
to
C:\Files\Users\<username1>\Documents\<varyingfoldername>\*
C:\Files\Users\<username2>\Documents\<varyingfoldername>\*
C:\Files\Users\<username3>\Documents\<varyingfoldername>\*
C:\Files\Users\<username4>\Documents\<varyingfoldername>\*
etc....
So basically all files and directories from path one need to be copied to the second path for each one of the different paths. The only known constant is the first part of the path like C:\TEMP\Users...... and the first part of the destination like C:\Files\Users.....
I can get all the different paths and files by using:
gci C:\TEMP\[a-z]*\Documents\[a-z]*\
but I am not sure how to then pass what's found in the wildcards so I can use them when I do the copy. Any help would be appreciated here.
This should work:
Get-ChildItem "C:\TEMP\*\Documents\*" | ForEach-Object {
$old = $_.FullName
$new = $_.FullName.Replace("C:\TEMP\Users\","C:\Files\Users\")
Move-Item $old $new
}
For additional complexity in matching folder levels, something like this should work:
Get-ChildItem "C:\TEMP\*\Documents\*" -File | ForEach-Object {
$old = $_.FullName
$pathArray = $old.Split("\") # Splits the path into an array
$new = [system.String]::Join("\", $pathArray[0..1]) # Creates a starting point, in this case C:\Temp
$new += "\" + $pathArray[4] # Appends another folder level, you can change the index to match the folder you're after
$new += "\" + $pathArray[6] # You can repeat this line to keep matching different folders
Copy-Item -Recurse -Force $old $new
}

Powershell script to copy and rename files in a loop

I have a number of files with extension .psa in my Prevalidation folder and I want to:
copy them one by one into my working folder
rename the .psa file to psaload.csv
run a set of commands against the file to load it to my db
and then delete the csv file from my working folder.
This will be repeated for all the .psa files I have on my source folder.
So, the question is how do I execute the set of commands in a loop over as many .psa files as present.
Here's my piece of code testing for only one file in my Prevalidation folder -
Copy-Item C:\Downloads\PreValidation\*.psa C:\Downloads\Validation\WIP
Rename-Item 'C:\Downloads\Validation\WIP\abc 1234.psa' 'psaload1.csv'
Get-Content C:\Downloads\Validation\WIP\psaload1.csv | ForEach-Object { $_.replace("\,"," ") } | Set-Content C:\Downloads\Validation\WIP\psaload.csv
Remove-Item C:\Downloads\Validation\WIP\psaload1.csv
<run the psaload.csv to load to my db>
This is what I intend to do -
Consider multiple .psa files in my C:\Downloads\Prevalidation folder.
For each C:\Downloads\PreValidation\*.psa
BEGIN LOOP
Copy-Item C:\Downloads\PreValidation\aaaa.psa C:\Downloads\Validation\WIP\aaaa.psa
Rename-Item 'C:\Downloads\Validation\WIP\aaaa.psa' 'psaload1.csv'
Get-Content C:\Downloads\Validation\WIP\psaload1.csv | ForEach-Object { $_.replace("\,"," ") } | Set-Content C:\Downloads\Validation\WIP\psaload.csv
Remove-Item C:\Downloads\Validation\WIP\psaload1.csv
END LOOP
I am looking for the syntax to run these set of commands for each files one by one as present in my /prevalidation folder.
Since all the other answers were quite horrible code and not very idiomatic PowerShell, here is my take (though untested):
# Get all .psa files
Get-ChildItem C:\Downloads\PreValidation\*.psa |
ForEach-Object {
# Load the file's contents, replace commas with spaces
(Get-Content $_) -replace ',', ' ' |
# and write it to the correct folder and file name
Out-File C:\Downloads\WIP\psaload.csv
# I guess you'd run whatever you're doing against the file here,
# not after the loop
Remove-Item C:\Downloads\WIP\psaload.csv
}
You can use foreach with Get-Item to do the loop. Get-Item will return a FileInfo
object that you can use to get the file name (and other info) from. So you could do something like:
foreach($file in (Get-Item .\*.psa))
{
Copy-Item $file.FullName "C:\Downloads\Validation\WIP\$($file.Name)";
}
etc.
Try this:
$a = Get-Item .\*.psa
foreach($file in $a)
{
Copy-Item $file.FullName "C:\Downloads\Validation\WIP\$($file.Name.replace(".psa",".psload.csv)";
remove-item $file
}