Replace Special Characters In Batch-File Variable Feeding ffmpeg program - batch-file

I am attempting to write a batch-file that leverages ffmpeg.exe to convert all files in a folder structure to mp3 format (specifically 128 KBps).
My batch-file is presently unable to process filenames (constructed by concatenating the %_SOURCE% and %%~F variables) containing certain special characters generating the following errors:
No such file or directory
… ellipsis sign
– en dash
— em dash
− minus sign
Invalid argument
‘ and ’ curved single quotation marks
“ and ” curved double quotation marks
Invalid argument (yet sometimes passes depending on where symbol is in the filename, for example, seems to work if placed between the n and t of Dont in C:\Users\Test\Documents\Input\Peter Bjorn And John - I Know You Dont Love Me.mp3)
- hyphen
! exclamation mark
~ tilde
' non-curved single quotation mark
= equals sign
+ plus sign
% percentage sign
( open bracket
How can I modify my batch-file script so that the %%~F variable escapes these characters correctly?
Example current filename input: C:\Users\Test\Documents\Input\Peter Bjorn And John - I Know You Don't Love Me.mp3
Example desired filename input: C:\Users\Test\Documents\Input\Peter Bjorn And John - I Know You Don"^'"t Love Me.mp3
Script (see line beginning C:\ffmpeg\bin\ffmpeg.exe):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=C:\Users\Test\Documents\Input" & rem // (absolute source path)
set "_TARGET=C:\Users\Test\Documents\Output" & rem // (absolute target path)
set "_PATTERN=*.*" & rem // (pure file pattern for input files)
set "_FILEEXT=.mp3" & rem // (pure file extension of output files)
pushd "%_TARGET%" || exit /B 1
for /F "delims=" %%F in ('
cd /D "%_SOURCE%" ^&^& ^(rem/ list but do not copy: ^
^& xcopy /L /S /Y /I ".\%_PATTERN%" "%_TARGET%" ^
^| find ".\" ^& rem/ remove summary line;
^)
') do (
2> nul mkdir "%%~dpF."
rem // Set up the correct `ffmpeg` command line here:
set "FFREPORT=file=C\:\\Users\\Test\\Documents\\Output\\ffreport-%%~F.log:level=32"
"C:\ffmpeg\bin\ffmpeg.exe" -report -n -i "%_SOURCE%\%%~F" -vn -c:a libmp3lame -b:a 128k "%%~dpnF%_FILEEXT%"
if not errorlevel 1 if exist "%%~dpnF%_FILEEXT%" del /f /q "%_SOURCE%\%%~F"
)
popd
endlocal
pause

#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants:
set "_SOURCE=C:\Users\Test\Documents\Input" & rem // (absolute source path)
set "_TARGET=C:\Users\Test\Documents\Output" & rem // (absolute target path)
set "_PATTERN=*.*" & rem // (pure file pattern for input files)
set "_FILEEXT=.mp3" & rem // (pure file extension of output files)
pushd "%_TARGET%" || exit /B 1
for /r "%_SOURCE%" %%F in ("%_PATTERN%") do (
set "_fullpath=%%~F"
set "_filename=%%~nF"
call :ffmpeg
)
popd
endlocal
pause
exit /b
:ffmpeg
setlocal EnableDelayedExpansion
rem // Get target path and log path:
for %%A in ("!_fullpath:%_SOURCE%=.!") do (
set "_logpath=%%~dpAffreport-!_filename!.log"
set "_targetpath=%%~dpA"
)
for %%A in (_logpath _targetpath) do if not defined %%A (
echo ERROR: %%A not defined.
exit /b 1
)
rem // Escape logpath:
set "_logpath=!_logpath:\=\\!"
set "_logpath=!_logpath::=\:!"
set "_logpath=!_logpath:'=\'!"
set "_logpath=!_logpath:%%=%%%%!"
rem // Set FFREPORT:
set "FFREPORT=file=!_logpath!:level=32"
rem // Make dir for the target file:
if not exist "!_targetpath!" md "!_targetpath!"
rem // Run ffmpeg command:
"C:\ffmpeg\bin\ffmpeg.exe" -hide_banner -report -n -i "!_fullpath!" -vn -c:a libmp3lame -b:a 128k "!_targetpath!\!_filename!%_FILEEXT%"
if not errorlevel 1 if exist "!_targetpath!\!_filename!%_FILEEXT%" del /f /q "!_fullpath!"
exit /b 0
Use of for /f with a command tends to alter filenames if
contains Extended ASCII such as the ellipsis.
Using a for /r to iterate the files gives better results
and have adjusted the code to handle the change.
Added the label :ffmpeg to make it easier for working with
the delayed expansion variables etc.
Added _logpath variable to store the log filepath with any
backslash, colon or single quote to be escaped with a backslash.
Percent signs are escaped by doubling up.
Added _targetpath variable to store the path to where ffmpeg
is to output the file. Used by md command to make a directory
structure like that of the input.
This may not solve all issues. Tried to do without delayed
expansion though always had some failures depending on special
characters i.e. %.

Related

Batch file to copy file from specific sub folder inside a folder to destination folder

I have a folder structure like his
folder1-------|
|123456------123.txt
abc.txt
|234567------fgt.txt
234.txt
|abc---------ytr.txt
1234.txt
I need to copy files from Main folders sub directories if the sub folder has a length of 6(only numbers) . So .txt files from 123456,234567 will be copied and abc will not be copied.
I have tried using wildcards but not succeed yet. Thanks in advance.
>xcopy /s "C:\Users\xxx.xxx\Desktop\folder1\*\*.txt" "C:\Users\xxx.xxx\Documents\New folder" /l /D:09-09-2019
Ok, so here is an example:
#echo off
setlocal enabledelayedexpansion
for /f %%i in ('dir "C:\Users\xxx.xxx\Desktop\folder1\" /b /ad') do (
set "str=#%%i"
set "len=0"
for %%a in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!str:~%%a,1!" neq "" (
set /a "len+=%%a"
set "str=!str:~%%a!"
if !len! equ 6 if 1%%i equ +1%%i echo %%i is !len! characters
)
)
)
This will do a dir of the directories inside the path you give it. It will then test the length of the string, if the length is 6 characters, it will then test if the string is numeric, if true, it will echo the string (folder name) and the length of it. You should then test it as is and if it gives you the required output, you can replace echo portion with your copy string.
So your final solution will be something like:
#echo off
setlocal enabledelayedexpansion
set /p "mydate=Enter Date (format dd-mm-yyyy): "
for /f %%i in ('dir "C:\Users\xxx.xxx\Desktop\folder1\" /b /ad') do (
set "str=#%%i"
set "len=0"
for %%a in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!str:~%%a,1!" neq "" (
set /a "len+=%%a"
set "str=!str:~%%a!"
if !len! equ 6 if 1%%i equ +1%%i xcopy "C:\Users\xxx.xxx\Desktop\folder1\%%i\*.txt" "C:\Users\xxx.xxx\Documents\New folder" /D:!mydate!
)
)
)
You could use FindStr to isolate the directories matching your pattern:
batch-file:
#Echo Off
Set "SrcDir=%UserProfile%\Desktop\folder1"
Set "DstDir=%UserProfile%\Desktop\New folder"
For /F Delims^=^ EOL^= %%A In ('
"Dir /B/AD "%SrcDir%\*" 2>NUL|FindStr "^[0-9][0-9][0-9][0-9][0-9][0-9]$""
')Do XCopy "%SrcDir%\%%A\*.txt" "%DstDir%\" /D:09-09-2019 /Y>NUL 2>&1
Just modify the paths between the = and " on lines 2 and 3 to suit your actual source and destination directories.
Here is yet another method to achieve what you want (see all the explanatory rem remarks):
#echo off
rem // Use a `for /D` loop to iterate through all immediate sub-directories in the given root directory:
for /D %%I in ("%UserProfile%\Desktop\folder1\*") do (
rem // Store path and name of currently iterated directory in variables:
set "DIRECTORY=%%~I" & set "NAME=%%~nxI"
rem // Toggle delayed expansion to be able to write and to read a variable within the same block of code:
setlocal EnableDelayedExpansion
rem // Check if directory name holds more than 5 characters:
if not "!NAME:~5!" == "" (
rem // Check if directory name holds not more than 6 characters:
if "!NAME:~0,6!" == "!NAME!" (
rem // The directory name is exactly 6 characters long.
rem // Check whether the directory name consists of decimal figures by (mis-)using a `for /F` loop:
set "FLAG=" & for /F "tokens=1 delims=0123456789 eol=0" %%J in ("!NAME!") do set "FLAG=#"
rem // The flag variable has not been set, hence the directory name consists of decimal figures:
if not defined FLAG (
rem // Do the actual copy job at this point:
xcopy /I /Y /D:09-09-2019 "!DIRECTORY!\*.txt" "!UserProfile!\Documents\New Folder"
)
)
)
endlocal
)
You were trying to use wildcards not only in the last element of a path, which cannot be done (in Windows), but you can use a for /D loop to resolve the wildcards in upper directory levels, as demonstrated in the script.
The script uses ~-modifiers (which are the same for for meta-variables like %%I and for argument references like %1) to get the pure name of the currently iterated directory: %%~nxI.
To determine whether or not the directory name is exactly six (6) characters long, sub-string expansion is applied.
To determine whether or not the directory name consists of decimal digits only, a for /F loop is (mis-)used: for /F splits strings into parts at given delimiter characters (option delims); since all decimal digits are defined as delimiters, a directory named 1a2b3c would be split into the parts a, b and c (a becomes assigned to %%J then due to tokens=1, the other parts become dismissed), but a directory name 123456 leaves nothing behind since this is a delimiter-only string, in which case the for /F loop does not iterate at all. Together with a flag-like variable FLAG that becomes set in the body of the for /F loop we are able to determine whether or not the loop iterated, thus knowing whether the current directory name only consists of decimal digits.
The script writes to and reads from the variable NAME in the same block of code, which can normally not be done, unless you enable delayed variable expansion.
You can use the system environment variable %UserProfile% instead of specifying C:\Users\xxx.xxx, so the script may even work on a system where the user profile directory is not at the default location.

Replace the string from output

I have file C:/test.txt which is having content as below.
05/13/2017 07:29:34 Value= \\america.com\efpf_share\efpf\ipm_files
05/13/2017 07:29:41 Value= \\america.com\efpf_share\efpf\ipm_files
05/17/2017 08:31:54 Value= \\america.com\efpf_share\efpf\ipm_files
05/17/2017 08:32:03 Value= \\america.com\efpf_share\efpf\ipm_files
I want to extract 'epfp' or any string comes at this place and convert this into upaercase also if its have test attached (as epfptest) then it it should split EPFP-TEST. For extracting I am running the below code and redirecting the output in temp1.txt file
findstr "Value=" C:\test.txt| findstr america > "C:\temp.txt" && for /l %l in (1,1,1) do #for /f "tokens=3* delims=." %a in ('findstr /n /r "^" "C:\temp.txt" ^| findstr /r "^%l:"') do #echo %b > c:\temp1.txt
now temp1.txt file having the content as below :
com\efpf_share\efpf\ipm_files
Now finally I am exracting efpf from below code it gives me the output as below :
for /f "tokens=3 delims=\" %a in (c:\temp1.txt) do #echo %a
epfp
I want this output or to be converted as EPFP (in uppercare) and if this output does not having test string attached then it should only split as EPFP-TEST
Note: Final output can be anything (in this case epfp) and I want this convert in uppercase also if this output containing 'test' string attached then that should be split in "STRING-TEST"
This test file modification task should be definitely not done using a batch file and pure Windows command processor commands. There are much better scripting languages for this task.
It would be also much more useful to do this file content modification with a powerful text editor like UltraEdit or any other text editor with Perl regular expression support. Searching for (\\[^\\]+\\)(?=ipm_files) and using as replace string \U$1\E changes the directory name left to ipm_files to upper case and searching for (?<!\\|-)TEST(?=\\ipm_files) and using as replace string -TEST inserts the hyphen character left to TEST if there is not already a hyphen and the entire folder name is not TEST.
However, here is a commented batch file solution for this task:
#echo off
if not exist "%~dp0Test.txt" goto :EOF
setlocal EnableExtensions DisableDelayedExpansion
set "Modified=0"
set "DataFile=%~dp0Test.txt"
set "TempFile=%TEMP%\%~n0.tmp"
del "%TempFile%" 2>nul
for /F delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /N "^" "%DataFile%" 2^>nul') do (
set "Line=%%I"
call :ProcessLine
)
if %Modified% == 1 move /Y "%TempFile%" "%DataFile%"
del "%TempFile%" 2>nul
endlocal
goto :EOF
rem The subroutine ProcessLine removes first line number and colon inserted
rem by FINDSTR at beginning of each line to process correct also empty lines
rem in data file. The subroutine jumps to output of line in case of current
rem line is an empty line.
rem Next the line is split up into substrings using backslash as delimiter.
rem Of interest are only the fourth and fifth substrings. The fifth substring
rem should be ipm_files to identify the current line as a line to process.
rem A jump to writing the line into temporary file is done if this condition
rem is not true. Otherwise the fourth substring is assigned to a variable
rem because that string is the folder name to modify by this batch file.
rem Each ASCII character in the folder name is replaced by its upper case character.
rem If the entire new folder name is TEST, just do the replace and don't change
rem the folder name to -TEST. If the new folder name ends already with -TEST,
rem just do the replace. But if new folder name ends with only TEST, replace
rem just TEST by -TEST with hyphen.
rem A case-sensitive comparison of current and new folder name is done before
rem running the folder replace on line to determine if the replace is really
rem necessary at all. The modification information is saved in an environment
rem variable which is passed over local environment of subroutine to main code
rem above. This information is used finally to determine if the data file must
rem be replaced at all by the temporary file because of a modification is made
rem or the temporary file can be simply deleted as being equal with data file.
:ProcessLine
setlocal EnableDelayedExpansion
set "Line=!Line:*:=!"
if not defined Line goto WriteLine
for /F "tokens=4,5 delims=\" %%A in ("!Line!") do (
if /I not "%%B" == "ipm_files" goto WriteLine
set "CurFolderName=%%A"
)
set "NewFolderName=%CurFolderName%"
for %%C in (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) do set "NewFolderName=!NewFolderName:%%C=%%C!"
if "%NewFolderName%" == "TEST" goto DoReplace
if "%NewFolderName:~-5%" == "-TEST" goto DoReplace
if "%NewFolderName:~-4%" == "TEST" set "NewFolderName=%NewFolderName:~0,-4%-TEST"
:DoReplace
if "%CurFolderName%" == "%NewFolderName%" goto WriteLine
set "Modified=1"
set "Line=!Line:%CurFolderName%\ipm_files=%NewFolderName%\ipm_files!"
:WriteLine
echo(!Line!>>"%TempFile%"
endlocal & set "Modified=%Modified%"
goto :EOF
%~dp0Test.txt must be two times replaced by real file name of data file with relative or absolute path.
The purpose of first FOR loop in main code at top is described in my answer on:
How to read and print contents of text file line by line?
The other command lines are explained by the remarks between main code and subroutine.
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 /?
del /?
echo /?
endlocal /?
findstr /?
goto /?
if /?
move /?
rem /?
setlocal /?

Renaming files in numerical series

So I am using an application that creates a series of .tif files with very annoying filenames that do not sort correctly in alphanumerical order. I have no way to change the behavior of this commercial software, so I must use my own creativity to rename the files so they may be processed externally in the correct order. For example, the files created look like this:
image.tif, image(2).tif, image(3).tif, ..., image(9).tif, image(10).tif, ..., image(n).tif
Where n is an integer not less than 2. The first file has no number in parenthesis. Sorted alphanumerically, for example, the list would look like:
image.tif, image(10).tif, image(100).tif, image(1000), image(2).tif, ...
Obviously while this is the correct alphanumerical order, it's the incorrect numerical order. So with the aid of some batch commands, I created this:
#echo off & setlocal EnableDelayedExpansion
ren "image.tif" "a.tif"
ren "image(1).tif" "b.tif"
ren "image(2).tif" "c.tif"
ren "image(3).tif" "d.tif"
ren "image(4).tif" "e.tif"
ren "image(5).tif" "f.tif"
ren "image(6).tif" "g.tif"
ren "image(7).tif" "h.tif"
ren "image(8).tif" "i.tif"
ren "image(9).tif" "j.tif"
set a=1
for /f "delims=" %%i in ('dir /b *.tif') do (
ren "%%i" "!a!.tif"
set /a a+=1
)
This at least corrects the names of the first ten files, as I often process less than 100 files. The for loop is a snippet I found here on stackoverflow, it renames the alphanumeric files into integers starting with 1 up to the number of files.
Problem I run into is that this loop still creates 1.tif, 10.tif, and 100.tif that would come before 2.tif.
I've seen the use of %02d to format numbers into 01, 02, 10, etc. Does anyone know how to syntactically include that in the loop shown above?
Or if anyone can figure a more elegant method to rename these files into a suitable alphanumeric order, it would be greatly appreciated. I have the feeling my solution is very kludgey.
#echo off
setlocal EnableDelayedExpansion
rem Initialize "i" variable with the desired number of digits as zeros
set i=1000
rem Process the files in creation date-time order
for /F "delims=" %%a in ('dir /A-D /OD /B *.tif') do (
rem Increment the number: "1001"...
set /A i+=1
rem Omit first digit in number: "1", use the rest: "001"...
ren "%%a" "image(!i:~1!).tif"
)
#echo off
setlocal EnableDelayedExpansion
set /a max=99&set /a numlen=2
if exist "image(100).tif" set /a max=999&set /a numlen=3
for /L %%a in (1,1,%max%) do if exist "image(%%a).tif" set /a newnum=1000+%%a&if not exist "image(!newnum:~-%numlen%!).tif" echo(ren "image(%%a).tif" "image(!newnum:~-%numlen%!).tif"
set /a max+=1
echo(ren "image.tif" "image(!newnum:~-%numlen%!).tif"
[untested]
If "image(100).tif" exists, then we need to rename to (001)..(999). If not, to (01)..(99), so then number-length will be 3 or 2.
for each of the names 1..max, if the file exists, calculate 1000+the file number;take the last 3 or 2 characters and rename. Only attempt to rename if the destination name does not exist, so (6) will be renamed to (006) or (06), (15) to (05) or skipped as (15)=(15) and (123) will also be skipped as (123)=(123)
Finally, rename the image.tif file to (000) or (00)
Note that this will simply echo the required commands to allow verification. Change the echo(ren to ren when verified to actually perform the rename.
The %02d convention for padding numbers is a printf thing, not really applicable to the Windows cmd environment or batch scripting. PowerShell has something similar, but not pure batch. However, you can zero-pad numerals pretty easily using variable substring manipulation.
set /a a=0
for /f "delims=" %%I in ('dir /b *.tif') do (
set "idx=00!a!"
ren "%%~I" "!idx:~-3!%%~xI"
set /a a += 1
)
If you'd prefer to keep the naming convention of image(idx).tif while ensuring the TIF files remain in their intended sequential order, a lazy solution might be to use prename.bat and rename with regexps.
prename /v "s/\((\d)\)/(00\1)/" *.tif
prename /v "s/\((\d\d)\)/(0\1)/" *.tif
In cmd there are no format strings like %02d. However, you can use dir /B to retrieve a plain list of files, findstr to filter for those having a parenthesised numeric suffix in their names or no such suffix at all, for /F to capture the resulting file names and split them into tokens relying on (, ), and sub-string expansion to do the actual left-zero-padding, like in the following example:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_LOCATION=." & rem // (directory containing the files to rename)
set "_PATTERN=*.tif" & rem // (search pattern for the files to rename)
set /A "_DIGITS=4" & rem // (number of proposed digits from 1 to 10)
set "_REGEX1=^[^()][^()]*([0-9][0-9]*)\.tif$" & rem // (filter expression)
set "_REGEX2=^[^()][^()]*\.tif$" & rem // (filter expression)
rem // (the filter expressions ensure wrongly named files not to be processed)
rem // Build zero-padding string:
set "PAD=0000000000" & call set "PAD=%%PAD:~-%_DIGITS%%%"
rem // Loop through all matching files:
for /F "tokens=1-3 delims=()" %%A in ('
dir /B /O:N "%_PATTERN%" ^| findstr /I /R /C:"%_REGEX1%" /C:"%_REGEX2%"
') do (
rem // Check if current file name contains a parenthesised numeric suffix:
if not "%%B"=="" (
rem // Numeric suffix found, so store file name parts and pad number:
set "LEFT=%%A" & set "MID=%PAD%%%B" & set "RIGHT=%%C"
setlocal EnableDelayedExpansion
ECHO rename "!LEFT!(%%B)!RIGHT!" "!LEFT!(!MID:~-%_DIGITS%!)!RIGHT!"
endlocal
) else (
rem // No numeric suffix encountered, so append zero-suffix:
ECHO rename "%%A" "%%~nA(%PAD%)%%~xA"
)
)
endlocal
exit /B
After having successfully verified the appropriate output of the script, remove the upper-case ECHO command to actually rename any files.
The script renames a file like image.tif to image(0000).tif (supposing there is no file named image(0).tif, originally). If you do not want that to happen, simply remove the /C:"%_REGEX2%" part from the findstr command line.

Batch unzip files in child folders, then copy specific files to network folder based on number in file name

I am a serious newbie at creating batch files and am hoping someone can help me. One of our staff receives zipped pdf docs by email, which she copies to a folder on her desktop. Within that folder, I would like for her to run a batch script that will
A. Unzip the zipped contents into a network directory, i.e. \server\contracts
Under this directory, the process will create folders for each group of contracts, i.e. \server\contracts\Masterson (The name of this will be same as zipped file name).
B. Then the batch process should copy a select few of the pdf documents into a network directory based on the filename. Each file contains a number, which will go in the following manner: Masterson + 1.pdf >> \server\contracts\Item1 and \server\contracts\Item2, etc. Masterson + 1.pdf will go into \server\contracts\Item1 without a folder name, as will Paisley + 1 certificate.pdf and Johnsonville + 1 document.pdf.
The problem is that the companies do not follow instructions and the number can be at the beginning, middle, or end of the file name. Also, unfortunately, there are spaces in the name of the zipped file and the pdf documents. Currently, we are only copying 4 filenames into separate directories for other people to review and validate.
Below is what I did so far looking around this site:
#Echo off
SETLOCAL
for /R "delims=\\server\contracts\RECEIVED 2017-18 APPLICATION" %%I in
("*.zip") do (
"%ProgramFiles(x86)%\7-Zip\7z.exe" x -y -o"%%~dpnI" "%%~fI"
)
rem setlocal enableextensions disabledelayedexpansion
CLS
::The Input Folder
set "InputFolder=C:\Users\eartha.kitt\Desktop\Test"
::The Output Folders
set "Output1=\\server\contracts\ITEM 1 17-18 CERTS"
set "Output6=\\server\contracts\ITEM 6 SIGNATURES"
set "Output8A=\\server\contracts\ITEM 8A 17-18 CALENDARS"
set "Output8B=\\server\contracts\ITEM 8B 16-17 REVISED CALENDARS"
set "Output8a=\\server\contracts\ITEM 8A 17-18 CALENDARS"
set "Output8b=\\server\contracts\ITEM 8B 16-17 REVISED CALENDARS"
::The extensions to wait
set "extensions=*.pdf"
setlocal EnableDelayedExpansion
:Loop
cls
echo Waiting for file ...
for /f "usebackq delims=|" %%a in ('dir /b /s %InputFolder%\%extensions%
2^>nul') do (
rem for /r %%a in in (%InputFolder%\%extensions% 2^>nul') do (
set "Fichier=%%a"
echo Treating _^> %%a
if "!Fichier:~0,-2!"==" 1" COPY "%%~a" %Output1%
if "!Fichier:~0,-2!"==" 6" COPY "%InputFolder%\~%%a" %Output6%
if "!Fichier:~0,-3!"=="8A" COPY "%InputFolder%\%%a" %Output8A%
if "!Fichier:~0,-3!"=="8B" COPY "%InputFolder%\%%a" %Output8B%
if "!Fichier:~0,-3!"=="8a" COPY "%InputFolder%\%%a" %Output8a%
if "!Fichier:~0,-3!"=="8b" COPY "%InputFolder%\%%a" %Output8b%
::Waiting ~5 seconds
ping localhost -n 6 >nul
)
::Return to the loop
goto:Loop
Of course this doesn't work. Please help!
Well - bravo for the attempt! And so close...
Let's take the first part
for /R "delims=\\server\contracts\RECEIVED 2017-18 APPLICATION" %%I in ("*.zip") do (
"%ProgramFiles(x86)%\7-Zip\7z.exe" x -y -o"%%~dpnI" "%%~fI"
)
What's wrong here is that the delims clause is only usable in for /f. for /r always delivers the entire filename to the metavariable %%I.
On my system I use %server%\u for testing - u is a shared resource on the server assigned to U:\ on server.
for /R "\\%server%\u\contracts\RECEIVED 2017-18 APPLICATION" %%I IN ("*.zip") do (
"%ProgramFiles(x86)%\7-Zip\7z.exe" x -y -o"%%~dpnI" "%%~fI"
worked happily for me - delivering the extracted files to "u:\contracts\RECEIVED 2017-18 APPLICATION"
The second part of your code is examining "C:\Users\eartha.kitt\Desktop\Test" not "\%server%\u\contracts\RECEIVED 2017-18 APPLICATION" - very sensibly assigned to a variablename for easy adjustment.
Here's my modified code:
SET "terminatefilename=stop.txt"
DEL "%terminatefilename%" 2>nul
rem setlocal enableextensions disabledelayedexpansion
CLS
::The Input Folder
set "InputFolder=C:\Users\eartha.kitt\Desktop\Test"
set "InputFolder=\\%server%\u\contracts\RECEIVED 2017-18 APPLICATION"
::The Output Folders
set "Output1=\\%server%\u\contracts\ITEM 1 17-18 CERTS"
set "Output6=\\%server%\u\contracts\ITEM 6 SIGNATURES"
set "Output8A=\\%server%\u\contracts\ITEM 8A 17-18 CALENDARS"
set "Output8B=\\%server%\u\contracts\ITEM 8B 16-17 REVISED CALENDARS"
FOR /f "tokens=1*delims==" %%b IN ('set output') DO MD "%%c" 2>nul
::The extensions to wait
set "extensions=*.pdf"
setlocal EnableDelayedExpansion
:Loop
cls
echo Waiting for file ...
for /f "delims=|" %%a in ('dir /b /s "%InputFolder%\%extensions%" 2^>nul') do (
rem for /r %%a in in (%InputFolder%\%extensions% 2^>nul') do (
SET "copied="
echo Treating _^> %%a
REM OPTION 1 - Key string must be at end-of name part
set "Fichier=%%~Na"
if /i "!Fichier:~0,-2!"==" 1" COPY "%%a" "%Output1%"&SET "copied=Y"
if /i "!Fichier:~0,-2!"==" 6" COPY "%%a" "%Output6%"&SET "copied=Y"
if /i "!Fichier:~0,-3!"==" 8A" COPY "%%a" "%Output8A%"&SET "copied=Y"
if /i "!Fichier:~0,-3!"==" 8B" COPY "%%a" "%Output8B%"&SET "copied=Y"
REM OPTION 2 - Key string may be anywhere in filename
IF NOT DEFINED copied (
echo "%%~na"|FINDSTR /i /L /C:" 8B" >NUL
IF NOT ERRORLEVEL 1 COPY "%%a" "%Output8B%"&SET "copied=Y"
)
IF NOT DEFINED copied (
echo "%%~na"|FINDSTR /i /L /C:" 8A" >NUL
IF NOT ERRORLEVEL 1 COPY "%%a" "%Output8A%"&SET "copied=Y"
)
IF NOT DEFINED copied (
echo "%%~na"|FINDSTR /i /L /C:" 6" >NUL
IF NOT ERRORLEVEL 1 COPY "%%a" "%Output6%"&SET "copied=Y"
)
IF NOT DEFINED copied (
echo "%%~na"|FINDSTR /i /L /C:" 1" >NUL
IF NOT ERRORLEVEL 1 COPY "%%a" "%Output1%"&SET "copied=Y"
)
)
::Waiting ~5 seconds
timeout 6 >NUL
:: Test for exit
IF EXIST "%terminatefilename%" DEL "%terminatefilename%"&GOTO :EOF
::Return to the loop
goto:Loop
First, I set up terminatefilename so that creating this file will terminate the batch (it's an infinite loop by design in your code)
Next, I overrode your inputfolder name to suit my system.
Then the output directories. I adjusted their names to suit my system. Note that batch is largely case-insensitive, so setting Output8A and Output8a is setting the same variable. The only time that batch commands are case-sensitive is the metavariable (loop-control variable) in a for statement.
Then I inserted a line to create the destination directories. This uses a set command to list the variables starting output in the format Output1=\\%server%\u\contracts\ITEM 1 17-18 CERTS (where server will have been resolved). The command reads the output of the set command, uses = as the delimiter and assigns output1 to %%b and \\%server%\u\contracts\ITEM 1 17-18 CERTS to %%c. We want to make sure the directory %%c exists, so we make it with an md command and ignore complaints that it already exists with 2>nul.
Next the for/f. for /f reads each line of (thisfilename) or ("this literal value") or ('the output of this command') but when you need to read a from a file whose name must be double-quoted beacuse it contains spaces, then the syntax is for /f "usebackq"... (idontknow) or ("this filename containing spaces") or ('ive no idea whatever') or (`the output of this command`)
So there's no need to use usebackq -- in fact, it's counterproductive.
The delims=| is optional and could be replaced by delims= as the output of a dir command will never contain | (illegal in a file or directoryname). You do need the delims clause however, as the default delimiters include Space and the default tokens is 1 so only the first string of each line output up to the first space will be assigned to %%a.
Hmm - you've remmed-out a for/r. Sadly, the target directory in the for /r can't be a metavariable.
Next, I've cleared a copied flag saying "so far, this file has not been copied"
Next, set fichier to the name-part only of the filename. Since you are using .pdf as a filter, each name output by the for/f will be a full-filename, ending in .pdf
Next, almost-correct with the battery of if statements. The /i makes the comparison case-insensitive so that it will cope with both 8a and 8A. The strings on both sides of the == must be identical to pass the == test, so you need a 3-character string in the 8 tests.
You've evidently been experimenting with the copy command and trying to feed it with appropriate strings. %%~a strips %%a of any enclosing quotes. %%a won't have enclosing quotes - just the fullfilename, so this does nothing in this instance. %InputFolder%\~%%a concatenates the values from InputFolder,"\~" and %%a - which means "%inputfolder%\~%inputfolder%\filenameandextension of %%a". The last two would be resolved to the same, bar the ~.
Since the entire filename is contained in %%a, all that's needed for the sourcefile is "%%a" - quoted as it will probably contain spaces.
Well - the destination directory (we've already established it with the for /f...%%b) can also contain spaces, so it needs to be quoted, too.
I've then set the flag copied to Y if the line took effect. Actually, it can be set to any non-empty value as the whole object is to interpret whether or not it exists with an if defined statement to bypass any later attempts to copy the same file (remember - copied is cleared for each file)
Now - the second option. This is actually a more powerful detector of the required target strings than is the substring version, and will detect the target string anywhere in the filename.
If you echo the name-part only of the file %%~na into a findstr, then set to findstr to look for the /L literal string /i case-insensitive c:"some literal string" and output ny matches found to nowhere (>nul) then findstr will set errorlevel to 0 if found and 1 otherwise. (the /L is superfluous here, I use it habitually to remind me I'm working with literal strings, not regular expressions)
If errorlevel n will be true if errorlevel is currently n or greater than n, so if the string is found, we do the copy and set the copied flag as before.
I reversed the order of tests because it was easier for me to do using the editor I use.
Now - there's a downside to this approach. It's a double-edged sword. Since the target string is detected wherever it appears in the filename, whatever whatever 10 something something.pdf will pass the test for 1 because Space1 appears in its name.
When the loop has finished, use timeout to wait, redirecting the output to nul to make it pipe down. Note that your delay was inside the for loop - so it would have waited 6 seconds after processing each file, not 6 seconds after processing an entire batch.
Finally, if you create terminatefilename from anothe cmd instance, the batch will exit cleanly and kill terminatefilename for you. Much cleaner than control-c.
A last note: Since you are copying the file, not MOVEing it, it will still exist in the same place presumably after the 6 seconds has elapsed and will be endlessly copied and recopied. You'd probably need to make adjustments to achieve the desired result.

findstr not working with string containing dash (-)?

I've been working on a BAT file which will delete old files based on creation date. To do this I've generated a list of all files and paths, then a list of files names to be protected. FINDSTR is then used to remove these files from the list of files and paths.
This system works fine until I encounter a file with a dash (or so it seems!)
Here's an example:
cleaner_protect.txt contains:
New File.txt
New File - Copy.txt
cleaner_fullpath.txt contains:
P:\New File.txt
P:\New File - Copy.txt
P:\Old File.txt
I want to remove the New Files stored in cleaner_protect.txt from the cleaner_fullpath.txt, leaving the Old Files behind which I will later delete (not up to that bit yet lol). Here is my code so far:
:: Remove protected files from list to be deleted (fullpath)
:RemoveFile
:: load string into variable
set /p target= <cleaner_protect.txt
:: remove protected file from full path list
echo -----------------------------
echo Searching for: "%target%"
echo -----------------------------
pause
findstr /v ".\<%target%\>." cleaner_fullpath.txt > cleaner_temp.txt
echo -----------------------------
type cleaner_temp.txt
echo -----------------------------
pause
del cleaner_fullpath.txt
ren cleaner_temp.txt cleaner_fullpath.txt
:: Count remaining lines in list
Set target=cleaner_protect.txt
Set /a lines=0
For /f %%j in ('Find "" /v /c ^< %target%') Do Set /a lines=%%j
Echo %target% has %lines% lines.
pause
:: Loop until completed
IF %lines% GTR 0 (
:: Remove line from protected list
more +1 cleaner_protect.txt > cleaner_temp.txt
del cleaner_protect.txt
ren cleaner_temp.txt cleaner_protect.txt
set /a lines-=1
GOTO :RemoveFile
)
Pauses and echos are for debugging purposes... I want this to run almost invisibly.
Can anyone shed some light on this? I need this code to repeatedly go through a dropbox and delete old files which may be in various levels of structure.
Maybe this simple line does all you want:
findstr /E /V /G:cleaner_protect.txt cleaner_fullpath.txt > cleaner_temp.txt
Sample output:
P:\Old File.txt
I would do it as follows:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem /* Prepend each line of `cleaner_protect.txt` with `\\`, remove a trailing space,
rem if applicable, and write result to `cleaner_temp.txt`: */
> "cleaner_temp.txt" (
for /F "usebackq delims= eol=|" %%E in ("cleaner_protect.txt") do (
set "ITEM=\\%%E|"
setlocal EnableDelayedExpansion
echo !ITEM: ^|=!
endlocal
)
)
rem /* Search `cleaner_fullpath.txt` for lines that do not end in any of the lines of
rem `cleaner_temp.txt` and process the returned items only: */
for /F "delims= eol=|" %%F in ('
findstr /V /I /E /L /G:"cleaner_temp.txt" "cleaner_fullpath.txt"
') do (
ECHO del "%%F"
)
rem // Clean up temporary file `cleaner_temp.txt`:
del "cleaner_temp.txt"
endlocal
exit /B
After having tested the script, remove the upper-case ECHO command.
Supposing cleaner_protect.txt contains this:
New File.txt
New File - Copy.txt
The temporary file cleaner_temp.txt is going to contain this:
\\New File.txt
\\New File - Copy.txt
So having the text file cleaner_fullpath.txt:
P:\New File.txt
P:\New File - Copy.txt
P:\Old File.txt
Only the following items are processed:
P:\Old File.txt
The leading \\ is taken as one literal \ by findstr (as it uses the \ as an escape character).
The prefix \\ is implemented in order to cover also the following situations:
Let us assume cleaner_protect.txt holds this:
New File.txt
And cleaner_fullpath.txt holds this:
P:\New File.txt
P:\Very New File.txt
Without the prefix, P:\Very New File.txt would also match New File.txt at the end, hence it would not become deleted erroneously.
Then let us assume cleaner_protect.txt holds this:
.File.txt
And cleaner_fullpath.txt holds this:
P:\.File.txt
P:\Other.File.txt
With a \ prefix, P:\Other.File.txt would also match \.File.txt at the end, because . is a meta character to findstr (even though literal search strings are defined by /L, but escaping like \. still applies and results in a literal .), hence it would also not become deleted erroneously. However, with a \\ prefix, escaping applies to the second \, so a literal \ is the result; the . does not need to be escaped with the /L option.

Resources