I have an issue when trying to bulk rename files within folders using forfiles and Powershell. The issue I am having is I need to search for files with _T_ in them, so I use the search notation *_T_*.cr2 as they are all RAW files. In a given folder there will be 143 or so RAW files, 69 of them will have _T_ in their names and the rest don't. What I want to achieve is run a quick command that weeds out all files with _T_ in them and then adds an extra _ before the file.
So before: Ab01_T_gh.cr2 and after _Ab01_T_gh.cr2
My issue is that searching for the _T_ files causes the command to keep executing over and over and over, so the file eventually looks like _____________Ab01etc until it hits the Windows file name limit.
Here's what my forfiles command looks like:
forfiles /S /M *_T_*.cr2 /C "cmd /c rename #file _#file"
It works but it works a little too well.
I also tried Powershell with the same result.
Get-ChildItem -Filter "*_T_*.cr2" -Recurse | Rename-Item -NewName { "_" + $_.Name}
Perhaps there's a way I could split up the "find" and "rename" parts of the code? Any help would be greatly appreciated! I can't manually separate out the _T_ files as it would be very time intensive as each parent folder will sometimes have 75 subfolders with 143 RAW files in each.
This works fine:
#echo off
setlocal EnableDelayedExpansion
for %%a in (*_T_.cr2) do (
set z=%%a
set z=!z:~,1!
if not !z!==_ rename %%a _%%a
)
I had to drop forfiles since I could not work with the # variables (maybe with findstr).
I resorted to a simple for loop on the pattern, and only rename if not already starts by underscore.
Recursive version:
#echo off
setlocal EnableDelayedExpansion
for /R . %%a in (*_T_.cr2) do (
echo %%~na%%~xa
set z=%%~na
set z=!z:~,1!
if not !z!==_ rename %%a _%%~na%%~xa
)
Here's a PowerShell version based on your original code:
Get-ChildItem -Filter "*_T_*.cr2" -Recurse |
Where-Object { $_.Name.Substring(0,1) -ne '_' } |
Rename-Item -NewName $("_" + $_.Name)
Related
I want to remove first 21 characters from every ".pdf" file in folder.
My command:
rename "*.pdf" "/////////////////////*.pdf"
The problem remains only the following: The first file is renamed twice. Deletes the first 42 characters. Other files are renamen correctly (21).
This command would do it, but there are many undetected pitfalls. What if the filename does not have more than 21 characters? What if the NewName duplicates an existing filename?
powershell -NoLogo -NoProfile -Command ^
"Get-ChildItem .\*.pdf |" ^
"ForEach-Object { Rename-Item -Path $_.FullName -NewName $($_.Name[21..($_.Name.Length)] -join '') -WhatIf }"
The problem is, that the file mask *.pdf may be "updated" with newly created files during the process.
The same goes for a simple for %%a in (*.pdf) loop.
To work around this, make sure, the list of files gets generated before rename does it's thing:
for /f "delims=" %%a in ('dir /b *.pdf') do rename "%%a" "/////////////////////*.pdf"
The do part is only executed, after the (...) part is done, so newly created/changed files won't be processed a second time.
Same pitfalls as in lit's answer apply:
What if the filename does not have more than 21 characters?
What if the NewName duplicates an existing filename?
I have a list of en.srt files in my folder. I need to convert them to .srt extension.
For example
Criminal Minds - 1x01 - Extreme Aggressor.en.srt to Criminal Minds - 1x01 - Extreme Aggressor.srt
Tried the below command and it didn't work,
ren *.en.srt *.srt
Renaming extension like ren *.srt *.srv works. (Changing all files' extensions in a folder with one command on Windows)
Would like to know if there is a workaround for this?
A simply though clumsy method is to rename the files twice – first remove .srt, then change .en to .srt (given that there are no other files *.en):
ren "*.en.srt" "*." & ren "*.en" "*.srt"
A more elegant solution is the one provided by user Mofi in his comment:
#for /F "eol=| delims=" %I in ('dir "*.en.srt" /B /A:-D 2^> nul') do #for %J in ("%~nI") do #ren "%~I" "%~nJ%~xI"
In a batch-file this code would look similar to this (note the necessarily doubled %-signs):
#echo off
rem // Loop through all matching files:
for /F "eol=| delims=" %%I in ('dir "*.en.srt" /B /A:-D 2^> nul') do (
rem /* There are `~`-modifiers for `for` meta-variables that allow to split file names:
rem `~n` returns the base name, so the (last) extension becomes removed;
rem `~x` returns the extension (including the leading `.`);
rem therefore, `%%~nI` is the original file name with `.srt` removed, hence
rem ending with `.en`, and `%%~xI` is the original extension `.srt`;
rem another loop is used to also split off `.en` from `%%~nI`: */
for %%J in ("%%~nI") do (
rem /* Now `%%~J` returned the same as `%%~nI`, but `%%~nJ` removes `.en`;
rem so finally, rename the file to `%%~nJ` plus the original extension `.srt`: */
ren "%%~I" "%%~nJ%%~xI"
)
)
Following the thorough thread How does the Windows RENAME command interpret wildcards? on Super User, I found out that there is a way using a single ren command:
ren "*.en.srt" "?????????????????????????????????????????.srt"
However, you need to make sure to have enough ?, namely as many as there are characters in longest matching file name without .en.srt; otherwise, file names become truncated. You can avoid truncation by replacing the same sequence of ? instead of *, so longer file names are not renamed at all:
ren "?????????????????????????????????????????.en.srt" "?????????????????????????????????????????.srt"
Anyway, this only works when the original file names do not contain any more . besides the two in .en.srt; otherwise, everything behind the first . becomes removed and (finally replaced by srt).
Not difficult in PowerShell to identify the files and replace the end of the filename with a regex. When you are confident that the files will be renamed correctly, remove the -WhatIf from the Move-Item command.
powershell -NoLogo -NoProfile -Command ^
"Get-ChildItem -File -Path '.' -Filter '*.en.srt' |" ^
"ForEach-Object {" ^
"Move-Item -Path $_.FullName -Destination $($_.FullName -replace 'en.srt$','srt') -WhatIf" ^
"}"
Of course, it would be easier if the command shell were PowerShell. BTW, this exact same code would work on Linux and Mac without change.
Get-ChildItem -File -Path '.' -Filter '*.en.srt' |
ForEach-Object {
Move-Item -Path $_.FullName -Destination $($_.FullName -replace 'en.srt$','srt') -WhatIf
}
I don't have a cmd at my disposition but I would guess that
ren *.en.srt *.tmp
ren *.tmp *.srt
works
I have a text file list of approx 120,000 filenames. Many of the files on the list are in a folder or it's subfolders, but with slight variations on the filenames.
so I want to search using the list of partial filenames and copy the matches to another folder.
Each line on the list is a name and a title separated by a bar for example:
A Name|The Title
John Smith|A Life
The files are various text formats and all have extra stuff in the filenames like:
A Name - The Title V1.4 (html).lit
John Smith - A Life: Living on the Edge [MD] (pdf).rar
I've tried the code from this thread
and this thread but neither are finding any of the files. Can anyone help please.
This PowerShell script assumes that if both the first field "name" and the second field "title" are anywhere in the filename that it should be copied. When you are confident that the correct files will be copied, remove the -WhatIf from the Copy-Item command.
Note that this does not address the issue of multiple files with the same name.
If you wanted to require the "name" field to be at the beginning of the string, you could add it to the match expression. $_.Name -match '^'+$pair.name. If you want the matches to be case sensitive, use -cmatch.
$sourcepath = 'C:\src'
$targetpath = 'C:\other'
$searchpairs = Import-Csv -Header "name","title" -Delimiter "|" -Encoding ASCII -path .\mdb.txt
foreach ($pair in $searchpairs) {
Get-ChildItem -Recurse -File -Path $sourcepath |
Where-Object { ($_.Name -match $pair.name) -and ($_.Name -match $pair.title) } |
ForEach-Object { Copy-Item $_.FullName $targetpath -WhatIf}
}
Adjust the paths and you should be good with this one:
#ECHO OFF
REM **************************************************
REM Adjust location of list
SET list=C:\adjust\path\list.txt
REM Source dir
SET source=C:\adjust\path\source
REM Target dir
SET destination=C:\adjust\path\destination
REM **************************************************
FOR /F "tokens=1,* delims=|" %%A IN (%list%) DO (
ECHO.
ECHO %%A - %%B
CALL :copy "%%A - %%B"
)
ECHO.
ECHO Done^!
PAUSE
EXIT
:copy
FOR /R "%source%" %%F IN (*) DO (
ECHO "%%~nF" | FINDSTR /C:%1 >nul && COPY "%%~fF" "%destination%\%%~nxF" && EXIT /B
)
EXIT /B
Be aware: this requires the scheme in list.txt to be A|B and the scheme of every file to be copied *A - B* (including spaces) while * may be no or any character(s).
Might not be the solution you are looking for, but I ditched batch scripting years ago. I use Powershell instead, and simply call the Powershell script from batch file.
Here is the code in case you are interested,
searchfiles.ps1
$searchStrings = #("city", "sniper") # Declare an array to iterate over.
foreach ($string in $searchStrings) {
Get-ChildItem -Path D:\Movies\Movies | ? { $_ -match $string }
}
And now call the Powershell script from batch file.
searchfiles.bat
Powershell.exe -ExecutionPolicy RemoteSigned -Command "& searchfiles.ps1 -Verb RunAs"
Hope it helps!
That's not to say I don't use batch scripting at all. I will use them only for simpler operations, like calling another script, or opening a folder, etc. With Powershell, I love taking the help of the underlying .NET framework and sweet piping!
I got difficulties in replacing multiple file names.
The scenario would be :
[Before]
C:¥data¥
-HOSTNAME1_20170921_5555.zip
-HOSTNAME2_20170921_5555.zip
-HOSTNAME3_20170921_5555.zip
[After]
C:¥data¥
-HOSTNAME1_20170908_5555.zip
-HOSTNAME2_20170908_5555.zip
-HOSTNAME3_20170908_5555.zip
I tried the below command but not getting satisfied result.
RENAME C:¥data¥*20170921_5555.zip *20170908_5555.zip
Unfortunately the result was:
C:¥data¥
-HOSTNAME1_20170920170908_5555.zip
-HOSTNAME2_20170920170908_5555.zip
-HOSTNAME3_20170920170908_5555.zip
Anyone can solve the problem?
and tell me why the above code was not working as expected.
You specifically require a single line, so here is a powershell version.
get-childitem *20170921_5555.zip | foreach { rename-item $_ $_.Name.Replace("20170921_5555", "20170908_5555") }
Use a for to iterate the files with the wrong date
a 2nd stacked for /f to split the name at the _ and
rename replacing the 2nd element with the proper date
With one single cmd line:
> #for %A in (*_20170921_*.zip) do #for /f "tokens=1,2* delims=_" %B in ("%A") do #echo ren "%A" "%B_20170908_%D"
ren "¥data¥-HOSTNAME1_20170921_5555.zip" "¥data¥-HOSTNAME1_20170908_5555.zip"
ren "¥data¥-HOSTNAME2_20170921_5555.zip" "¥data¥-HOSTNAME2_20170908_5555.zip"
ren "¥data¥-HOSTNAME3_20170921_5555.zip" "¥data¥-HOSTNAME3_20170908_5555.zip"
If the output looks OK remove the echo in front of the ren command.
If used in a batch file the percent signs have to be doubled %%
BTW you could also use this Powershell script from batch:
powershell -NoP -C "gci '*_20170921_*.zip'|rni -NewName {$_.Name -replace '_20170921_','_20170908_'} -WhatIf
If the output looks OK, remove the trailing -WhatIf
I need to copy all .TXT files under a directory [subfolders included] and copy them to another directory [without subfolders] but I can't change the extension of the files after I copy them, for example to .OLD or .TXT.OLD. Is that possible to do it via .bat file? Is there any special tool to use instead?
Consider using powershell instead:
Get-ChildItem C:\path\to\root\dir -recurse | where {$_.extension -eq ".txt"} | % {Copy-Item $_.FullName -Destination C:\path\to\target\dir}
You just need to use the root part of the filename and then append your own extension in the rename. For example, to demonstrate that (you don't say you're having any issue with the logic of the actual process, just the extension manipulation, so that's what I'm addressing):
FOR %%I IN (*.txt) DO REN %%I %%~nI.txt.old
This will take all *.txt files in the current directory and rename them to the same root + extension .txt.old. There's a possible complication in that the resulting filename cannot already exist in a rename, so you may want to put it into a more complex loop such as:
FOR %%I IN (*.txt) DO (
[do other stuff here]
IF EXIST %%~nI.txt.old DEL %%~nI.txt.old
REN %%I %%~nI.txt.old
)
Try for /? at a command prompt and check out the last part of it for the syntax of filename/path substitution parameters