How to use wildcards in Batch programming? - batch-file

I am trying to move certain groups of files using wildcards to a folder that is created by the group's filename. The names of the files are stored in "events.txt".My batch file is running uptil the last line. It is displayed that my syntax is not correct.
echo off
for /F "tokens=*" %%A in (events.txt) do call :makemove %%A
pause
exit
:makemove
set f=%1
set file=%f:~0,-4%
md X%file%
set dest=C:\Users\sony\Desktop\X%file%
move /y "C:\Users\sony\Desktop\*%file%*.*" "%dest%"

Seems like there are whitespace characters after
set file=%f:~0,-4%
line.
This causes the last line of your script evaluate to
move /y "C:\Users\sony\Desktop\*foobar *.*" "C:\Users\sony\Desktop\Xfoobar "
and messes paths up.

Like Helbreder pointed out, there is a space after set file=%f:~0,-4%.
To avoid this type of problems you can use the extended syntax of SET.
set "file=%f:~0,-4%"
The surrounding quotes will ensure that only all characters until the last quote are part of the string.
The quotes itself are not part of the string.
So even this would work
set "file=%f:~0,-4%" the spaces and this text will be removed
Another positive effect from the quotes is that they will avoid problems with special characters in the filename, like in Cat&Dog.
So your code should look like
#echo off
for /F "tokens=*" %%A in (events.txt) do call :makemove %%A
pause
exit
:makemove
set "f=%~1"
set "file=%f:~0,-4%"
md "X%file%"
set "dest=C:\Users\sony\Desktop\X%file%"
move /y "C:\Users\sony\Desktop\*%file%*.*" "%dest%"

Related

How do I write a batch programe to copy a set of folders with same suffix and their files from a server to a client?

I want to write a batch program to copy some deeply nested folders with the same suffix in this case 100,
It only copied all the folders but only one file in the top most folder (they are alphabetically arranged in the server) *100.bat was copied.
I want to copy all files in all folders with name_of_folder100.
Thanks for your Time.
This is my attempt:
#echo off
:: variables
set hour=%time:~0,2%
if "%hour:~0,1%"==" " set hour=0%time:~1,1%
set
set drive= E:\PWD_BACKUP_%date:~10,4%_%date:~4,2%_%date:~7,2%_%hour%_%time:~3,2%
set PWD_drive_100=E:\PWD_BACKUP_\PWD_100_%date:~10,4%_%date:~4,2%_%date:~7,2%_%hour%_%time:~3,2%
set backupcmd=xcopy /s /c /d /e /h /i /r /y
%backupcmd% "\\xx.xx.xx.xx\live_projects\PWD\*100" %PWD_drive_100%
It's normal to follow the #echo off line with setlocal. This makes sure that any variables altered or created in the batch file are not maintained in the cmd session's environment when the batch ends.
Use set "var=value" for setting string values - this avoids problems caused by trailing spaces. Don't assign a terminal \, Space or " - build pathnames from the elements - counterintuitively, it is likely to make the process easier. If the syntax set var="value" is used, then the quotes become part of the value assigned.
The set drive=... statement will include the space following the = in the value assigned to drive. This is probably of no consequence in the current situation, but may be significant for other batches.
Other than the last 2 lines of code, there is a great deal of date/time-manipulation going on. We have to rely on your local knowledge here as the date/time format is user-dependent.
The overall problem is that * cannot be used for directorynames. Logically, what you appear to want to copy from your description is ...e_projects\PWD\*100\*, but that structure is not supported by xcopy.
So I'll restate the problem as "copy all directories that have a name ending 100"
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following settings for the directories and filenames are names
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "destdir=u:\your results"
FOR /f "delims=" %%e IN (
'dir /b /s /ad "%sourcedir%\PWD\*" ^|findstr /e "100"'
) DO (
SET "dirname=%%e"
SET "dirname=!dirname:*\PWD\=!"
ECHO XCOPY "%%e\*" "%destdir%\!dirname!\"
)
GOTO :EOF
Always verify against a test directory before applying to real data.
The setlocal enabledelayedexpansion invokes delayedexpansion mode, where within a code block (parenthesised sequence of lines) !var! is the run-time value of var (as var is changed) and %var% is the value that var contained when the code block was parsed.
Documentation: Stephan's DELAYEDEXPANSION link
We then establish the source and destination directories. I've posted my test directories, you would need to edit them to suit your particular circumstances.
The dir command lists all directories (/ad) in basic form (/b) that is, name only - no size, date, etc. The /s extends the scan to subdirectories also.
The dir output is piped to findstr (the caret escapes the pipe to show that it is part of the command to be executed, not of the for) which filters the dir list for (directorynames) which /e end "100".
The for/f assigns each line of the result to %%e. The delims=" ensures the entire line is assigned to %%e(in case%%e` contains separators like Space)
%%e thus contains something like U:\your files\PWD\sub 5\subsub 5\subsubsub\subsubsubsub100, so we need to remove the part up to \PWD\ to get the subdirectory name in a format suitable for the xcopy command. Batch cannot substring a metavariable like %%e directly, so we assign %%e to a user-variable dirname (which is therefore changing within the loop, so we need to use !dirname! to acess it's run-time value) and the following set removes all characters up to and including \PWD\. (See set /? from the prompt for documentation)
Then it's just a matter of performing the xcopy. Note that I've added an echo keyword to "disarm" the xcopy so that the xcopy command is merely listed to the console instead of being executed. Once you've verified that the command is correct, remove that echo to actually execute the xcopy.
The destination of the xcopy is </kbd> which tells xcopy that the destination is a directory, and to create it if it does not exist.
Add >nul to the xcopy command to suppress the report if you want.

How to clean-up file paths (back/forward slashes) provided as arguments in a .bat script to avoid syntax errors

I have a simple .bat script which renames all files in a folder using ren. The input argument is a path to a folder containing the files to be renamed. The script sometimes returns syntax errors which we've traced to the fact that sometimes the input path has forward slashes, backslashes, or a mix of both (and sometimes starts with a double forward slash). We would like to make this script more robust by allowing it to accept any of these types of paths, and cleaning up the path as part of the .bat script before calling the ren command.
So my question is: is there a (set of) command(s) I can apply to the file path argument (%1 in the example below) before calling the ren function that will correct all forward/backslashes to be consistent and avoid syntax errors? I don't have much experience with .bat scripts, so any code examples would be helpful.
#echo off
setlocal ENABLEDELAYEDEXPANSION
for %%F in (%1*.nc) do (
for /F "tokens=1-8 delims=_" %%a in ("%%~nF") do (
ren "%%F" "%%a_%%b_%%c_%%d_%%e_%%g_%%f_%%h.nc"
)
)
UPDATE: In the end, only the last suggestion by Magoo was needed, because changing %1 to "%~f1" fixed the slash issues. I also had to add %~f1\ to the first argument of the ren command because otherwise it was somehow looking in the wrong folder (the first for found the files ok, but the ren command was looking in the wrong folder.
#echo off
setlocal ENABLEDELAYEDEXPANSION
for /F "delims=" %%F in ('dir /b /a-d "%~f1\*.nc"') do (
for /F "tokens=1-8 delims=_" %%a in ("%%~nF") do (
ren "%~f1\%%~nF.nc" "%%a_%%b_%%c_%%d_%%e_%%g_%%f_%%h.nc"
)
)
set "corrected=%~1"
set "corrected=%corrected:/=\%"
Then use %corrected% in place of %1 AND quote the filename thus:
for %%F in ("%corrected%*.nc") do (
If %1 is always a directory-name, then add
if "%corrected:~-1%" neq "\" set "corrected=%corrected%\"
as a third set line before the for line.
The first set assigns the value of %1 to a variable corrected - the ~ removes any enclosing quotes.
The second set changes all strings matching that between the : and = into that between the = and % in the variable given and assigns to the first-mentioned variable (can be the same variable, as in this case)
The third set, if used, checks that the last character is \ and if it is not, appends a \.
The quoting of the filename-string allows there to be spaces in the path/filename and is harmless if there are no spaces.
To avoid attempting to rename a file twice, instead of
for %%F in ("%corrected%*.nc") do (
use
for /F "delims=" %%F in ('dir /b /a-d "%corrected%*.nc"') do (
This builds a list of filenames in memory, then processes that list.

Batch Script - Rename files, removing a variable prefix and suffix

I have several files in a folder with names like
"prefix (S-N 12RE3123) suffix.pdf"
I would like to rename them to just "12RE3123.pdf", removing the prefix and suffix. The issue is that both the prefix and suffix are of varying lengths/ characters, so I cannot just rename by removing the first/last xx characters, but have to use the only commonality where only the characters inside the parenthesis following "S-N" are those to be kept.
There is one special case where a few of the serial numbers are named as WD-12RE3123, and I need to remove the WD- as well. If I had to do it manually, there aren't a lot of them like that so it wouldn't be the end of the world, but having it automated would be nice. I thought of maybe doing an if statement after the removal of prefix/suffix to check if the first 3 characters match WD- then remove those if true, but I am not sure on the implementation.
I am still a novice in batch or vbscript, so I was wondering if this can be done in either of those. Batch has the method "REN" to rename files, but since the final name depends upon what the current name is I am not sure how to set up delimiters or how to approach this.
Any assistance would be greatly appreciated.
Here is a simple solution for your request. It relies on the following facts:
the prefix portion does not contain any parenthesis ( or ) on its own (suffix might though);
the serial number does not contain any parentheses ( or ) on its own;
there are no duplicate serial numbers, also with respect to removal of potentional WD- strings;
This is the code (after having tested the code on the files in your target folder, you need to remove the upper-case ECHO command to actually rename the files):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Set constants here:
set "LOCATION=.\test"
set "PATTERN=*.pdf"
set STRINGS="S-N " "WD-"
cd /D "%LOCATION%"
for /F "eol=| delims=" %%K in ('dir /B "%PATTERN%"') do (
for /F "eol=| tokens=2 delims=()" %%L in ("%%~nK") do (
set "NAME=%%L"
setlocal EnableDelayedExpansion
for %%J in (%STRINGS%) do (
if "!NAME!"=="%%~J!NAME:*%%~J=!" (
set "NAME=!NAME:*%%~J=!"
)
)
ECHO ren "%%~fK" "!NAME!%%~xK"
endlocal
)
)
endlocal
exit /B
How it works:
the first section specifies the folder location, the file pattern and the strings to remove (after having extracted the portion within ()); adapt the values to your needs;
there are several nested for loops; the outermost one simply iterates all the files matching the given pattern (it basically reads and parses the output of a dir command applied on the given files and loops through the returned items; opposed to a standard for loop, this for /F method ensures that the entire folder is read before the loop starts iterating, which is necessary when modifying the enumerated folder content like we do here by renaming files; see also this thread about that issue);
the next for /F loop extracts the file name portion of interest, that is everything between the first pair of parenthesis, and stores it in variable NAME; this loop iterates once only per file;
the is another for loop which walks though all items in the STRINGS variable;
the if clause checks whether the current item of STRINGS occurs at the very beginning of the NAME value; if so, it is removed, otherwise not; this is just a safety query because perhaps a serial number might also contain a given STRINGS item in the middle or at the end (for instance, 123-WD-45A);
at this point, the renaming is performed (after having removed ECHO, of course);
the toggling of delayed expansion is intended to avoid trouble with some special characters in the file names;
And here is another script that uses a more complex method for extracting the interesting parts of the file names. It relies on the following facts:
there occurs only a single substring (S-NSPACE in the file name
the serial number is followed by a );
the serial number does not contain any parentheses ( or ) on its own;
there are no duplicate serial numbers, also with respect to removal of potentional WD- strings;
The code looks like this:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Set constants here:
set "LOCATION=.\test"
set "PATTERN=*.pdf"
set "FILTER=(S-N [^()][^()]*)"
set "IDENTIFYER=S-N "
set STRINGS="WD-"
cd /D "%LOCATION%"
for /F "eol=| delims=" %%K in ('
dir /B "%PATTERN%" ^| findstr /I /R /C:"%FILTER%"
') do (
set "NAME=%%K"
setlocal EnableDelayedExpansion
set "NAME=!NAME:*(%IDENTIFYER%=!"
for /F "eol=| tokens=1 delims=)" %%L in ("!NAME!") do (
setlocal DisableDelayedExpansion
set "NAME=%%L"
setlocal EnableDelayedExpansion
for %%J in (%STRINGS%) do (
if "!NAME!"=="%%~J!NAME:*%%~J=!" (
set "NAME=!NAME:*%%~J=!"
)
)
ECHO ren "%%~fK" "!NAME!%%~xK"
endlocal
endlocal
)
endlocal
)
endlocal
exit /B
Basically, this script works similar to the above one, with a few deviations:
the first section specifies a file name filter and the serial number identifyer (S-N) in addition;
the dir command in the outermost for loop is piped into a findstr command to filter out files not containing (S-N and ) and a string (not containing (/)) in between already at the beginning;
the part (S-N and everything before is removed, the result is stored in NAME;
the next for /F loop extracts everything up to but not including the first ) from the NAME value, which constitutes the file name portion of interest; this loop iterates once only per file;
the is another for loop which walks though all items in the STRINGS variable, which does not contain the S-N portion here as this has already been removed before; the rest in this loop is the same as in the above script;
at this point, the renaming is performed (after having removed ECHO, of course);
#ECHO Off
SETLOCAL
SET "sourcedir=U:\sourcedir"
FOR /f "delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*(*)*.*" '
) DO (
FOR /f "tokens=1,2delims=()" %%c IN ("%%a") DO (
FOR /f "tokens=1-3delims=- " %%m IN ("%%d") DO (
IF "%%o"=="" (ECHO(REN "%sourcedir%\%%a" "%%n%%~xa"
) ELSE (
ECHO(REN "%sourcedir%\%%a" "%%o%%~xa"
)
)
)
)
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(REN to REN to actually rename the files.
Apply each filename matching the mask "()." to %%a
Tokenise using ( and ) as delimiters so that the parenthesised portion is in %%d
Re-tokenise using - and as delimiters so that the required number is in %%o for S-N patterns and %%n for WD- pattern.
Show the rename line.
Note: this should work provided the prefix portion does not contain parentheses and the parenthesised portion is exactly as described.
Magoo and aschipfl both provided good pure batch solutions.
I find development of custom batch scripts for every complex renaming task to be tedious, which is why I wrote JREN.BAT - a regex find/replace renaming utility. JREN.BAT is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward. Full documentation is available from the command line via jren /?, or use jren /?? for paged help.
With JREN.BAT, the solution is as simple as (untested):
jren "^.*\((?:S-N (?:WD-)?)(.+?)\).*" "$1.jpg" /fm *.jpg
If I got the regex wrong, it can easily be fixed. You should probably add the /T option to run the command in test mode first, and then remove it when everything looks good.
If you put the command in a batch script, then you must use CALL JREN.

Batch file : how to get a specific part of a folder path

I'm currently looping threw the subfolders of a known directly. I would like to grab the folder that contains the file I'm looking for, but only that folder's name, not the whole path, and preferably in a different variable to be use later on in the batch file ...
pause
CD c:\%username%\testFolder\Program\OLD\
For /R %%G in (test.t?t) do Echo %%G
pause
Now, this shows me the file I'm looking for : C:\User\testFolder\Program\OLD\myfile\exemple1\test.txt
what I would like is to replace the Echo %%G to set in a variable the "unknown" folders and subfolder, something along the line of
For /R %%G in (test.t?t) do set var1 = myfile\exemple1
anyone can point to what I'm missing ?
If you for /? in a cmd console, you'll see runtime variables on the last couple of pages. Using %%~dpG notation you can get the full drive\path specification of matched files. Then, using substring substitution and delayed expansion, replace %CD% with nothing. Finally, you can strip the leading and trailing backslash with a numeric substring -- !varname:~1,-1!.
#echo off
setlocal enabledelayedexpansion
cd /d "c:\%username%\testFolder\Program\OLD\"
for /R %%G in (test.t?t) do (
set "var1=%%~dpG"
set "var1=!var1:%CD%=!"
if "!var1!"=="\" (echo .\) else echo(!var1:~1,-1!
)
If you wish, you can prevent echoing duplicates by echoing only if the previous match does not equal the current one.
#echo off
setlocal enabledelayedexpansion
cd /d "c:\%username%\testFolder\Program\OLD\"
for /R %%G in (test.t?t) do (
set "var1=%%~dpG"
set "var1=!var1:%CD%=!"
if not "!var1!"=="!prev!" (
if "!var1!"=="\" (echo .\) else echo(!var1:~1,-1!
)
set "prev=!var1!"
)

Edit html file in batch file

I have a script in which I read html files which I want to edit. Here I paste the code which calls :remove_redundant_columns subroutine.
It should remove the spaces/white spaces from begin of each line and remove from html file. Only problem is that it adds extra text like = to lines which are almost empty, just have few tabs.
The html file which I downloaded is from hidemyass.com/proxy-list/1
call parse_proxy.bat remove_redundant_columns !FILENAME!
exit /b
:remove_redundant_columns
REM Remove whitespaces from begin of lines and <span></span>
FOR /f "tokens=*" %%t in (%1) do (
SET S=%%t
SET S=!S:^<span^>^</span^>=!
if NOT "!S!"=="" >>$tmp$ echo !S!
)
del %1
REN $tmp$ %1
exit /b
If you believe, that's your only problem... You need to check, if your variable S contains content.
That's required, as substitution on an undefined variable will not produce an undefined/empty variable, the new content will be the substitution text.
:remove_redundant_columns
REM Remove whitespaces from begin of lines and <span></span>
FOR /f "tokens=*" %%t in (%1) do (
SET S=%%t
if defined S (
SET S=!S:^<span^>^</span^>=!
>>$tmp$ echo !S!
)
)
As dbenham stated, you got many other problems,
and one additional problem is the echo !S! command itself.
ECHO has some nasty side effects on different content.
If the content is empty (or only spaces) then it will print it's currently state
ECHO IS OFF
If the content is OFF or ON it will NOT be echoed, it will only change the state.
And if the content is /? it will echo the help instead of /?.
To solve this you could simply change ECHO !S! to ECHO(!S! and all problems are gone.
jeb already solved your = problem (once the extra IF DEFINED check is added to his answer). But you may have at least one other problem.
I agree with Joey that you should not be using batch to manipulate HTML like this. But, if you really want to...
Your potential problem is that HTML usually has ! characters sprinkled within. Your code uses delayed expansion, but that causes corruption of FOR variable expansion when it contains ! character(s). The solution is to toggle delayed expansion on and off within your loop.
:remove_redundant_columns
setlocal disableDelayedExpansion
REM Remove whitespaces from begin of lines and <span></span>
(
FOR /f "usebackq eol= tokens=*" %%t in ("%~1") do (
SET S=%%t
setlocal enableDelayedExpansion
if defined S SET "S=!S:<span></span>=!"
for /f "eol= tokens=*" %%S in ("!S!") do if "%%S" neq "" echo %%S
endlocal
)
) >>$tmp$
move /y $tmp$ "%~1"
exit /b
Other minor changes that were made to the code:
The search and replace can be simplified by using quotes so that special chars don't need to be escaped.
You can replace DEL and REN with a single MOVE.
Redirection is more efficient (faster) if you redirect once using an outer set of parentheses
You may need to search a file name that has spaces and or special characters, in which case you will need to quote the name. But that requires the FOR /F "USEBACKQ" option.
EDIT
Modified code to strip leading spaces after <span></span> has been replaced to eliminate potential of a line containing nothing but spaces and/or tabs.
Also set EOL to space to prevent stripping of lines beginning with ;

Resources