rename 2nd extension but allow for duplicates - powershell

I have a server that was infected with ransomware. I have decrypted most of it, but now have files that are a changed filetype or that have been renamed that I need to check:
newsfeed.xml.BLACK_MAMBA_Files#QQ.COM.BLACK_MAMBA_Files#QQ
Google Chrome.lnk.BLACK_MAMBA_Files#QQ
I tried
ren *.BLACK_MAMBA_Files#QQ* *.
I was thinking this would rename all the files, removing the extra text but keeping the original file extension. The error I received was
A duplicate file name exists or the file cannot be found.
I have very limited experience with the command prompt and no experience with PowerShell. If anyone can advise how I should go about this or an alternative, I would appreciate it.

This will rename files to remove the .BLACK_MAMBA_Files suffix in any form:
Get-ChildItem C:\folder -Recurse | Where-Object { $_.Name -like "*BLACK_MAMBA_Files*" } | Rename-Item -NewName { $_.Name -replace ".BLACK_MAMBA_Files.*",""} -WhatIf
NOTE:
I've added -WhatIf as I've only tested this with the two examples you've included. I'm confident it will work fine but it's best to test it first.
With this parameter included you can run the command and it will only display the the results of the rename command, but not actually complete the rename for real.
Remove -WhatIf from the end when you've confirmed that the rename process works correctly with your files.

Edit Reworked the script to work with a RegEX
If the extension is appended multiple times,
run this script as often to remove all occurences.
PushD 'X:\folder\to\start'
$Pattern = '(\.COM)*\.BLACK_MAMBA_Files#QQ'
Get-ChildItem -Recurse -File -Filter "*BLACK_MAMBA*"|
Where Name -match $Pattern|
ForEach {
If (!(Test-Path ($_.FullName -replace $Pattern))) {
$_|Rename-Item -NewName {$_.Name -Replace $Pattern} -confirm
} Else {
"can't rename $($_.FullName) ยด`r`nbecause $($_.FullName -Replace $Pattern) already present"
}
}
PopD
If the script works OK, remove the -Confirm at the end of the Rename-Item.

You Can try this but you should test it first
Get-ChildItem -Path c:\PathtoDirectory -recurse |
Where {$_.FullName -Like "*.*BLACK*"} |
Foreach {Rename-item -Path $_.FullName -NewName $_.BaseName}
With the below command you can see the orignal file name is now the base name so we can use that property to rename them.
Get-Item -Path C:\PathtoFile | select *
You will have to run the command twice for the files with .com on the extention.

#ECHO OFF
SETLOCAL enabledelayedexpansion
SET "sourcedir=U:\sourcedir"
SET "outfile=U:\report.txt"
(
FOR /f "delims=" %%a IN (
'dir /b /s /a-d "%sourcedir%\*.BLACK_MAMBA_Files#QQ*" '
) DO (
SET "newname=%%a"
SET "newname=!newname:.BLACK_MAMBA_Files#QQ=?!"
FOR /f "delims=?" %%r IN ("!newname!") DO ECHO REN "%%a" "%%~nxr"
)
)>"%outfile%"
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
This simple batch should put you on the path. I used my u: ramdrive for testing.
Starting at the directory you define as sourcedir, perform a directory list for filenames only (no directorynames) including subdirectories. Assign each name found matching the mask ".BLACK_MAMBA_Files#QQ" to %%a.
Using the facilities available with delayed expansion, assign %%a to newname, then replace each occurrence of the target string with ?, which is an illegal filename character.
Then use the default tokens=1 with delims=? to assign the first part of the resultant name in newname - up to the first delimiter (which is ?) to %%r.
Then rename using just the name and extension parts of %%r (as that's the only part ren will accept).
I chose to output the ren commands by parenthesising the entire nested for statement and redirecting the output to the file defined as outfile. If this was a .bat file, then you should examine it for sanity and then just run the output file as another batch if it appears to be appropriate. This also allows you to retain a record of the modifications done.

Related

Want to retrieve the Modified date for list of file names present inside a file in CMD prompt

I have a list of file names present inside a file called My_text.txt, may be more than 100. I want to retrieve the Date modified, basically the DIR command output for all those file names.
My_Text.txt contains
D:\Users\dsa99_p\Desktop\My_Program1.txt
D:\Users\dsa99_p\Desktop\My_Program2.txt
D:\Users\dsa99_p\Desktop\My_Program3.txt
D:\Users\dsa99_p\Desktop\My_Program4.txt
and so on..
I want to retrieve the Date modified for all these My_Program1, My_Program2, My_Program3, My_Program4 files. How to do it? Please help.
If it's possible over Powershell then let me know.
In PowerShell the file content can be loaded by Get-Content and file information can be obtained with Get-ChildItem. So this is how it can be done in PowerShell:
Get-Content My_text.txt | ForEach-Object { (Get-ChildItem $_).LastWriteTime }
(Get-ChildItem (Get-Content My_text.txt)).LastWriteTime
Both commands do the same thing. Shorter form of them:
gc My_text.txt |% { (ls $_).LastWriteTime }
(ls (gc My_text.txt)).LastWriteTime
If you want a batch file solution
FOR /F "usebackq delims=" %%G IN ("My_Text.txt") DO ECHO FileName:%%G Modified:%%~tG
Because it is possible that one or more of the files may not exist, I would probably structure my code a little differently. I would first check whether each line related to an existing file, and only then get its information.
The first example I'll provide is for PowerShell, whilst it may seem like more text, it will be far more configurable, especially with regards to modifying the layout and content of the results.
powershell command line:
(Get-Content -Path '.\My_Text.txt') | ForEach-Object { If (Test-Path -LiteralPath $_ -PathType Leaf) { Get-Item -LiteralPath $_ | Select-Object -Property LastWriteTime, Name } }
cmd command line:
For /F "UseBackQ Delims=" %G In (".\My_Text.txt") Do #If "%~aG" Lss "d" If "%~aG" GEq "-" Echo %~tG: %~nxG
Single line batch-file version:
#(For /F "UseBackQ Delims=" %%G In (".\My_Text.txt") Do #If "%%~aG" Lss "d" If "%%~aG" GEq "-" Echo %%~tG: %%~nxG)&Pause
In all examples above, I have assumed that My_Text.txt is in the current directory, if it isn't please change its currently relative location .\ as necessary without modifying its quoting.

rename multiple folder to Uppercase not files using powershell

I am working on powershell where I have multiple folders i need to change all folders names into Uppercase not the files only folder.
I have tried the below code
Get-ChildItem -Path "C:\Users\Xyz\Desktop\sample" -Recurse | % {
if ($_.Name -cne $_.Name.ToUpper()) { ren $_.FullName $_.Name.ToUpper() }
}
But with this code it was changing only file name but I want to change only dir
for example
foldername (lowerCase)
abc
cab
dab
like this (UPPERCASE)
ABC
CAB
DAB
Thanks in advance
These tricks may not be obvious. How's this? Hmm, that didn't actually work. You can't rename folders to the same thing in upper case in powershell.
# doesn't work!
get-childitem -recurse -directory -path "C:\Users\Xyz\Desktop\sample" |
Rename-Item -NewName { $_.name.toupper() } -whatif
Sometimes calling cmd from powershell just works better. Try this first as a "whatif" to see if it does what you want. And if I really understand the question. All this does is echo strings. This command is just "pretend".
get-childitem -recurse -directory -path "C:\Users\Xyz\Desktop\sample" |
foreach { write-host cmd /c ren $_.fullname $_.name.toupper() }
And if that looks good, this actually does the rename. But maybe make a backup in case something goes wrong. Be able to undo the action.
get-childitem -recurse -directory -path "C:\Users\Xyz\Desktop\sample" |
foreach { cmd /c ren $_.fullname $_.name.toupper() }
To limit Get-ChildItem's output to directories only, use the -Directory switch (PSv3+; in PSv2, pipe to Where-Object { $_.PSIsContainer }
Apart from that, your solution should work, but doesn't due to a conceptual flaw in .NET's System.IO.DirectoryInfo.MoveTo() method (and also System.IO.Directory.Move() method), which PowerShell's Rename-Item cmdlet builds on, as of .NET Core 3.0 preview6 / .NET 4.8:
The method doesn't recognize case variations of directory name as a different name, and fails with Source and destination path must be different.
Curiously, files are not affected, as you've experienced.[1]
Of course, while NTFS is case-insensitive, it is also case-preserving, so it should be possible to rename foo to FOO, for instance.
The problem has been reported in this GitHub issue.
Workaround (PSv4+):
Note: js2010's helpful answer offers another, workaround, based on calling cmd.exe for each input folder to use its ren command. While it is conceptually simpler, the caveat is that this approach of creating a child process for every folder processed is inefficient and slow. That said, for occasional renaming operations that probably won't matter.
$path = 'C:\Users\Xyz\Desktop\sample'
Get-ChildItem -LiteralPath $path -Recurse -Directory |
Where-Object { $_.Name -cne $_.Name.ToUpper() } -PipelineVariable dir |
Rename-Item -PassThru -NewName { [IO.Path]::GetRandomFileName() } |
Rename-Item -NewName { $dir.Name.ToUpper() }
The workaround temporarily renames matching folders to a transient, randomly generated name ([IO.Path]::GetRandomFileName()) and then applies the all-uppercase version of the original name.
The input folder's original state and name are captured via variable $dir, which is "stashed" away for later use via the common -PipelineVariable parameter.
[1] Caveat: When you use Get-ChildItem or Get-Item to report a specific directory or file by its literal name, whatever case variation you specify is reported back, even though the true case variation as stored in the filesystem may differ (File Explorer, by contrast, always shows you the true case). To see the true case, useGet-ChildItem <parentDir> -Filter <name>or, as a quick workaround that may show additional items, however, append * to the name/path. In both cases, the name is treated as a wildcard pattern, and the result of the matching process reflects the true case.
How to double quotes folder names ? Some folders has spaces ...
write-host cmd /c ren $.fullname $.name.toupper()

Create text file containing a list of all files of a certain type with their filesize

I want to create a text file with all filenames of a certain filetype plus the filesize, recursively from a specified directory and all subdirectories.
For example: Listing all .jpg files plus their sizes from a huge photo-collection.
I have found several similar questions, but not this specific listing.
One did this with the full path name, but I don't need this and it would become very long.
Another lists all files, but without size.
Another lists all filenames with size, but I can't specify a filetype.
This PowerShell command creates the desired list, but I don't know how to limit it to a certain filetype (e.g. .jpg)
gci -rec|?{!$_.PSIsContainer}|%{"$($_.Fullname) $($_.Length)"} >filelist.txt
This batch file lists all .jpg's, but without showing the filesize.
dir /b /s z:\Filme\*.jpg > list1.txt
for /f "tokens=*" %%A in (list1.txt) do echo %%~nxA >> list.txt
del list1.txt
Could anyone edit one of these? so I get the desired list, or come up with a different solution?
Could anyone edit one of these so I get the desired list?
You are almost there with the batch script.
%~z1 will display the file size (in bytes).
You can also get rid of the temporary file by using a slightly different version of the for command.
Use the following batch file:
#echo off
setlocal
for /f "tokens=*" %%A in ('dir /b /s z:\Filme*.jpg') do (
if /i "%%~xf" equ ".jpg" echo %%~nxf %%~zf
) > list.txt
endlocal
Further Reading
An A-Z Index of the Windows CMD command line | SS64.com
Windows CMD Commands (categorized) - Windows CMD - SS64.com
Command Redirection, Pipes - Windows CMD - SS64.com
Dir - list files and folders - Windows CMD - SS64.com
For - Loop through command output - Windows CMD - SS64.com
If - Conditionally perform command - Windows CMD - SS64.com
Parameters / Arguments - Windows CMD - SS64.com
You know about the %%~nxA modifier, so I'm a bit surprised you didn't notice the %%~zA modifier.
To simplify it even more, use a for /R loop and don't use a temp file:
(for /R %%A in (*.jpg) do echo %%~nxA %%~zA)>list.txt
or if you need the full path\name, use %%~fA (explicite) or even just %%A
Text output:
Get-ChildItem -Path 'X:\PHOTO' -Filter '*.jp*g' -Recurse |
Where-Object {-not $_.PsIsContainer} |
Select-Object Name, Length |
Out-File -FilePath '.\FileList.txt'
CSV output:
Get-ChildItem -Path 'X:\PHOTO' -Filter '*.jp*g' -Recurse |
Where-Object {-not $_.PsIsContainer} |
Select-Object Name, Length |
Export-Csv -Path '.\FileList.csv' -NoTypeInformation
P.S. I've used *.jp*g wildcard that will also match *.jpeg files. Unfortunately, * wildcard matches zero or more symbols, so you can get files like zzz.jpXXXg in your list. There are other ways to filter Get-ChildItem output that don't suffer from this issue, such as filtering with pipeline and regex but they're slower: Where-Object {$_.Extension -match '^\.jp[e]{1}g$'}
Another option would be to not use the -Filter parameter, but the -Include instead where the wildcard pattern works as expected, like this:
PowerShell version 3.0 and up
Get-ChildItem 'z:\Filme' -File -Include '*.jpg' -Recurse |
Select FullName, Length |
Export-Csv '.\FileList.csv' -NoTypeInformation
PowerShell version below 3.0
Get-ChildItem 'z:\Filme' -Include '*.jpg' -Recurse |
Where-Object { !$_.PsIsContainer} |
Select FullName, Length |
Export-Csv '.\FileList.csv' -NoTypeInformation
Note that -Include only works if you also specify -Recurse or if you have the path end in \* like in Get-Childitem 'z:\Filme\*'.
Also, -Filter works faster than -Include (or -Exclude) parameters.
As stated in the docs:
"Filters are more efficient than other parameters, because the provider applies them when the cmdlet gets the objects. Otherwise, PowerShell filters the objects after they are retrieved."
I have never looked into the layout from the Where command, but if it does not alter between languages/locales, or technically if your layout is not too dissimilar to that of my test system, you could do it on your machine like this:
From the Command Prompt:
(For /F "Tokens=1,3*" %A In ('Where/T /R . *.jpg 2^>Nul')Do #Echo("%C","%A")>"list.txt"
From a batch file:
#(For /F "Tokens=1,3*" %%A In ('Where/T /R . *.jpg 2^>Nul')Do #Echo("%%C","%%A")>"list.txt"
Obviously if the layout from your Where command output differs there's still a possibility to adjust the Tokens and/or include delimiters to suit your target system.
In the examples above, I've used . to represent the current directory, you could of course change that to another relative path, e.g. ..\Pictures, or full path, e.g. C:\Users\Patrick\Pictures as necessary.
And a powershell option:
Ls -Filt '*.jpg' -Fo -Rec -EA SilentlyContinue|?{!$_.PSIsContainer -And $_.Extension -Eq '.jpg'}|Select FullName,Length|ConvertTo-CSV -NoT|Select -Skip 1|SC '.\list.txt'
This will also include e.g. system and hidden files, will not include files with extensions other than .jpg and will not include an unrequested header with that listing.
try this
Get-ChildItem "yourdir" -File -Filter '*.jpg' -Recurse |
Select FullName, Length |
Export-Csv '.\FileList.csv' -NoType

Is there one line of code for changing all folders and sub folders file extensions using powershell?

I used the following code to batch change one extension in one folder at a time.
Dir *.mkv | rename-item -newname { $_.name -replace ".mkv",".vlc" }
What I want is to be able to have one line of code that will
change all file extension types to one file extension
include files inside sub folders
Any help on this would be greatly appreciated, THANKS
Use -Recurse to include subfolders. Use BaseName which is the filename without extension.
Read the documentation for more info on either
Get-ChildItem *.mkv -Recurse | Rename-Item -NewName {"$($_.BaseName).vlc"}
Edit
Apologies, I missed this part:
change all file extension types to one file extension
To change all file extensions you just need to exclude the *mkv but as covered by LotPings's answer this isn't recommended; it's very easy to change the extension of files you didn't mean to. He covers using the -Include parameter, which is the recommended way as it filters early on.
In the interest of showing another way, you could use the Extension property. This allows you to leverage all of the PowerShell comparison operators - examples below.
Get-ChildItem -Recurse | Where-Object {$_.Extension -eq ".mkv"}
Get-ChildItem -Recurse | Where-Object {$_.Extension -in #(".mkv",".avi")}
Get-ChildItem -Recurse | Where-Object {$_.Extension -ne ".vlc"}
as you also tagged it batch-file:
for /r %%a in (*.mkv) do #ECHO ren "%%a" "%%~na.vlc"
(this is batch-file syntax. If you want to use it directly on command line)
for /r %a in (*.mkv) do #ECHO ren "%a" "%~na.vlc"
Remove #ECHO, if the output fits your needs.
I wouldn't change ALL extensions, just use -Include and present a list
Get-ChildItem -Path X:\start\here -Recurse -Include ('*.mkv','*.xyz')| Rename-Item -NewName {$_.BaseName+'.vlc'} -WhatIf
If the output looks OK, remove the trailing -WhatIf

Robocopy: Copying files from variable source

I'm trying to copy a specific folder w/ files from a network drive using Robocopy.
The catch is, that the files I want to copy are updated often, and placed in folders with version-numbers. Would it be possible to use Robocopy to grab files from whatever folder has the highest number?
Ex: The source path looks like this:
K:\program\versions\6.7.0.144\
with '144' being the number that is changed often.
The path K:\Program\versions\ contains all versions, each in their own folder, like so:
http://i.stack.imgur.com/zDL16.png
So, each time I run the script, I want it to get files from the latest version/highest number.
So far, my script looks like this:
robocopy \\K:\program\versions\6.7.0.*\bin\config C:\Target /e /z /A-:R
Robocopy does not accept the * in the source-path. So, is this possible with Robocopy, or do I have to use a different approach?
You cannot with robocopy alone. You have to script it a bit.
Assuming you first versions are zeroed (like 6.7.001), then it is easy to get the highest version number you requested.
I provide below snippets for batch & powershell.
Batch:
set SRCPATH=K:\program\versions
for /f %%f in ('dir /b /ad /o-n %SRCPATH%') do set SRCVER=%%f & goto NEXT
:NEXT
echo # Version %SRCVER% will be used
robocopy %SRCPATH%\%SRCVER%\bin\config C:\Target /E /Z /A-:R /LOG:C:\backup.log
goto NEXT is to break for loop after first element, since we sorted by name, descending
Powershell:
$SRCPATH = "K:\program\versions"
$SRCPATH = "D:\temp"
$SRCVER = (Get-ChildItem $SRCPATH | Where-Object { $_.PsISContainer } | Sort-Object -Property Name -Descending | Select-Object -First 1).FullName
$SRCFULL= $SRCVER + '\bin\config'
echo "# Version $SRCVER will be used"
& robocopy $SRCFULL C:\Target /E /Z /A-:R /LOG:C:\backup.log
HTH
The only thing I can suggest vis using Robocopy only is the /MAXAGE: flag.
Otherwise I'd wrap Robocopy in a Powershell Script to do the directory selection for me.
$dirlist = "k:\program\version\6.7.0.1","k:\program\version\6.7.0.144","k:\program\version\6.7.0.77"
$pattern = [regex]'.*6\.7\.0\.(\d*)'
$maxvers = 0
foreach ($dirname in $dirlist) {
$vers = $pattern.match( $dirname ).Groups[1].Value
if($vers -gt $maxvers) { $maxvers = $vers }
}
$robodir = "k:\program\version\6.7.0.$maxvers\bin\config"
robocopy $robodir c:\Target /e /z /A-:R