Time ago I request some help to develope a short batch file to compress files inside a tree using 7zip, see following link:
Compress separately files within subfolders
Now I want to avoid already zipped files (.7z, .zip files) and jump to the next item.
Also I want to remove previous compressed files (like '.7z.7z' or '.zip.7z') created by using the original batch file several times on a same folder.
This is what I got so far:
#echo off
cd /d %~dp0
rem 7z.exe path
set sevenzip=
if "%sevenzip%"=="" if exist "%ProgramFiles(x86)%\7-zip\7z.exe" set sevenzip=%ProgramFiles(x86)%\7-zip\7z.exe
if "%sevenzip%"=="" if exist "%ProgramFiles%\7-zip\7z.exe" set sevenzip=%ProgramFiles%\7-zip\7z.exe
if "%sevenzip%"=="" echo 7-zip not found&pause&exit
#echo searching...
for /R %%I in (*) do (
if not exist %%I.zip if not exist %%I.7zip "%sevenzip%" a -mx -mmt4 "%%I.7z" -r -x!*.bat "%%I" && "%sevenzip%" t "%%I.7z" * -r
)
del "Compressing_files_7zip.bat.7z"
del *.7z.7z*
del *.zip.7z*
::::::::::::::::::::::::::For setting up shutdown 60' after the end of the process.Remove colons in the line below.
::shutdown.exe /s /t 3600
pause
The main issues I get with this code are:
-I am unable to detect already compressed files and therefore, jump to the next %%I entity.
-Remove non-desirable over-zipped files from sub-folders. Obviously, '*' wildcard does not work inside the loop; on the other hand, if I look for 'del %%I.7z.7z' it does not work either.
I read I cannot use a proper 'OR' fuction for the 'if' loops, that is why I nested them.
Thank you in advance.
Alex.
for /R %%I in (*) do (
if not exist %%I.zip if not exist %%I.7zip "%sevenzip%" a -mx -mmt4 "%%I.7z" -r -x!*.bat "%%I" && "%sevenzip%" t "%%I.7z" * -r
)
Note that %%I will contain the complete filename, so if you locate whatever.zip then the code looks for whatever.zip**.zip** or whatever.zip**.7zip** and will execute the 7zip if both of these are absent and generate whatever.zip**.7z**
so if you locate whichever.txt then the code looks for whichever.txt**.zip** or whichever.txt**.7zip** and will execute the 7zip if both of these are absent and generate whichever.txt**.7z**
I'd suggest
for /R %%I in (*) do (
if not exist %%~nI.zip if not exist %%~nI.7z "%sevenzip%" a -mx -mmt4 "%%~nI.7z" -r -x!*.bat "%%I" && "%sevenzip%" t "%%~nI.7z" * -r
)
which should select the name part only of the filename. (see for /?|more from the prompt for docco)
Hence, the code would look for whatever.zip or whatever.7z - finds whatever.zip and doesn't execute 7zip
or, for whichever.txt, the code would look for whichever.zip or whichever.7z - finds neither and executes 7zip generating whichever.7z
Now, since whichever.7z exists, further runs will skip the 7zip.
Which raises the question - if the zip/7z exists, why not try to freshen the archive, since the detected file may have changed?
Related
Based on this stackhowto, https://stackhowto.com/batch-file-to-list-folder-names/ ,
I tried to write a batch script that goes through all subfolders of a folder and renames .txt files based on the original folder name. However, I can't seem to actually store the name of the original folder and pass it along to rename the files, even though I can print them out directly just fine using echo %%~nxD.
My dummy folder structure looks like this:
Folder
subfolder
subsubfolder
test.txt
Where my batch script sits in the Folder.
The script I tried to use is pasted below.
It is supposed to run from within the Folder, go into each subfolder, save that subfolders name, then go into each subfolder within that subfolder, and rename any text files that contain the pattern by adding the subfoldername before the pattern in the filename.
However, the subfolder name is not properly saved, instead, what is returned from the echo %replace% is an empty string, and that is what the test.txt file will be renamed to: ".txt".
If I just type
echo %%~nxD
the folder name gets printed out correctly as expected, so it's the saving that isn't working
If I just add
set "replace=thisworksfine_%pattern%"
right at the beginning of this script after set "pattern=test",
then the file will be renamed into "thisworksfine_test.txt" as expected, so normal saving of a parameters works fine.
So clearly I am not understanding how one can save a variable in such a manner using these loops through folders.
Any help would be greatly appreciated!
setlocal enabledelayedexpansion
#echo off
set "pattern=test"
for /d %%D in (*) do (
cd %%~nxD
set "replace=%%~nxD_%pattern%"
echo %replace%
for /d %%D in (*) do (
cd %%~nxD
for %%I in (*.txt) do (
set "file=%%~I"
ren "%%I" "!file:%pattern%=%replace%!"
)
)
cd ..
)
cd ..
Thank you everyone, with those comments I got it to work, here is the (probably dirty by your standards) solution:
Basically use !! instead of %% to avoid delayed expansion, while also redefining the replace string at each level, and then using rename in the end instead of ren because that didn't seem to work with !replace3! inside of the argument.
setlocal enabledelayedexpansion
#echo off
set "pattern=test.txt"
for /d %%D in (*) do (
cd %%~nxD
set "replace=%%~nxD_%pattern%"
for /d %%D in (*) do (
set "replace2=!replace!"
cd %%~nxD
for %%I in (*.txt) do (
set "file=%%~I"
set "replace3=!replace2!"
echo !replace3!
rename %pattern% !replace3!
)
)
cd ..
)
cd ..
I am trying to run a batch file against a list of remote computers.
The batch file will check for a specific file, if the file is not found in the directory it should be copied there.
I am using the %%a variable for the PC names.
The xcopy command fails in the batch file with invalid drive specification.
If I take the xcopy command and replace the variable with the computer name and run from a command prompt it will copy the file over.
I am not sure why the xcopy command fails in the batch file when using %%a
PC.txt is my text file with the list of computers.
I am using the hostname and not the FQDN in the text file
for /f %%a in (PCList.txt) do (
If Exist "\\%%a\c$\Program Files\Folder\App.exe" (
ECHO %%a App.exe exists
) ELSE (
ECHO %%a App.exe Does Not Exist
xcopy "C:\Folder\App.exe" "\\%%a\c$\Program Files\SubFolder\" /Q /R /Y
First of all, it looks like you have one or two syntax errors. You opened a parenthesis in ... Folder\App.exe" (and you haven't closed it since, the same thing with the ) ELSE (. Also, sometimes the parenthesis inside a FOR can cause interference with the rest of the code. If you add the missing parenthesis does not resolve, try to fix the code to: Folder\App.exe" ^( which can help.
Try this:
for /f %%a in (PCList.txt) do (
If Exist "\\%%a\c$\Program Files\Folder\App.exe" ECHO %%a App.exe exists
) ELSE (
ECHO %%a App.exe Does Not Exist
xcopy "C:\Folder\App.exe" "\\%%a\c$\Program Files\SubFolder\" /Q /R /Y )
Also, you can use the COPY only, maybe the problem is with the COPY. And using the parameter if exist to a entire .exe isn't the best idea, the user can just create a txt file and rename it as App.exe.
Hope this helps,
K.
First of all, I am a total beginner. I was trying an ultimate script bat file solution for a game. To not annoy you with details, let me tell you what I tried to do.
Example:
I have 17 files. 10 of them are .jpg and 7 of them are .obj files.
The images are in path \mods\images and the objects are in path \mods\models.
I want to list all 17 missing files in a list.txt
The bat file will read that list, search for the files and paste them into the TTSmissing folder
and here are my problems:
The bat script only looks exactly into the source path, but not into subfolders (that's why I wrote \mods\images\, to test if it works) so
what I basically want is: \Tabletop Simulator\Mods\ as source path and
the script shall look into all subfolders, too.
The list.txt only works, when the filenames also have their extensions. is it possible to change the script so i don't need the extension? so it will only look for names and copy the files? (example: the names in the list have to be like: hello123.jpg. It's not working when its only hello123.)
How do I need to change the bat script if i don't want a list.txt but just put the list of files right into the bat file?
#echo off
mkdir %USERPROFILE%\Desktop\TTSmissing
set src_folder=%USERPROFILE%\Documents\My Games\Tabletop Simulator\Mods\Images
set dst_folder=%USERPROFILE%\Desktop\TTSmissing
set file_list=%USERPROFILE%\Desktop\list.txt
for /f "tokens=*" %%i in (%file_list%) DO (
xcopy /S/E "%src_folder%\%%i" "%dst_folder%"
)
pause
#echo off
setlocal
set "src_folder=%USERPROFILE%\Documents\My Games\Tabletop Simulator\Mods"
set "dst_folder=%USERPROFILE%\Desktop\TTSmissing"
set "file_list=%USERPROFILE%\Desktop\list.txt"
set "ext_list=.gif .jpeg .jpg .mp4 .obj .pdf .png .webm"
if not exist "%dst_folder%" md "%dst_folder%"
for /d /r "%src_folder%\" %%A in (*) do (
pushd "%%~A" && (
for /f "usebackq delims=" %%B in ("%file_list%") do (
for %%C in (%ext_list%) do (
if exist "%%~B%%~C" (
echo copy /y "%%~B%%~C" "%dst_folder%\"
)
)
)
popd
)
)
You just want to copy files so copy is easier to use than xcopy. The code will echo the copy command to test whether it is working how you want it. If satisfied, remove the echo in front of copy and run the code again to do the actual copy process.
A for /d /r loop will recursively iterate the subdirectories in %src_folder%. pushd will change the current directory to each subdirectory so as can work relative to the source files.
The for /f loop will iterate each line from %file_list%. The simple for loop will iterate each of %ext_list%. If current "name.extension" exists, it will be copied to %dst_folder%.
If you set variables names in a script, it is usually a good idea to use setlocal to keep the variables defined local to the script.
To view help for a command, use command /?. This will work for many of commands used in the code.
View command /? help for copy, for, if, setlocal ...
I have an application which saves files to a FTP folder that I sync to my PC which contains multiple JPG and MP4 files, named in the following format:
ARC20170510151547.jpg
ARC20170510151549.mp4
What I'm trying to do is:
Copy the files from my FTP to my PC
Sort the files into folders based on the day they were created
Delete files from the FTP older than 14 days
Delete files from the PC older than 1 month
Using WinSCP to connect to FTP with the following code, I am able to download all the files to my local drive:
"c:\program files (x86)\winscp\winscp.com" /ini=nul /command ^
"open ftp://[username]:[password]#[ipaddress]/" ^
"synchronize local f:\[localpath]\ /[remotepath]/ " ^
"exit"
Then I need to sort the files. Here is where I am stuck. I'm think I know the commands but I am unsure how to use the 'tokens' and 'delims' to get it to work how I want.
#echo
for %%a in (*.*) do (
echo processing "%%a"
for /f "tokens=1 delims=" %%a in ("%%~nxa") do (
md "%%b-%%c" 2>nul
move "%%a" "%%b-%%c" >nul
)
)
pause
As I know the filename format isn't going to change, one thing I have considered is adding some special characters to the filename, maybe using the 'ren' command. I could then use those special characters as search delims but, again, I'm struggling how to best proceed.
Removing local files older than 30 days is easy using the following script
forfiles -p "f:\[localpath]" -s -m *.* -d <number of days> -c "cmd /c del #path"
However, the WinSCP 'RM' command I am using doesn't appear to be working. It returns an error "no file matching '*<14D' found"
"rm /[filepath]/*<14D" ^
Any help, advice and guidance would be very gratefully received!
Since there is no delimiter between the date elements you need substrings,
substrings do only work with normal variables and in a (code block) you need delayed expansion
It's unclear if you want folders with year-month or month-day, select the proper part in the batch and comment/uncomment the Rem:
With extensions enabled md can create the structure YY-MM\DDin one step.
So you can move directly to that folder.
#Echo off&SetLocal EnableExtensions EnableDelayedExpansion
for %%a in (ARC*.*) do (
echo processing "%%a"
Set File=%%~nA
Set YY=!File:~3,4!
Set MM=!File:~7,2!
Set DD=!File:~9,2!
Rem YY-MM\DD
md "!YY!-!MM!\!DD!" 2>nul
move "%%a" "!YY!-!MM!\!DD!" >nul
)
pause
#LotPings! Thank you for the script. This does almost what I want it to do. I have modified the script as below which moves the files into folders based on the Day.
So, each day, maybe 100 - 200 files are generated. So I do not mind having a folder for each day. Once the files are moved into their respective "Day" folder, what I'd then like to do is create a SubFolder "!YY!-!MM!" and then move the "!DD!" Folders into the "!YY!-!MM!" folder.
#Echo off&SetLocal EnableExtensions EnableDelayedExpansion
for %%a in (ARC*.*) do (
echo processing "%%a"
Set File=%%~a
Set YY=!File:~3,4!
Set MM=!File:~7,2!
Set DD=!File:~9,2!
md "!DD!" 2>nul
md "!YY!-!MM!" 2>nul
move "%%a" "!DD!" >nul
)
pause
Consider this hierarchy:
MainFolder\Sub_Folder1\Original_Files\
\Converted_Files\
\Sub_Folder2\Original_Files\
\Converted_Files\
Now in each ...\Original_Files\ I've a bunch of video files which I'll encode and save to the respective ...\Converted_Files\.
I could do it for one subfolder with this batch code:
#echo off
set "sourcedir=G:\Animation\Anime\OnePiece\Episodes\Main"
set "outputdir=G:\Animation\Anime\OnePiece\Episodes\Converted"
PUSHD "%sourcedir%"
for %%F in (*.mkv) DO ffmpeg -i "%%F" -s 640x480 -map 0 -c:v libx265 "%outputdir%\%%F"
POPD
I've generated a text file with the folder paths of all the subfolders which contains:
G:\Animation\ToConvert\Berserk_1997_The_Complete_Series
G:\Animation\ToConvert\Blue_Exorcist
G:\Animation\ToConvert\Elfen_Lied
Every folder listed in the file has Main and Converted folders within them. I've to loop through all files in Main and save into Converted as you might see in above.
This is something I came up with :
#echo off
for /F "tokens=*" %%A in (f.txt) DO (
set "sourcedir=%A%\Main"
set "outputdir=%A%\Converted"
PUSHD "%sourcedir%"
for %%F in (*.mkv) DO ffmpeg -i "%%F" -s 640x480 -map 0 -c:v libx265 "%outputdir%\%%F"
POPD
) %%A
Running for /F "tokens=*" %A in (f.txt) DO #echo %A gives me the names of the subfolders.
I thought somehow if I could pass the name to some variable and concatenate \Main and \Converted to it, it might work.
But on running the code above from within a command prompt window, it's just switching the current directory from the folder I'm running the batch file to C:\Windows.
How can I run nested loops, one for the subfolders and then chose between working in Main and saving in Converted and the next loop for files in Main?
Your last batch code fails because of
referencing the loop variable A like an environment variable with %A% instead of %%A and
referencing environment variables defined/set within a command block defined with ( and ) requires the usage of delayed expansion enabled before with the command line setlocal EnableDelayedExpansion and using !sourcedir! and !outputdir! instead of %sourcedir% and %outputdir% which are already replaced by current value of the environment variables sourcedir and outputdir (empty string here as not defined before) when Windows command processor parses the entire command block before executing command FOR the first time.
%%A after closing parenthesis at end is unknown for Windows command interpreter and results therefore in an exit of batch processing because of a syntax error.
However, better than your code which requires first the creation of a text file with the folder paths would be the usage of following code:
#echo off
for /D %%D in ("G:\Animation\ToConvert\*") do (
if exist "%%D\Main\*.mkv" (
echo Processing %%D ...
if not exist "%%D\Converted\*" md "%%D\Converted"
for %%I in ("%%D\Main\*.mkv") do (
ffmpeg.exe -i "%%I" -s 640x480 -map 0 -c:v libx265 "%%D\Converted\%%~nxI"
)
)
)
The outer FOR with parameter /D finds just non hidden subfolders within folder G:\Animation\ToConvert and holds in loop variable D the name of the subfolder with full path not ending with a backslash.
The IF condition checks if in the current subfolder there is a folder Main with 1 or more *.mkv files to process. If this condition is true,
an information message is output to see progress on running the batch file,
in current subfolder the folder Converted is created if not already existing,
another FOR loop is executed to process each *.mkv file found in the folder Main of current subfolder.
The loop variable I holds the name of the current *.mkv file with full path. So "%%I" can be used for the input file as current directory does not matter because input file name is with full path.
For the output file the folder Converted in current subfolder is specified and appended is with %%~nxI the file name and the file extension of input file as name for the output file.
This batch code does not require delayed expansion as there is no environment variable used, only the loop variables D and I.
For completeness also your code using a text file containing line by line the folders to process with removing all unnecessary environment variables to make it possible to run the batch file without using the commands setlocal and endlocal.
#echo off
for /F "usebackq tokens=*" %%A in ("f.txt") do (
if exist "%%A\Main\*.mkv" (
echo Processing %%A ...
if not exist "%%A\Converted\*" md "%%A\Converted"
for %%I in ("%%A\Main\*.mkv") do (
ffmpeg.exe -i "%%I" -s 640x480 -map 0 -c:v libx265 "%%A\Converted\%%~nxI"
)
)
)
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.
echo /?
for /?
if /?
md /?
BTW: See this answer and the other answers linked there if you ever want to understand what delayed expansion is and what the commands setlocal and endlocal do not needed here.
#Mofi wrote a great answer, both his codes work flawlessly. This is just a simpler version I am running because the conditions being checked in that program are already met.
#echo off
for /F "tokens=*" %%A in (f.txt) DO (
for %%F in (%%A\Main\*.mkv) DO ffmpeg -i "%%F" -s 640x480 -map 0 -c:v libx265 "%%A\Converted\%%~nxF"
)