Windows scripting for loop - windows

I'm new to Windows scripting. I wrote a small batch file to move subdirectories and files around in a large directory.
#ECHO OFF
for /f %x in ('dir /ad /b') do move %xipad %x\
for /f %x in ('dir /ad /b') do md %x\thumbs
for /f %x in ('dir /ad /b') do move %x\*thumb.png %x\thumbs\
for /f %x in ('dir /ad /b') do move %x\*thumb.jpg %x\thumbs\
for /f %x in ('dir /ad /b') do del %x\%xipad\*thumb.png
for /f %x in ('dir /ad /b') do del %x\%xipad\*thumb.jpg
for /f %x in ('dir /ad /b') do del %x\xml.php
for /f %x in ('dir /ad /b') do del %x\%xipad\xml.php
It looks like I can put all of my commands into a single "for /f %x in..." loop and then do the logic inside. I should probably check if the extension is .png or .jpg (not with two separate commands). What's the best way to do these two actions? In addition is there something else I should implement to make this better?

PowerShell looks a little more verbose in this instance, but here's an example anyway. Again, as I mentioned in my comment - if you're trying to learn a scripting language for Windows right now, do yourself a favor and learn PowerShell!
#Get the directories we're going to work with:
Get-ChildItem -Path d:\rootdirectory\ | ? {$_.PSIsContainer} | % {
#Remove all xml.php files from current directory and current directory ipad.
Remove-Item ($_.FullName + "\xml.php")
#For all the files in the directory move the each "ipad" directory into the directory with the same name.
If ($_.Name -like *ipad) {
#Delete all png and jpg images with "thumb" in the name from each current directories ipad directory
Get-ChildItem $_ -filter "*thumb* | ? {($_.Extension -eq "jpg") -or ($_.Extension -eq "png")} | % {
Remove-Item $_
}
#...Then actually move the item
Move-Item $_ -Destination $_.FullName.Replace("ipad","")}
}
#Use else to work on the remainder of the directories:
else {
#Create a directory called "thumbs" inside all of the current directories
$thumbDir = New-Item -ItemType Directory -Path ($_.FullName + "\thumbs\")
#Move all png and jpg files in the current directory with "thumb" in the name into the "thumbs" directory.
Get-ChildItem $_ -filter "*thumb* | ? {($_.Extension -eq "jpg") -or ($_.Extension -eq "png")} | % {
Move-Item $_ -Destination $thumbDir.FullName
}
}

Just do a single for loop the following way:
for /D %%x in (*) do (
move %%xipad %%x\
md %%x\thumbs
move %%x\*thumb.png %x\thumbs\
move %%x\*thumb.jpg %x\thumbs\
del %%x\%%xipad\*thumb.png
del %%x\%%xipad\*thumb.jpg
del %%x\xml.php
del %%x\%%xipad\xml.php
)
Note that you must use a double-% in batch files for those variables. And as you notice you don't need to loop over dir output because for can iterate over files and directories on its own just fine.
As for checking the extension I'm a little at a loss what extension you want to check, specifically. You're iterating over folders but extensions on folders rarely have any significance.

Related

Batch renaming part of filenames using filter to identify files with set prefix

I have a series of image files with a set prefix that I would like to remove. Example of the filenames that I will have:
Image_2022-06-09-12-29-00_Filename-01
Image_2022-07-09-13-29-59_Filename-02
Image_2022-02-11-09-26-31_Filename-03
I would like them to turn into:
Filename-01
Filename-02
Filename-03
I currently have a .bat file that manages to remove the first part of all the files which can either be .jpg or .png files:
set current_dir=%cd%
powershell -Command "get-childitem *.png | rename-item -newname { [string]($_.name).substring(26) }"
powershell -Command "get-childitem *.jpg | rename-item -newname { [string]($_.name).substring(26) }"
My problem now however, is that once I run this and the files are renamed, I cannot copy in new files and run the .bat file again because it will rename the files that have already been renamed. Is it possible to identify a sequence of the first part of a filename, and then apply this only to those files? The first 26 characters will always be in that format, but the numbers may change.
This simple pure Batch file should do the work (and should run much faster than the PS versions):
#echo off
for /F "delims=" %%a in ('dir /B *.png *.jpg') do for /F "tokens=3 delims=_" %%f in ("%%a") do ren %%a %%f
EDIT 2022/06/22: New method to fulfill the new specifications
#echo off
for /F "delims=" %%a in ('dir /B *.png *.jpg') do for /F "tokens=2* delims=_" %%e in ("%%a") do ren %%a %%f
Seems to me all you have to do is to split the file's BaseName (name without extension) on the underscore and take the last element. Then recombine that with the file's extension and you're good to go.
(Get-ChildItem -Path 'X:\WhereTheFilesAre' -File | Where-Object { $_.Extension -match '\.(jpg|png)' }) |
Rename-Item -NewName {'{0}{1}' -f ($_.BaseName -split '_')[-1], $_.Extension }
As per your comment, the filenames could have mixed hyphens and underscores, you can change the Rename-Item scriptblock into
Rename-Item -NewName {'{0}{1}' -f ($_.BaseName -replace '.*[-_](\w+[-_]\d+)$', '$1'), $_.Extension }
Tests:
'Image_2022-06-09-12-29-00_Filename-01' -replace '.*[-_](\w+[-_]\d+)$', '$1' # --> Filename-01
'Image_2022-07-09-13-29-59_Filename_02' -replace '.*[-_](\w+[-_]\d+)$', '$1' # --> Filename_02
As for setting the PowerShell function to use the same current directory as your batch file, please read this answer

mass rename zip files to original name but removing part of name

I have directories with a lot of zips in them.
all with different names of software that have been acquired over time.
Ex:
Software1_ABCDE-FGHIJ.zip
Software2_256SS-BM42.zip
Software3_aswdfbbgtyyn.zip
I want to use batch/powershell or bash to rename them all to
Ex:
Software1.zip
Software2.zip
Software3.zip
Basically anything after _ and before .zip needs to go, But its all random content... Serial keys basically. The names are to long for my php downloader i made on my server, and limit is needed for other things to function so i need to rename 100s of zips.
I already have a for loop to zip all files/folders in a dir in to own zip in bash.
for i in */; do zip -r "${i%/}.zip" "$i"; done
and i already have this bash that takes .exe out of any file name before the .zip
rename.ul '.exe' '' *.zip
Ex:
file1_23434434.exe.zip
became
file1_23434434.zip
How can i mass rename all my zips from
software1_serial-here-1234.zip to software1.zip
keeping original zip name of the software and the .zip extension
Since you tagged your question with powershell also, this would be the PowerShell way to do it:
Get-ChildItem -Filter *.zip | Rename-Item -NewName { $_.Name -replace "_.*", ".zip" }
Short version using batch-like aliases:
dir -filter *.zip | ren -new { $_.name -replace "_.*", ".zip" }
In a Windows command prompt window run in directory containing the ZIP files:
for /F "tokens=1* delims=_ eol=" %I in ('dir *_*.zip /A-D-H /B 2^>nul') do ren "%I_%J" "%I.zip"
In a batch file stored in directory containing the ZIP files:
#for /F "tokens=1* delims=_ eol=" %%I in ('dir "%~dp0*_*.zip" /A-D-H /B 2^>nul') do ren "%~dp0%%I_%%J" "%%I.zip"
If there are multiple underscores after software name instead of just one _ or the software name starts with one or more underscores, it is necessary to use two FOR loops to rename really all *_*.zip files correct:
#for /F "delims= eol=" %%I in ('dir "%~dp0*_*.zip" /A-D-H /B 2^>nul') do #for /F "delims=_ eol=" %%J in ("%%I") do ren "%~dp0%%I" "%%J.zip"
Loop variable I of outer FOR holds complete file name of ZIP file with file extension, but without file path, and loop variable J of inner for loop holds just the file name part before first underscore. J has removed also all underscores at beginning of a file name. So __SoftwareX__Y.zip is renamed to SoftwareX.zip.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /? ... explains %~dp0 ... drive and path of argument 0 which is the batch file path always ending with a backslash.
dir /?
for /?
ren /?
Get-ChildItem -Path C:\temp -Filter software* -File |
Rename-Item -NewName { ($_.BaseName -split '_')[0] + $_.Extension } -WhatIf
Remove the -whatif to apply the action. -whatif is for a preview.
This assumes that all files you want to rename begin with the word software and there is only one _ in the filename.

Moving specific extension up one directory level

My current folder structure goes as follows:
E:\Videos\Movies\Random Folder Name\Subs\Random File Name.srt
I would like to move my .srt files up one level so it reads:
E:\Videos\Movies\Random Folder Name\Random File Name.srt
I would prefer this to be a .bat file, but am willing to use PowerShell.
~EDIT~
I found something online that partially works and edited it to my needs:
#echo off
set thisdir=%cd%
for /f "delims=" %%A in ('dir /b /ad') do (
cd /d "%%~dpnA"
for /f "delims=" %%B in ('dir /b /ad') do (
echo Level 2 Directory: %%~dpnB
cd /d "%%~dpnB"
for /f "delims=" %%C in ('dir /b /ad') do (
echo Level 3 Directory: %%~dpnC
cd /d "%%~dpnC"
move *.srt ..\
cd..
rd "%%~dpnC"
)
)
)
This works, but only for the first folder, I can't seem to make Level 2 recursive as that is the level with random movie names. I tried replace for /f with for /r, but it was a no go.
Here's a one-liner:
forfiles /m *.srt /s /c "cmd /c move #file .."
Full code (you can run this from any drive now):
#echo off
cd /d E:\Videos\Movies\
for /r %%i in (*.srt) do move "%%~dpnxi" "%%~dpi.."
pause
This looks for all files with type .srt and moves them to the folder it was found in -1 directory (%%~dpi is the directory it was found in, adding .. to a path removes the last directory, so C:\Users\.. would put you at C:\).
PS: This time I have tested this, and it works.
Although the answers already given work, I still wanted to try and figure out how to perfect the code to my exact needs. Couldn't accomplish this with CMD, so I looked into powershell (which was easier for me to grasp for some reason) and coded this:
$sourcefolder = "F:\Videos\Movies\*\Subs\"
$files = Get-ChildItem -Recurse $sourcefolder | where {$_.PSIScontainer -eq $false}
foreach ($file in $files)
{
$destinationFolder = Split-Path -Parent $file.Directory.FullName
move-item $file.FullName $destinationFolder
}
It doesn't specify .srt files, but they are the only extension located in that folder. Thank you for the help guys!

Rename all files and folders in folder and sub folder

I need to rename a whole file structure, and find a certain piece of string such as "_foo" and rename it to "". The structure will be like :
something_foo->multiples_folders_and_files_foo->multiples_folders_and_files_foo->files_foo
Where all the files in each folders and each folders in each folders must not contain the piece fo string "_foo".
I am working under windows. I have tried powershell script, I was able to rename a single folder, but not recursivly rename all folders.
thanks guys!
#echo off
setlocal
set "string_to_remove=_foo"
set "root_dir=c:\root"
pushd "%root_dir%"
setlocal enableDelayedExpansion
for %%f in (*) do (
set "file_name=%%f"
ren "!file_name!" "!file_name:%string_to_remove%=!"
)
for /r /d %%# in (*) do (
set "dir_name=%%#"
pushd "%%#"
for %%f in (*) do (
set "file_name=%%f"
ren "!file_name!" "!file_name:%string_to_remove%=!"
)
popd
if "!dir_name!" NEQ "!dir_name:%string_to_remove%=!" move "!dir_name!" "!dir_name:%string_to_remove%=!"
)
popd
endlocal
Not tested but try this (in PowerShell)
PS> ls -rec -path $path | Ren -new { $_.name -replace '_foo' } -passthru
If work remove -passthru switch
Edit:
If '_foo' in the end of the name you can change the expression like:
| Ren -new { $_.basename -replace '_foo$' } -ea silentlycontinue
Edit:
For more robust code: (thank TheMadTechnician)
PS> GCI $path -filter '*_foo*' -rec | Ren -new { $_.name -replace '_foo' } -passthru
#echo off
setlocal enableextensions disabledelayedexpansion
set "remove=_foo"
for /f "delims=" %%a in ('dir /s /b "*%remove%*.*" 2^>nul ^| sort /r') do (
set "name=%%~nxa"
setlocal enabledelayedexpansion
for %%b in ("!name:%remove%=!") do endlocal & echo ren "%%~fa" "%%~b"
)
This will enumerate all matching files/folders, sort in reverse order (so files are processed before the folders that contains them) and for each one, remove the indicated string
Rename operations are only echoed to console. If the output is correct, remove the echo command

How to grab the names of all sub-folders in a batch script?

I just want to know how can I get all the names of the folders in a current directory. For example in my current directory I have three folders:
stackoverflow
reddit
codinghorror
Then when I execute my batch script all the three folders will print in the screen.
How can I achieve this?
Using batch files:
for /d %%d in (*.*) do echo %%d
If you want to test that on the command line, use only one % sign in both cases.
On Windows, you can use:
dir /ad /b
/ad will get you the directories only
/b will present it in 'bare' format
EDIT (reply to comment):
If you want to iterate over these directories and do something with them, use a for command:
for /F "delims=" %%a in ('dir /ad /b') do (
echo %%a
)
note the double % - this is for use in a batch, if you use for on the command line, use a single %.
added the resetting of default space delims in response to #Helen's comment
With PowerShell:
gci | ? { $_.PSIsContainer }
Old Answer:
With PowerShell:
gci | ? {$_.Length -eq $null } | % { $_.Name }
You can use the result as an array in a script, and then foreach trough it, or whatever you want to do...
For getting all the subfolders of a specific directory and display it in CMD :
#echo off
dir C:\input /s /b /o:n /a:d
Pause&Exit
For getting all the subfolders of a specific directory and save it in a text file :
dir C:\your_directory /s /b /o:n /a:d > output.txt

Resources