Changing Multiple Filenames in multiple Folders with Batch - powershell

I have several Folder which contain from 5 to 20 Files with all different names. They get replaced every week, so the Name of each File also changes. But i Need them to have specific Names so i can upload them by using my SQL loader.
Is there a way to create a Batch file, which goes into every Folder that i specify, select all Files and changes all the names? Perfect Solution would just be a upcounting number like: file1.xml, file2.xml etc.
Since im a total newbie to Batch i searched around a bit and found following code, but it only changes the files in 1 specific Folder.
Dir *.xml | ForEach-Object -begin { $count=1 }
-process { rename-item $_ -NewName "$count.xml"; $count++ }
Update 1
I found the working code which allows me to rename the files in a Folder as i want them to be. I would just Need a code, that allows me to do this to several other Folders at the same time or automatically one after another.
#echo off & setlocal EnableDelayedExpansion
set a=1
for /f "delims=" %%i in ('dir /b *.xml') do (
ren "%%i" "!a!.xml"
set /a a+=1
)

#echo off & setlocal EnableDelayedExpansion
set a=1
rem make old name to prevent same name collision
for /D %%i in (*) do (
cd %%i
for /f "delims=" %%j in ('dir /b *.xml') do (
ren "%%j" "%%j-old.xml"
)
cd ..
)
rem rename process
for /D %%i in (*) do (
cd %%i
for /f "delims=" %%j in ('dir /b *.xml') do (
ren "%%j" "!a!.xml"
set /a a+=1
)
cd ..
)
echo Done
pause
Start .
hope it helps.

Here's a PowerShell code to do the same for several folders (sub-folders)
$path = 'Z:\'
Get-ChildItem -Path $path -Filter *.xml -Recurse | ForEach-Object -Begin {
[int]$count = '1'
} -Process {
Rename-Item -Path $_.FullName -NewName "$count.xml" -ErrorAction Stop
$count++
}

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.

How to find whether a particular folder has modifications on today using batch file

I have a folder and using batch file I want to know whether its modified(any file created or modified in it) today or not.
Below is command to get count of all files in a folder. I am new in batch files and don't know how to get it, pl help.
set MyFolder=D:\folder1
SET file_Cnt=0
for %%o IN (%MyFolder%/*.*) DO (
SET /A file_Cnt=file_Cnt+ 1
)
echo %file_Cnt%
Edit :-
I got this solution but only problem is I want to set a variable(for total count of files modified) here which can be used later.
forfiles /S /P "%MyFolder%" /M "*" /D +0 /C "cmd /C if #isdir==FALSE echo _" | find /C "_"
Edit2:- I have tried below commands to set the variable but not working.
forfiles /S /P "%MyFolder%" /M "*" /D +0 /C "cmd /C if #isdir==FALSE echo _" | find /C "_"
echo %_% --getting echo off message
for /F %%N in ('forfiles /S /P "%MyFolder%" /M "*" /D +0 /C "cmd /C if #isdir==FALSE echo _" ^| find /C "_"') do set "NUMBER=%%N"
echo %NUMBER% --getting echo off message
echo %%N --getting echo off message
I would suggest you to use PowerShell for this.
Get-ChildItem -Path 'D:\Folder1' | Where-Object -FilterScript { $_.LastWriteTime.Date -eq (Get-Date).Date}
To call from command prompt.
PowerShell.exe -c "Get-ChildItem -Path 'D:\Folder1' | Where-Object -FilterScript { $_.LastWriteTime.Date -eq (Get-Date).Date}|measure | select -expand count"
The following command gets the list of all the files from the current folder that are created on the same day.
for /F "tokens=2" %i in ('date /t') do dir /T:C | findstr /C:%i /B
Hope this helps !
If you just want to output filename, you can try this command
for /F "tokens=2" %i in ('date /t') do dir /T:C "C:\Test" | findstr /C:%i /B | for /f "tokens=5" %j in ('more') do #echo %j
Or if you want to get the count, you can try this command
for /F "tokens=2" %i in ('date /t') do dir /T:C "C:\Test" | findstr /C:%i /B | find /c ":"
for /F %%N in ('forfiles /S /P "%MyFolder%" /M "*" /D +0 /C "cmd /C if #isdir==FALSE echo _" ^| find /C "_"') do (
// we can simply get %%N here or we can also set some variable here if required.
)
I think the answer for this question could possible help you, they have scripts for getting the LastModified, LastAccessed and DateCreated from folders and files.
Batch file : get the creation date of a folder
Below is my test result. What the error in your command, #Mona ?
commandline

Need a script to delete certain type of files in a folder based on conditions

I am having some issues with OneNote overpopulating the drive with .onetoc2 files. I need a script or cmd command that deletes these files only if the folder that it's contained in does not have a .one file. I need this run for the entire directory.
I have a delete prompt that deletes all the files but I don't know how to get the conditional aspect of it accomplished.
DEL /S /Q c:\Folders \*.onetoc2
something like this could work in powershell
$folder
if (!(dir $folder *.one)) {
dir $folder *.onetoc2 | % {del $_.FullName -WhatIf}
}
for /f "delims=" %A in ('dir /b "c:\folder\*.onetoc2"') do if not exist "%~dpA%~nA.one" echo del "%A"
Use %%A in batch. Remove the echo statement to allow it to delete.

Moving files into sub folders based by average filesize

I need the help of you programming savants in creating a batch script or powershell script that will move and divide a group of files from one directory into 4 subdirectories based on an average total filesize. After the sort, the sub-directories should be roughly equal in terms of folder size.
Why do I need this?
I have 4 computers that I would like to utilize for encoding via FFMPEG and it would be helpful for a script to divide a folder into 4 parts (sub-directories) based on a total average size.
So lets say there are an assortment of movie files with varying different file sizes totaling to 100 GB, the script would divy the movie files and move them into 4 sub folders; each folder having around 25 GB. Doing this will allow the 4 machines to encode the sum of the data equally and efficiently.
After all that encoding I'll have 2 files, XYZ.(original Extension) and XYZ.264, A script that could compare the 2 files and delete the larger file would be extremely helpful and cut down on manual inspection.
Thank you, I hope this is possible.
#ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
PUSHD "%sourcedir%"
:: number of subdirectories
SET /a parts=4
:: make subdirs and initialise totalsizes
FOR /L %%a IN (1,1,%parts%) DO MD "%destdir%\sub%%a" 2>nul&SET /a $%%a=0
:: directory of sourcefiles, sort in reverse-size order
FOR /f "tokens=1*delims=" %%a IN (
'dir /b /a-d /o-s * '
) DO (
REM find smallest subdir by size-transferred-in
SET /a smallest=2000000000
FOR /L %%p IN (1,1,%parts%) DO IF !$%%p! lss !smallest! SET /a smallest=!$%%p!&SET part=%%p
REM transfer the file and count the size
ECHO(MOVE "%%a" "%destdir%\sub!part!"
REM divide by 100 as actual filelength possibly gt 2**31
SET "size=%%~za"
IF "!size:~0,-2!" equ "" (SET /a $!part!+=1) ELSE (SET /a $!part!=!size:~0,-2! + $!part!)
)
popd
GOTO :EOF
I believe the remarks should explain the method. The principle is to record the length-transferred to each subdirectory and select the least-filled as the destination for the file (processed in reverse-size order)
Since batch has a limit of 2^31, I chose to roughly divide the filesize by 100 by lopping of the last 2 digits. For files <100 bytes, I arbitrarily recorded that as 100 bytes.
You would need to change the settings of sourcedir and destdir to suit your circumstances.
The required MOVE commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MOVE to MOVE to actually move the files. Append >nul to suppress report messages (eg. 1 file moved)
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "destdir=U:\destdir"
SET "spaces= "
FOR /f "delims=" %%a IN (
'dir /b /ad "%destdir%\*"'
) DO (
PUSHD "%destdir%\%%a"
FOR /f "delims=" %%f IN (
'dir /b /a-d "*.xyz" 2^>nul'
) DO (
IF EXIST "%%f.264" (
FOR %%k IN ("%%f.264") DO (
SET "sizexyz=%spaces%%%~zf"
SET "size264=%spaces%%%~zk"
IF "!sizexyz:~-15!" gtr "!size264:~-15!" (ECHO(DEL /F /Q "%%f") ELSE (ECHO(DEL /F /Q "%%f.264")
)
)
)
popd
)
GOTO :EOF
This second batch scans the directorynames into %%a then switches teporarily to the detination directory %destfile\%%a.
Once there, we look for .xyz files and for each one found, find the corresponding .xyz.264 file.
If that exists, then we find the sizes of the files (%%~zk or %%~zf) and append that to a long string of spaces. By comparing the last 15 characters of the result as a string, we can determine which is longer.
The required DEL commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(DEL to DEL to actually delete the files.
If the .264 file is filename.264 instead of filename.xyz.264 then replace each "%%f.264" with "%%~nf.264" (the ~n selects the name-part only).
To manually enter a source directoryname, use
SET /p "sourcedir=Source directory "
To accept the source directoryname as a parameter, use
SET "sourcedir=%%~1"
To process all files, except .h264 files, change
FOR /f "delims=" %%f IN (
'dir /b /a-d "*.xyz" 2^>nul'
) DO (
to
FOR /f "delims=" %%f IN (
'dir /b /a-d "*.*" 2^>nul'
) DO if /i "%%~xf" neq ".h264" (
where *.* means "all files" and the extra if statement checks whether the extension to the filename %%f (%%~xf) is not equal to (neq) .h264 and the /i directs "regardless of case (case-Insensitive)"
This might seem like a simple request, but exact partitioning is actually a really hard problem.
The easiest way to approximate a somewhat fair partitioning is simply to sort all files (from biggest to smallest) and then distribute them one-by-one into n groups (a bit like if you were giving out cards for a card game):
# Define number of subgroups/partitions
$n = 4
# Create your destination folders:
$TargetFolders = 1..$n |ForEach-Object {
mkdir "C:\path\to\movies\sub$_"
}
# Find the movie files sort by length, descending
$Files = Get-ChildItem "C:\path\to\movies" -Recurse |Where-Object {'.mp4','.mpg','.xyz' -contains $_.Extension} |Sort-Object Length -Descending
for($i = 0; $i -lt $Files.Count; $i++)
{
# Move files into sub folders, using module $n to "rotate" target folder
Move-Item $Files[$i].FullName -Destination $TargetFolders[$i % $n]
}
If you have multiple file types that you want to include, use a Where-Object filter instead of the Filter parameter with Get-ChildItem:
$Files = Get-ChildItem "C:\path\to\movies" -File -Recurse |Where-Object {'.mp4','.mpg','.xyz' -contains $_.Extension} |Sort-Object Length -Descending
#!/bin/bash
nbr_of_dirs=4
# Go to directory if specified, otherwise execute in current directory
if [ -n "$1" ]; then
cd $1
fi
# Create output directories and store them in an array
for i in $(seq 1 $nbr_of_dirs); do
dir=dir_$i
mkdir $dir
dirs[i]=$dir
done
# For every non-directory, in decreasing size:
# find out the current smallest directory and move the file there
ls -pS | grep -v / | while read line; do
smallest_dir=$(du -S ${dirs[#]} | sort -n | head -1 | cut -f2)
mv "$line" $smallest_dir
done
Remember to keep the script file in a different directory when executing this. The script iterates over every file, so if the script was in the directory too it would be moved to one of the sub-directories.

Flatten Files in Subfolders on Windows 7 (cmd or PowerShell)

I have a large volume of files organized in a very hierarchical folder structure. In this structure, the file that I care about is always located in the lowest level of the folders. As such, I'd like to flatten the directory so that it's easier to access the files that I care about. However, I'd like to preserve the 2 higher levels (Person & Project) of the folder structure.
Here's an example of the EXISTING folder directory:
Directory
Tom
Project 1
Subfolder Level A
FileA
FileB
Project 2
Subfolder Level C
FileC
FileD
Jerry
Project 1
Subfolder Level E
FileE
Here's an example of the DESIRED folder directory:
Directory
Tom
Project 1
FileA
FileB
Project 2
FileC
FileD
Jerry
Project 1
FileE
I have tried doing something like this, however this flattens all of the files into a single directory:
for /r %f in (*) do #copy "%f" .
However, this produces:
Directory
FileA
FileB
FileC
FileD
FileE
I'd appreciate any guidance that you can offer. Thanks a lot!
Here is a Powershell approach. It gets a list of the folders at the level that you want. Then it moves all the sub files up to that level. it will also remove the sub folders.
$Rootfolder = Dir directory\*\* -Directory
ForEach($folder in $Rootfolder)
{
Dir $folder.fullname -Recurse -File | Copy-Item -Destination $folder.fullname
Dir $folder.fullname -Recurse -Directory | Remove-Item -Force -Recurse -WhatIf
}
If you want it to delete, remove the -WhatIf from the last line.
Throw a couple of extra for loops around the one that works then.
e.g. First change to the name folder, then the project folder, looping through both levels.
for /D %n in (*) do (
pushd %n
for /D %p in (*) do (
pushd %p
for /r %f in (*) do #copy "%f" .
popd
)
popd
)
If you put this in a bat file, remember to replace % with %%
#echo off
pushd yourDirectory
for /d %%A in (*) do for /d %%B in ("%%A\*") do for /d %%C in ("%%B\*") do (
pushd "%%C"
for /r %%F in (*) do move /y "%%F" "%%B" >nul
popd
rd /q /s "%%C"
)
popd
%%A contains something like "yourDirectory\Tom"
%%B contains something like "yourDirectory\Tom\Project 1"
%%C contains something like "yourDirectory\Tom\Project 1\subdirectory1"
%%F contains a file to move, to any depth
At first I thought I could eliminate PUSHD/POPD and use
for /r "%%C" %%F in (*) do ... THIS DOES NOT WORK
But that doesn't work - the value after /R cannot use a FOR variable or delayed expansion because of how the command is parsed.
I tweaked #ScottC's answer and used the following code:
for /D %%n in (*) do (
pushd %%n
for /D %%p in (*) do (
pushd %%p
for /r %%f in (*.ppt) do (
move "%%f" "[ROOT_PATH_THAT_I_WANT]\%%n\%%p".
)
popd
)
popd
)
I ran this solution as a .bat file, which is why I used %% instead of %.
%%n = name (aka C:\Directory\Name)
%%p = project (aka C:\Directory\Name\Project)
%%f = file to be moved (recursively drilling through the folders and moving them up to the project level)
Ultimately, I wasn't able to get #dbenham's suggestion of deleting the empty folders to work, so I ended up using this utility: http://www.jonasjohn.de/red.htm. So far it seems pretty intuitive and like it's taking care of the problem without much effort from me :)
Thanks for the help everybody!