Windows: copy a file until the file not exist - windows

I want to use a Windows batch file in to copy a file (myfile0001.bdg) from one specific directory to another. But I want to check if the file in the target directory exists and if the answer is yes, increment the file with 0001 and check again if the file exists (myfile0002.bdg) and so on, until the file does not exist, and copy the file with the new title.
So, if in the target directory, I have these files:
myfile0001.bdg
myfile0002.bdg
myfile0003.bdg
myfile0004.bdg
myfile0005.bdg
myfile0006.bdg
The new file should be named myfile0007.bdg. The next time I will execute the batch, the new file will be myfile0008.bdg, etc.
I know there is a command "IF EXIST" but I don't know to do what I need.
==============
I'm under Windows 7 x32
The source directory is "C:\USERS\RAMBYTES\DOCUMENTS\"
The target directory is "P:\BACKUP\"
The file is "MYFILE0001.BDG"

Something like this:
#echo off
set source_file=C:\USERS\RAMBYTES\DOCUMENTS\MYFILE0001.BDG
set target_dir=P:\BACKUP\
set done=0
for /l %%i in (1,1,1000) do (
call :check_and_copy %%i
if errorlevel 1 goto :eof
)
goto :eof
:check_and_copy
setlocal EnableDelayedExpansion
set num=000000%1
set fnum=!num:~-4!
set fname=%target_dir%\myfile%fnum%.bdg
rem echo %fname%
if not exist "%fname%" (
echo copying %source_file% to %fname%
exit /b 1
)
exit /b 0
There is no error handling in case there are more than a 1000 files present in the target directory. If you want to increas the file limit, you need to adjust the "main" for loop and the "formatting" of the number in the sub-program
The trick with adding the leading zeros was taken from here: https://stackoverflow.com/a/9430912/330315

#ECHO OFF
SET destdir=c:\destdir
SET newname=myfile0000
FOR /f %%i IN (' dir /b /on %destdir%\myfile????.bdg ' ) DO SET newname=%%~ni
SET newname=1%newname:~-4%
SET /a newname+=1
SET newname=myfile%newname:~-4%.bdg
COPY myfile0001.bdg %destdir%\%newname%
change the destination directory as desired, and include the source directory if required.

Take the file name.
Extract the numeric part.
Check if the corresponding target name exists.
If so,
4.1) increase the numeric part;
4.2) if it doesn't exceed the highest possible number go to Step 3;
4.3) otherwise terminate.
If the target name doesn't exist, copy the file with the current numeric part and terminate.
Although algorithmically the condition in 4.2 may be more natural to be checked just after increasing the numeric part, like I put it above, the below script performs the check at a different point, at the beginning of the loop, which starts just after extracting the original numeric value from the source filename. Implementationally, that seemed to me more convenient.
In all other respects, the script implements the same algorithm:
#ECHO OFF
SET "fname=%~n1"
SET counter=1%fname:~-4%
:loop
IF %counter% GTR 19999 (
1>&2 ECHO Cannot copy the file: no free slots.
EXIT /B 1
)
SET "targetname=%~2\%fname:~0,-4%%counter:~1%%~x1"
IF EXIST "%targetname%" (
SET /A counter+=1
GOTO loop
) ELSE (
COPY %1 "%targetname%"
)
To explain some parts:
The tilde (~) in references to positional parameters means de-quoting of the correspondent parameter.
Sometimes in the script, the tilde is also directly followed by a modifier. Two modifiers are used here, n and x. The former causes the parameter to expand to the corresponding file name only (without the path and the extension) and the latter extract only the extension.
You can learn more about modifier in the built-in of the FOR command (by running
The fname environment variable is needed because extracting of name parts can only be done on environment variables. The %fname:~-4 expression, in particular, evaluates to the last four characters of the fname value. More specifically, it reads: extract the substring that starts at the 4th character from the end and, as -4 isn't followed by another argument, includes all the characters from that point till the end of the string.
Another similar-looking expression, %fname:~0,-4%, does the opposite: it returns the contents of fname except the last four characters. The meaning of the numbers is this: extract the substring that starts at the beginning of the string (offset 0) and spans the range up to and including the character at the offset of 4 from the end.
One more expression of this kind, %counter:~1, extracts the characters starting from the second one (i.e. offset 1) and up to the end of string (no second argument).
Run SET /? to find out more about string expressions.
The counter implementation may also require explanation. The 1 added in front of the numeric part of the file name is needed so that the entire value could be interpreted and processed correctly when incrementing it.
The thing is, a numeric value starting with a 0 is treated as an octal by the CMD command processor, so, putting 1 at the beginning makes it to interpret the number as a decimal, which it actually is. When constructing the complete name of the target file, we simply need to discard the added 1, which is what the %counter:~1 is used for.

Related

Trying to make a menu in a windows command prompt

I have a batch file that gets run by the user typing:
usercompile filename
usercompile is a batch file that does this:
copy /y %1.txt lib\incoming_file.txt
and then starts the compiler:
compiler.exe
The compiler has the "incoming_file" name hard-coded into linked source (this can't be chaged), so the current method is simply to copy the user file in and rename it to the known name and run the compiler.
I'd like to present the user with a list of files that are generated when a batch file is run, then the batch file would copy the selected file in, rename it (just like is done now).
So it would look like this:
Please choose a file to compile:
1) matthews_build
2) marks_build
3) lukes_build
and then the user would type 1 or 2 or 3 (in this case) and press enter. The batch file would copy that file to the known file name and launch the compiler. The one good thing is that the files that need to be listed all have a unique extension (.jal).
Any ideas?
I changed my approach and consider my previous answer a bad practice: re-listing the files with a second dir command unnecessarily reads the disk again, not to mention the rare but possible case if a file is added/removed between the 2 dir's and makes the whole thing unreliable.
Based on this brilliant solution I did a possible implementation with dynamic array:
#echo off
set /a counter=0
setlocal enabledelayedexpansion
FOR /f "delims=|" %%i IN ('dir /b /on "yourpath*.jal"') DO (
set /a counter+=1
rem echo !counter!^) %%~ni
set FileList[!counter!]=%%~ni & rem This is an array element, a dinamically created variable
)
rem Iterate through variables:
FOR /l %%i IN (1,1,!counter!) DO (
echo %%i^) !FileList[%%i]!
)
set /p option="Choose an option: "
echo !FileList[%option%]!
endlocal
This makes the file list available for any number of following commands.
One possible solution is to list all .jal files and give them an option number, store the result, and based on user input, look up the file based on the option number. As I know no way of storing such a result in memory (no array/hash table data type), only in a file, if a file can not be used, then the listing should be repeated in a deterministic way so that if we re-assign the option numbers, we get the same result. We can do it ensuring alphabetical ordering.
Here is one implementation:
BLOCK 1
setlocal enabledelayedexpansion
FOR /f "delims=|" %%i IN ('dir /b /on "yourpath\*.jal"') DO (
set /a counter+=1
echo !counter!^) %%~ni
)
endlocal
The nested dir command ensures alphabetical ordering (reference.)
A remark why I put a pipe (|) as a delimiter: if you don't define a delimiter, the default space will be used. If your file name contains space then it would be truncated. So I picked a character that is not valid in file names ensuring the whole file name is returned.
Now if you get a number from the user by this:
set /p option=Choose your option:
after this command (evaluating and possibly re-requesting the input) to do a lookup for the file you can repeat BLOCK 1 but replace the echo line with examining the option like this:
if !counter! == %option%
and put those commands in the if block to do whatever you want to do with the file (for debugging, put back the echo command).

new to Batch script, what exactly this for loop do

I have a bactch script which I am trying to understand, as I am new to batch programming and have to customize this code. but I can't understand what the subroutines check_utf8_bom,create_utf8bom_free_file,remove_utf8 are actually doing. Can someone please help
set /p bom=<.\bom
for /f "delims=" %%G in (.\file-list.txt) do (
call:check_utf8_bom %bom% !SOURCE_FOLDER!
)
:check_utf8_bom
rem ;; Checks If There Is The UTF-8 Bom At The Beginning Of The File
set /p firstLine=<%2\tmp
if "%firstLine:~0,3%"=="%~1" call:create_utf8bom_free_file %2
goto:eof
:create_utf8bom_free_file
rem ;; Remove UTF-8 BOM From "tmp" File o Avoid Problems During Interpretation
type %1\tmp>%1\tmp.bom
for /f "delims=" %%G in (%1\tmp.bom) do (
if defined i echo.%%G>>%1\tmp
if not defined i (
call:remove_utf8_bom "%%G" %1
set i=1
)
)
del %1\tmp.bom
goto:eof
:remove_utf8_bom
rem ;; Called From create_utf8bom_free_file Function Create The File Without The BOM In The First line
set fl=%~1
echo %fl:~3,-1%%fl:~-1%>"%2\tmp"
goto:eof
can somebody please help me to understand it?
%1 stands for the first argument passed to the script / :create_utf8bom_free_file subroutine.
type %1\tmp>%1\tmp.bom this prints file tmp from %1 directory to tmp.bom file - this should convert unicode file to ascii one (probaly without changing byte order mark )
for /f "delims=" %%G in (%1\tmp.bom) do - means read %1\tmp.bom line by line without splitting the line with delimiters ("delims=") so on each iteration the line will be assigned to %%G token (temporary variable that lives during FOR /F execution).
if not defined i and set i=1 is a workaround for missing break operator in batch file loops . The first checks if the variable is defined and the second sets value to the variable. So the first line is passed to :remove_utf8_bom (here the first two characters of the line should be removed) function and then the for loop is over.
At the end temp file is deleted and goto:eof means go to the end of the script - i.e. something similar to exit.
It's not really clear what the script does because you didn't post the remove_utf8_bom procedure. So far I can tell you that the the loop reads the content of the file tmp.bom line by line. In each iteration it checks whether the variable i is set or not. If it is, the currently processed line is appended to the file tmp. Otherwise the current line as well as the parameter %1 both are passed to the procedure remove_utf8_bom (which we don't know) and the variable i is being set to 1.
This is - as you've asked - what the for-loop does (without the lines above and below it). For more information I need more code.
EDIT:
By the way...even without the code I'd suppose that this script should remove the BOM from UTF-8 encoded text files. This is often needed because if a batch or cmd file is stored in UTF-8 there is a BOM at the beginning of it which causes that the first line in the script won't be executed.
To eliminate this problem you should avoid saving scripts as UTF-8. If you can't be sure about the encoding, you also could start scripts with REM doesn't matter what text is here as this line will be ignored. The rest of the script will be executed as desired.

Windows batch file copying/renaming files

I've been working on creating a batch file which moves files from one dir to another dir and if the filename already exists rename it then move it over.
I'm really new to creating batch files so heres what I have so far
set temp=C:\Users\Daniel\Desktop\a\a1
set dir=C:\Users\Daniel\Desktop\a\
set /a "counter=0"
set "duplicate=-copy^("
set "bracket=^)"
if exist "%temp%" ( ^
for %%i in (%temp%\*) ^
do ^
if exist "%dir%\%%~ni%%~xi" ( call :checkFileName %%~ni %%~xi) ^
ELSE ( move %temp%\%%~ni%%~xi %dir% ) )^
ELSE ( echo doesnt exist)
:checkFileName
echo test
set fileName=%1
set fileExtenstion=%2
set /a "counter+=1
rem Do whatever you want here over the files of this subdir, for example:
if exist %dir%%fileName%%duplicate%%counter%%bracket%%fileExtenstion% ( IF defined %1 (
IF defined %2 (call :checkFileName %1 %2 )) ) ELSE (ren %temp%\%fileName%%fileExtenstion% %fileName%%duplicate%%counter%%bracket%%fileExtenstion% )
timeout 30
goto :eof
:increment
set /a "counter+=1"
goto :eof
I've no idea to increment a var before calling my checkFileName function. I think recursively calling the same function is the right idea but I'm a bit rusty with the commands/syntax as I only started this on friday.
Any advice or pointers would be appreciated. (If you know any useful links/books that are worth a look let me know!)
timeout 600
#ECHO OFF
SETLOCAL
set "tempdir=C:\Users\Daniel\Desktop\a\a1"
set "dir=C:\Users\Daniel\Desktop\a"
set "tempdir=U:\sourcedir\t w o"
set "dir=U:\destdir"
set "duplicate=-copy("
set "bracket=)"
if exist "%tempdir%" (
for %%i in ("%tempdir%\*") do (
if exist "%dir%\%%~nxi" ( call :checkFileName "%%~ni" "%%~xi"
) ELSE (
move "%tempdir%\%%~nxi" "%dir%" >nul
)
)
) ELSE (
echo doesnt EXIST
)
GOTO :eof
:checkFileName
set "fileName=%~1"
set "fileExtenstion=%~2"
set /a counter=0
:nexttry
set /a counter+=1
rem Do whatever you want here over the files of this subdir, for example:
if exist "%dir%\%fileName%%duplicate%%counter%%bracket%%fileExtenstion%" GOTO nexttry
move "%tempdir%\%fileName%%fileExtenstion%" "%dir%\%fileName%%duplicate%%counter%%bracket%%fileExtenstion%" >nul
goto :eof
Here's a revised version. I'll explain the changes I've made:
#echo off turns off command-echoing
setlocal ensures any changes made to the environment are backed-out when the procedure ends.
I've added to extra sets to re-set the directories to suit my system. You'd need to delete these two lines for yours.
temp is a special name which points to a temporary directory. One of quite a few. Best not to use that particular name - replaced with tempdir
set when used for a numeric set doesn't require quotes. In a string-set, the syntax set "var=value" is used to ensure that trailing spaces on the command-line are not included into the value assigned (which can cause chaos - spaces are sort of - invisible.) Note that in a string set, spaces on both sides of the = are significant...
I prefer to assign directorynames into variables without the trailing \. This allows the value to be extended with the least gymnastics. Personal preference - but you used it both ways...
The carets are not required before ( and are only required before ) where the syntax would close an open parenthesis (ie. in a parenthesised statement-sequence as may occur in an if, else or do.) Used arbitrarily, this can lead to stray literal carets in filenames, for instance.
carets at end-of-line is a valid but easily-lost and a little-used technique. The rule for breaking statements over multiple lines is crudely, keep do, if or else on the same physical line as its ( and else on the same physical line as the closing-parenthesis that precedes it. Then no eol-caret is required.
Batch simply charges on through statements. It has no concept of the end of a procedure and needs to be told when the procedure ends. This can be done with a goto :eof statement (which jumps to the physical end-of-file) or an exit /b statement (which returns from a subroutine, optionally setting errorlevel. goto :eof effectively does the smae thing in most circumstances and is way more common.)
%%~nxi means the name-and-extension of the file %%i. Of course, it's quite legal to use %%~ni and its counterpart individually, but it's not necessary. Note however that these parts should be despatched in "quotes" to the subroutine because each part may contain spaces. "quotes"make a spaces-containing-string appear as one string with spaces rather than a series of strings.
>nul redirects the move command's report "1 file(s) moved" to the bit-bucket.
Setting the two variables within checkfilename should be done after removing the quotes applied in the call - that's the purpose of the ~ before the parameter-number.
counter can be set to zero, then incremented.
If the proposed new filename exists, then simply increment the number and try again until you hit a name that doesn't exist. Yes - counter will run out eventually. It tops out at 2**31-1. Might take a while...
Note the use of quotes in the if exist and move. This is to guard against spaces in file/directorynames. The same goes for the for %%i in ("%tempdir%\*") used earlier...you may notice that in my testing, I used (deliberately) a directoryname that contained spaces. As it happens, the filenames I used also had spaces in them.
One last warning - There is no doubt that some odd filenames may choke on these procedures, but they should be few and far between. Filenames containing carets may be a problem, for instance.
Welcome to batch!
Unless this is a learning project, I recommend you study the XCOPY command.

Trying to conditionally change a file path in a windows batch script

I have a simple script that launches an XML file path in Notepad++
Unfortunately, the generated file path for some of the files is incorrect and I'm trying to compensate for it in the script (ideal fix is clearly to resolve the path issue but at the moment this is not an option).
Here is what I have, apologies for the code I'm very new to Batch Script...
set /p filePath= Enter file path:
if "%filePath%" == "1" goto xmlMenu
else if "%filePath%" == "file://path/of/file/*/*/*/*/A/*.XML"
set filePath="file://path/of/file/*/*/*/*/B/*.XML"
goto openXML
I would like the filePath variable to inherit the rest of the path from the user input but at the moment its explicitly setting the path with the wildcards. There also seems to be a problem with the way I have stated the condition as it appears to set the path to /B/*.XML regardless of the else if condition.
There are many errors, but as you say, youre new.
First, if the filepath you enter is "1" then you should arrive safely at xmlmenu.
The next line will generate an error because with an else clause,
the If-true instruction must be (parenthesised)
the ( must occur on the same physical line as the if
the sequence ) else ( must all be on the same physical line (which need not be the same as the if)
In any case, the else is redundant. If the condition is not true, the jump will not happen, hence the next statement line will be executed.
if...==... is true if the string on the left is identical to the sring on the right, hence you would need for the entered string to be absolutely identical to file://path/of/file/*/*/*/*/A/*.XML for the if to be true. The action-statment also must be on the same physical line as the if, so this is a second source of syntax errors.
the set is then executed, assigning that famous string (in quotes) to the variable filepath. And then we're off to openxml.
Note that \ is a path separator in Windows; / is a switch-specifier.
Now - if you were to explain what you want to do - perhaps enter a filename which may be somewhere in a directory structure and if it's found then assign what? to the variable filepath. One or two examples would be a good idea.
This should get a usable system going. The numeric check is incomplete, but would be adequate. I've not made it bullet-proof against a user determined to break it...
#ECHO OFF
SETLOCAL
:again
SET "filepath="
set /p filePath="Please enter filepath as yyyymmdd : "
IF NOT DEFINED filepath GOTO :EOF
if "%filePath%" == "1" goto xmlMenu
:: test to see whether entered data is numeric
SET /a "filepath=%filepath%" 2>nul
IF %filepath%==0 echo invalid entry - numerics only, please&GOTO again
IF %filepath% lss 19800100 GOTO badymd
IF %filepath% gtr 20991231 GOTO badymd
SET filepath=\path\prefix\%filepath:~0,4%\%filepath:~4,2%\%filepath:~6,2%
:: This is just to show the filepath constructed
ECHO filepath is "%filepath%"
IF NOT exist "%filepath%\A\*.xml" ECHO "%filepath%\A\*.xml" does NOT exist&GOTO again
IF NOT exist "%filepath%\B\*.xml" ECHO "%filepath%\B\*.xml" does NOT exist&GOTO again
SET "filepath=%filepath%\B\*.xml"
goto openXML
:badymd
ECHO invalid entry - 4 digits FOR year, 2 FOR month an 2 FOR day, please
GOTO again
:xmlmenu
ECHO AT xmlmenu&GOTO :eof
:openxml
ECHO AT openxml with filepath="%filepath%"&GOTO :eof
GOTO :EOF
Obviously, change the path prefix to suit.
For an input of 20140220, the path ....\2014\02\20 would be constructed. Then there's a check for and this +\B\*.xml - I can only presume both must exist to proceed to openxml.

Batch file variable filenames and editing

Fairly new to the game and have spent many hours looking for help and examples. I've had a version of this working, although I want to streamline the process by having a single input file 'computer_listc.txt'. I basically want to read in a series of folders paths (from computer_listc.txt), and use parts of the path to write further cmd expressions.
My problem is that fname and comp_id are not being set properly. I want to make them equal to T (the filename), and then edit them.
fname needs to be the filepath with the first 12 characters removed. While comp_id needs to equal the computername specified in the filepath e.g.
fname = c_modelling\model\slave1
comp_id = VOSTRO460-1
Heres the script. Can you see my error??
for /f "usebackq tokens=*" %%T in ("C:\c_modelling\Model\cal21_cal\Computer_ListC.TXT") do (
set fname = %%T
ren fname "%fname%" "C:\%fname%:~12%"
set comp_id = %%T
%comp_id% = %{comp_id:0:12}
mkdir %%T > NUL 2>&1
echo cd %fname% >\\%%T\beo_insC.cmd
echo beo >>\\%%T\beo_insC.cmd
robocopy c:\c_modelling\model\cal21_cal \\%%T\ /s /e
start cmd.exe /c psexec \\%comp_id% -c c:\c_modelling\model\cal21_cal\beo_insC.cmd
del \\%%T\beo_insC.cmd
)
Computer_ListC.txt contains the following file names: This will eventually have a long list of computer folder paths.
VOSTRO460-1\c_modelling\model\slave1
VOSTRO460-1\c_modelling\model\slave2
I'm creating a *.cmd file for use with 'psexec', as when I direct the process to a local computer, the path defaults to \system32.
Any assistance would be very helpful!!
First issue is that SPACES in string SET statements are significant. The variablename AND the value assigned will both include any spaces, so set fname = %%T will set a variable named "fname ", not "fname" - and its value will contain the leading space before the value of %%T
Next is the perpetual delayed-expansion issue. Within a block (a sequence of parenthesised statements) any %var% will be replaced by its value at PARSE time - before execution of the loop begins.
To access the RUN-TIME value, you need to execute a SETLOCAL ENABLEDELAYEDEXPANSION statement, and while delayedexpansion is invoked, !var! accesses the RUN-time variable value.
Note however that you cannot simply invoke delayedexpansion within a loop with impunity. There is a limit to the number you can have open at any one time, so you need to 'close the bracket' with an ENDLOCAL command (reaching logical End-Of-File is equivalent to an ENDLOCAL) and the downside is that ENDLOCAL restores the environment to its value when the matching SETLOCAL was invoked. For this reason, SETLOCAL ENABLEDELAYEDEXPANSION is normally executed at the start of the batch, usually after the #echo off
Next issue is
ren fname "%fname%" "C:\%fname%:~12%"
The rename command's syntax is ren sourcename newname - exactly two arguments, enclose any argument containing spaces in "rabbit's ears" Further, the destination name is a NAME only - not a path.
Then there's this:
%comp_id% = %{comp_id:0:12}
Assuming you've fixed this to remove the spaces in variablenames and that comp_id has been assigned the value fred and you've realised that you are dealing with the RUN-time value of comp_id then this would now appear as
!comp_id!=%{comp_id:0:12}
which Batch would interpret as
fred=%{comp_id:0:12}
and promptly give up.
What you are probably expecting to do is
set comp_id=!comp_id:~0,12!
that is, set comp_id to the RUN-time value of comp_id, from the first character (0) for 12 characters.

Resources