how to loop files in a folder which has space in it - batch-file

I have the following code to copy files from a list. When the path of the files which the folder name has no space, it works perfectly. But the folder names could have space sometimes, how can I improve the code to handle the folder name with space
FOR /F %%a in (find_file_list.txt) DO COPY "%%a" "C:\test\%%~nxa"
Basically, in this find_file_list.txt, there list all files with path
c:\abc\def\12345.txt
c:\abc\def\12346.txt
c:\abc\def\12347.txt
the above code copy all these three files into folder of test and works fine.
Now when find_file_list.txt change, actually the real folder name has space in it, like
c:\ab c\de f\12345.txt
c:\ab c\de f\12346.txt
c:\ab c\de f\12347.txt
the above code not working anymore...
any thoughts?

You forgot to select the delims you want to split on:
FOR /F "delims=" %%a in (find_file_list.txt) DO COPY "%%a" "C:\test\%%~nxa"
Default delimeters are whitespace, if your file contains it, it will split on the whitepace.
Simply add "delims=" to change it to take the full string.

To output each non empty line in the file, you should ensure that you stipulate either all tokens or no delimiters, (you may also need, depending upon your task, to prevent the exclusion of lines beginning with a specific character, the default For behavior is to ignore all lines which begin with a semicolon, ;).My preference in this instance is to stipulate all tokens; this, unlike stipulating nodelimiters, will remove all leading whitespace from every line, (this would prevent files carrying accidental leading spaces from being picked up).
You cannot copy a file which does not exist, you could supress any error when trying to do so by using 2>Nul, but I would consider it better practice to see if the file exists first.
When you copy a file you need provide only the destination, (you only need to provide the file name if you're changing it).
Instead of using the Copy command you could instead use the XCopy command, this has the benefit of creating the destination directory, (subject to permissions), if it doesn't already exist.
Here therefore is a batch file example:
#For /F UseBackTokens^=*EOL^= %%A In ("find_file_list.txt") Do #If Exist "%%A" XCopy "%%A" "C:\test\">Nul
…and a very slightly modified version to be run from the Command Prompt:
For /F UseBackTokens^=*EOL^= %A In ("find_file_list.txt") Do #If Exist "%A" XCopy "%A" "C:\test\">Nul

Related

how to move newest file in directory and then process the rest of the script

so i'm trying to take one file at a time out of a directory that contains 2000 files and going up and move it to a different directory to be worked on by the rest of my script. the script is below. Right now i know the gswin64 line works when it is alone and i specify it a filename but not i'm sure with the variable yet. But when i run this it will copy all the files in directory input to directory working before it runs the rest of the script. How do i make it do one at a time then process the script before copying the rest of the files?
for /F "delims=" %%I in ('dir "H:\documents\gs\input\*.*" /A-d /B /O:-D') do (move "H:\documents\gs\input\%%I" "H:\documents\gs\working"&goto filemoved)
:filemoved
Your original code would attempt to execute move "H:\documents\gs\input\*.*" "H:\documents\gs\working" which is why all of th files were moved. What you need to do is execute a directory list command with the switch parameters and supply that to the for /f. This form of dir will show names only. so you need to include the source directory name in the move command. You would also need to have the "delims=" option to ensure that filenames containing separators are processed correctly, and the /a-d directory switch to ensure that directory names are not included. Since you are sorting in reverse-date order, the newest file will be selected first. After the first file is moved, you need to abort the for loop otherwise it will continue processing the entire list, transferring every file. The easy way here is to simply goto a label on the next line.
You've also used a *.* filemask, which will process all files, regardless of extension. Since you appear to want to process only .pdf files, you should probably change that filemask to suit.
for /F "delims=" %%x in ('dir H:\documents\gs\working\*.pdf /b') do set "FileName=%%x"
Again, you need to execute a dir command if you are using a for /f. There's no apparent reason why you wouldn't use the far simpler
for %%x in (H:\documents\gs\working\*.pdf) do set "FileName=%%x"
In either case, you probably want only the name part of the file, so FileName should be set to %%~nx, not %%x.
"C:\program files\gs\gs9.20\bin\gswin64" -o H:\documents\gs\output\"%FileName%" -sDevice=pdfwrite -dFitPage -dFIXEDMEDIA H:\documents\gs\working\"%FileName%"
Unbalanced quotes - the full pathname to the executable needs to be quoted since it contains a separator. Stray spaces after \ will probably need to be removed.
It would be better imho to quote the entire filename, including drive and path rather than filename only.
del /q H:\documents\gs\working\*.*
rm is not a batch command. Note that this command will delete ALL files in H:\documents\gs\working - not just the .pdf files that you appear to be processing.
goto start
The space is required. gotostart is not an inbuilt command.

Renaming multiple files in a for loop with user input Windows batchfile

I want to create a program that can loop through multiple pdf files and have the user rename each file a unique name like so:
234324.pdf to Batch150.pdf
32154687.pdf to AdvancedPayment.pdf
and so on...
Here is my code:
setlocal EnableDelayedExpansion
echo rename pdf files
FOR %%F IN (*.pdf) DO (
set /p x=Enter:
move %%F !x!
)
endlocal
This seems to work for the first file and then when I try to rename the second one it says: The syntax of the command is incorrect..
I have tried using the rename command and haven't had much luck with it.
FOR %%F IN (*.pdf) DO (
SET "x=%%F"
set /p "x=%%F Enter: "
IF /i "!x!.pdf" neq "%%F" ren "%%F" "!x!.pdf"
)
Your code worked fine for me. Since you don't indicate what the "to" and "from" names you used were, we're reduced to guessing.
I developed the above code to perform the rename in the manner you originated. Note that it works as I expect. Using ren the "to" filename must be a filename within the directory where the "from" file resides [ie. it simply renames the file]. If you use "move" then the file can be moved to another directory if you specify that in the "to" name.
The first fix is purely cosmetic. By enclosing the variablename and prompt in the set /p in quotes, you can include a terminal space in the prompt (which I prefer), and including the %%F in the prompt shows you which file is about to be renamed.
The next fix is to quote the arguments to the ren or move. This ensures the syntax remains properly constructed in the case of eithr "to" or "from" name containing a space.
The next is to initialise x with the "from" filename. It's enclosed in quotes so any invisible trailing spaces are not included in the value assigned to x. Note that set /p does not alter the variable if Enter alone is keyed, so setting x ensures that if a file is not to be renamed, all you need do is press Enter
The next is to detect whether the "to" and "from" names are equal. ren will generate an error report if you attempt to rename a file to itself; equally, you can't move a file to itself. Hence, /i=ignore case, and only attempt the operation if the names are different.
Finally, add the .pdf to each usage of !x! in order that you don't need to key it in. Naturally, you could omit this change if you want to alter extensions or you could put .pdf into another variable and use that variable in place of the constant .pdf so that the extension being selected can be easily varied by being changed in one plaace rather than using a mass-edit. You could even use a set /p to assign the extension being processed dynamically at the start of the routine.
Note that if you rename say one.pdf to yellow.pdf then this construct is very likely to propose yellow.pdf for a rename later on. This is because the next-filename logic locates the next filename currently in the directory,then processes the loop, then locates the next filename currently in the directory, and so on.
You would need
For /f "delims=" %%F in ('dir /b/a-d "*.pdf" ') do (
to ensure that each filename is only presented once. This mechanism performs a directory scan of filenames-only in basic form, and stores the list in memory, then processes the list item-by-item and since that list is created and then processed, any subsequent alterations to the directory do not affect the list.

How to rename a file while it is being copied and copy complete

I want to create a script which should copy files from one folder to another.
Since a file can be of a large size, of up to 1000 MB, it may take few seconds
or a minute to completely copy it. While this is being happened, I want the filename in a destination folder to be prefixed with an underscore(_).
Once the file is completely copied over to a destination folder, then the _ should be removed from the filename. The purpose of doing this is to make sure that another process should not pick up the incomplete file from the destination folder.
How can we do this using a batch script? Currently my batch file is copying and moving the file to another folder but I don't know how can i prefix underscore and then remove again from the filename after the file is completely moved.
Here is my 2 line code which is copying and moving the file.
copy %1\Customer_*.xml C:\Users\ard\Documents\Folder1
move %1\Customer_*.xml %1\Archive
If this can't be done using batch script, then VB script should also work
What you want, in words:
For each affected file in C:\source:
Copy file x to C:\destination\_x
Rename file C:\destination\_x to C:\destination\x
and in Windows batch code, with a few extras:
#echo off
setlocal
set "SOURCE=%1"
set "DESTINATION=C:\Users\ard\Documents\Folder1"
for /f "usebackq delims=" %%x in (`dir /b "%SOURCE%\Customer_*.xml"`) do (
if exist "%DESTINATION%\%%x" (
echo %%x - already exists at destination
) else (
echo %%x
copy "%SOURCE%\%%x" "%DESTINATION%\_%%x" > NUL && rename "%DESTINATION%\_%%x" "%%x"
)
)
Notes:
the for loop can iterate a command's output line-wise
in our case, that command is dir /b "%SOURCE%\*.xml", i.e. the "bare" list of matching files in the source folder
usebackq means the command is delimited by backticks, which allows using double quotes inside the command itself
delims= sets the field delimiters to "none", otherwise for would split each filename into tokens if it contained spaces
> NUL suppresses the "1 file(s) copied." success messages from copy
&& means that rename is only executed if copy was successful
Removing a prefix is not trivial, but removing a suffix (new extension) is simple. So I would append a new extension like .inProgress. You can then COPY and RENAME a collection of files with two simple commands using wildcards.
But the MOVE command does not allow renaming multiple files, so a FOR loop would be required. I'll show the code, but I don't think it is really necessary in the OP's scenario. It looks to me like the source and destination folders are on the same drive, so the move should be virtually instantaneous, and there should be no need to use a temporary intermediate name.
copy "%1\Customer_*.xml" "C:\Users\ard\Documents\Folder1\*.*.inProgress"
ren "C:\Users\ard\Documents\Folder1\*.inProgress" *.
for %%F in ("%1\Customer_*.xml") do (
move "%%F" "%1\Archive\%%~nxF.inProgress"
ren "%1\Archive\%%~nxF.inProgress" *.
)
See How does the Windows RENAME command interpret wildcards? for information on why the rename works.
Note that all the copied files will show up as ".inProgress" until the entire COPY operation has completed. If you want each file to be available as soon as possible, then a FOR loop is needed for the COPY as well:
for %%F in ("%1\Customer_*.xml") do (
copy "%%F" "C:\Users\ard\Documents\Folder1\%%~nxF.inProgress"
ren "C:\Users\ard\Documents\Folder1\%%~nxF.inProgress" *.
)
My example copies from src to dest with a new name, then restores back the old name:
bn is the basename of the source file.
for %%a in (src\*.xml) do (
set bn=%%~nxa
copy %%a dest\!bn!_
rename dest\!bn!_ !bn!
)

batch file to swap out info between two files

I need help with a batch file to do the (I suppose simple) task of reading info from a master file between two "tags" and overwriting current info in other file with the info from this master file.
Here is my situation as a simple example:
I have a file called "template.htmltplt" that is my master file if you will. Then I have a bunch of other ".html" files.
I would like the batch file to:
Go through all the html files
Delete all the lines between <!--Stuff-Start--> and <!--Stuff-End-->
Copy the content between the <!--Stuff-Start--> and <!--Stuff-End--> tags in the template.htmltplt into their correct place in the other html files.
Is this even possible and if so how?!
I have NO bat script knowledge so well commented code would be awesome!
Thanks in advance for those willing to help!
Regards,
Reinhardt
#ECHO Off
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
SET "startstring=<!--Stuff-Start-->"
SET "endstring=<!--Stuff-End-->"
:: make a tempfile
:maketemp
SET "tempfile=%temp%\%random%"
IF EXIST "%tempfile%*" (GOTO maketemp) ELSE (ECHO.>"%tempfile%a")
(
SET "block="
FOR /f "tokens=1*delims=:" %%a IN ('findstr /n /r "^" q23715314.txt') DO (
IF "%%b"=="%endstring%" SET "block="
IF DEFINED block ECHO(%%b
IF "%%b"=="%startstring%" SET block=Y
)
)>"%tempfile%r"
FOR /f "delims=" %%t IN ('dir /b /a-d %sourcedir%\*.html') DO (
SET "block="
FOR /f "tokens=1*delims=:" %%a IN ('findstr /n /r "^" "%sourcedir%\%%t"') DO (
IF "%%b"=="%endstring%" SET "block="
IF NOT DEFINED block ECHO(%%b
IF "%%b"=="%startstring%" SET block=Y&TYPE "%tempfile%r"
)
)>"%destdir%\%%~nt.html"
del "%tempfile%*"
GOTO :EOF
Not hard.
I used a file named q23715314.txt containing this data for my testing:
drop <this> line
<!--Stuff-Start-->
Insert this
and this
and even <this> line after an empty line
<!--Stuff-End-->
omit this
leave this out
And test .html file:
leave <this> line
empty line retained
<!--Stuff-Start-->
Replace this
replace this too
and substitute for <this> line
<!--Stuff-End-->
keep this
retain this too
resultant new .html file:
leave <this> line
empty line retained
<!--Stuff-Start-->
Insert this
and this
and even <this> line after an empty line
<!--Stuff-End-->
keep this
retain this too
Naturally, you'd need to set your own file and directory names, and I'd advise strongly against trying to use the same directory for the source and destination. That won't work at all. And any .html source line that starts with one or more colons will have those colons stripped-out - not that many .html lines start with colons, but it needs to be said...
How it works - block by block.
The first part would seem obvious. The directories involved are defined and the target strings, too.
Next there's a create-a-tempfile routine. Simply generate a random filename and see whether there is an existing matching filename in directory %temp%. Personally, I set up temp to be c:\temp but the code is designed to use the default. So, if the random-number generator chooses 18749 then the code looks for any file 18749... in the temporary directory. If such a file exists, then choose another random number. If it doesn't create a file named 18749a in the temporary directory. This is simply a placeholder.
Next step is to extract the required lines to a temporary file. the (block of code)>filename syntax directs any data echoed to a new file in filename - which should contain the full filename of a valid file in the temporay directory; for example c:\temp\18749r.
The code within the block first sets block to empty, then reads the file q23715314.txt line-by-line, numbering each line by prefixing it with number:. This ensures that empty lines are processed as 13:, otherwise they'd be skipped. The q23715314.txt isn't significant - it can be any file containing the required template data. I simply use qSOquestionnumber.extension in order that I can keep the data in files related to the batch file I write (called qSOquestionnumber.bat) - so the many questions using file1 and file2 can be easily individually retrieved in the case of a problem. The temporary file could be any valid filename you like, if you want to have a fixed filename. Note however that filenames containing spaces and some other symbols will need to be "quoted".
Since each line is processed by the for as it it was number:linefromfile then using tokens=1*delims=: will assign the number to %%a and linefromfile to %%b.
The block processing simply matches the line that was read from the file to the start/end string defined. block was originally "set" to empty, so it is undefined. When the startstring is matched, block is assigned a value. I used Y, but any value will do.
When the for reads the next string from the file, it finds that block is now defined, so it echoes %%b to the file c:\temp\18749r. This continues until endstring is found, when block is "set" to empty again; hence it is undefined and there is no more echoing to c:\temp\18749r.
The inner for of the second block is similar, but reversed. It reproduces each line from the file selected in the outer loop until the startstring is found, then types the contents of the tempfile and waits for the endstring when it clears blockand hence turns onthe echoing again.
The outer loop simply reads dir /b /a-d for the source directory - a directory list of simply the filenames. The "delims=" assigns the entire line to %%t and hence the outer loop is for...%%t...do (innerblock)>"%destdir%\%%~nt.html" which redirects the data echoed by the inner loop to the file with the name part of %%t (%%~nt) with the destination directory specified and the extension .html
Finally, the tempfiles are deleted.
You can't do that with a batch file. Use visual basic or pearl for something like that. Closest a batch file could do to something like that is have the output from one file be used as the input for another file but that's as specific wait can get.
Lottopix.has > prevwinum.cgi

Use Dos commands to copy file and preserve date in file name

I'm having trouble trying to copy and rename a file using only dos commands. I have a file of the format myfile20130218 and want to copy and rename it to some_other_file_20130218.
I know I can use copy source dest but I'm having trouble with how to isolate the date and preserve it. I cannot guarantee that he date will be today's date so that is ruled out, the source file will always be the same name.
I can run either a series of commands or a batch script, but thing that that I am currently having trouble with, is after I find a match that I need to copy, using myfile????????, how can I now get those file names to pull the dates off them?
EDIT: for clarification I will be looking at files in a known directory, as above, I will know the format of the file name, and will only be checking a specific directory for it. The process that checks the directory is a ConnectDirect file watcher, so when a file is found matching myfile20130218 I can fire off some commands, but don't know how to check the directory and get the name of the file present.
Something like this should work:
%oldname:~-8% extracts the last 8 characters from %oldname% which are then appended to the new filename.
Update: If you can identify the file with an external program and then call the batch script with the file name
copyfile.cmd C:\path\to\myfile20130218
you could do something like this:
set oldname=%~nx1
set newname=%~dp1some_other_file_%oldname:~-8%
copy "%~f1" "%newname%"
Update 2: If you know folder and the format you could call the script with the folder
copyfile.cmd C:\folder
and do something like this:
#echo off
setlocal EnableDelayedExpansion
for /f %%f in (
'dir /b "%~f1" ^| findstr /r "myfile[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$"'
) do (
set oldname=%~f1\%%f
set newname=%~f1\my_other_name_!oldname:~-8!
copy "!oldname!" "!newname!"
)
endlocal
Edit: Script breakdown.
setlocal EnableDelayedExpansion enables variable expansion inside loops and conditionals.
for /f %%f in ('...') executes the command between the single quotes and then loops over the output of that command.
dir /b "%~f1" lists the content of the given directory (%~f1 expands to the full path of the first argument passed to the script) in simple mode (no header, no summary).
findstr /r "myfile[0-9]...[0-9]$" filters the input for strings that end with the substring "myfile" followed by 8 digits. The circumflex before the pipe (^|) escapes the pipe, because otherwise it would take precedence over the for command, which would effectively split the for command in half, resulting in an invalid command-line.
set oldname=%~f1\%%f assign the full path to a matching file to the variable oldname.
set newname=%~f1\my_other_name_!oldname:~-8! assign the full path to the new filename ("my_other_name_" followed by the trailing 8 digits from oldname) to the variable newname.
copy "!oldname!" "!newname!" I don't need to explain this, do I?

Resources