Well-tried and tested Windows batch file stops at for loop since today - what could be the cause? [closed] - for-loop

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 18 days ago.
Improve this question
I have written and used a windows batch file that has been working without a problem for quite a while.
This is what it does:
The program loops through the *.pdf files of the current folder, and then performs a few tasks like extracting the text layer of the pdf file, searching for a couple of search terms and in the end it renames the pdf file according to which search term was found.
The batch file worked fine until today.
Now it just stops working at the for loop and I have no idea why.
Stripped down to the essence, the batch looks like this:
setlocal enabledelayedexpansion
for /f "delims=" %%i in ('dir /b /a-d /o-d *.pdf') do (
echo %%i
echo %%~ti
echo %%~ni
)
The echos would just be examples, but as the batch files never reaches what's after the for loop, I simplified it.
Since today, the program would just stop when it reaches the for loop.
This is what I tried to solve the problem:
examined the syntax of the for loop, found no mistakes
tried simplified variations of the for /f loop including leaving out the delims option, changing it against a usebackq option, changing the name of the loop variable, stripping down the options of the dir command, changing the file filter to *.txt
turned the for loop into a one-liner with just an echo %%i command etc.
looped through the lines of a given text file
changed the location of the batch file to another folder
tried the batch on a Windows 10 and a Windows 7 system
try again after rebooting the system
checked folder access rights, I had all of them, no restrictions
None of these helped, the batch file would always stop and close its window right at the for loop.
I also inserted the dir command before the for loop just to check the syntax - this worked with no problem, so it can't be the syntax of the dir command either.
I remember that I had this problem a few times years ago and it either disappeared by itself or I had to retype the batch word for word in a new text file copying whatever was in the original file as copy and paste wouldn't work.
Has anyone out there observed this strange behaviour and maybe found the cause and at best a solution for it? I'd be so thrilled.
EDIT: When I run the batch in the cmd window, I get a simple SYNTAX ERROR when it reaches the line with the for loop.

Try this at the command prompt:
reg query "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v AutoRun
reg query "HKEY_LOCAL_MACHINE\\Software\Microsoft\Command Processor" /v AutoRun
Both results should be empty, but probably in your case they are set.
The problem of the AutoRun file, it's started when a new cmd instance is started, but also for the FOR/F command.
If this is the problem, you should delete these entries by
reg DELETE "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v AutoRun /f
reg DELETE "HKEY_LOCAL_MACHINE\\Software\Microsoft\Command Processor" /v AutoRun /f
Or if you really need an AutoRun batch, then it should start with a guard.
#echo off
setlocal EnableDelayedExpansion
REM *** ALWAYS make a copy of the complete CMDCMDLINE, else you destroy the originial!!!
set "_ccl_=!cmdcmdline!"
REM *** The check is necessary to distinguish between a new cmd.exe instance for a user or for a "FOR /F" sub-command
if "!_ccl_:~1,-2!" == "!comspec!" (
REM ***** INTERACTIVE ****
REM *** Do your stuff ***
)
endlocal
exit /b

Related

Wait till one program is finished before starting next in Windows Script

I have to found uninstallers in all the subdirectories inside a specified directory. Once the uninstaller is found then, I need to run it.
#setlocal enabledelayedexpansion
#for /r %%i in ( un*.exe) do (
#echo Found file: %%~nxi
%%i
)
I am already able to search the subdirectories and find the uninstallers inside them using the code above.
PROBLEM: The command %%i executes the uninstaller.exe and the control immediately returns to the command prompt. Due to the immediate return of control to the command prompt, the next iteration of for-loop is executed and therefore the next uninstaller (by command %%i) is also started.
What I need: I want to stay in the current iteration of for-loop till the uninstaller started by the command %%i is finished.
PS: The uninstaller programs do not finish by themselves. They ask for a couple of options and therefore, I want to start them one by one (so that user doesn't get confused).
PS-2: As pointed out in one of the comments below that PowerShell can solve the issue so, I am looking for more suggestions from PowerShell community as well.
Presuming you want to 1) find all files named un*.exe in a particular path and its subdirectories, 2) execute each one, and 3) wait for each executable to complete before executing the next one, you can write something like this in PowerShell:
Get-ChildItem "C:\Uninstaller Path\un*.exe" -Recurse | ForEach-Object {
Start-Process $_ -Wait
}
you can use start /wait. Compare the following two lines:
for %%i in (*.txt) do notepad "%%i"
for %%i in (*.txt) do start /wait notepad "%%i"
The first one will open all matching files in several Notepads, the second one will only open the next Notepad, when you close the previous one.

Run Batch Script Across Subfolders (Recursively)

I regularly have to rename hundreds of files across a subfolder structure. I have been creating a batch file consisting of all my rename commands, and manually pasting this into each subfolder to execute one subfolder at a time. I'd like to revise the batch script so that it executes against all subfolders in one fell swoop, run from the parent directory just once.
My renaming is very manual, and so I need to create a discrete entry for each file. For example, here are three lines:
REN STWP01_00669087* BCBSRI-01849351*
REN BCBSRI-01849357* 2011-12-19_BCBSRI-01849357*
REN STWP01_00669094* BCBSRI-01849369*
I've experimented with the FOR /R command, including trying a separate batch file that calls my renaming batch file (via the CALL command). No luck.
I have to assume that this is simple, but I'm a batch novice, so any help would be appreciated.
Thanks!
#Magoo,
Thanks so much for your response. Your approach is going to be far more efficient than my own so far.
A couple of questions. Please bear with me as I am a total novice with batch commands.
Here's what I did: I saved your code to a .BAT file ("RRename.bat"), modified my filenames as per your instructions and saved those to a text file ("Filenames.txt"), and then run this command from the command line: {RRename.bat Filenames.txt}.
The resulting command windows confirm correct renaming. And so I removed the ECHO and PAUSE commands and re-ran. No luck. Just a bunch of Command windows confirming the directory.
Ideally I'd love to save this as a .BAT file and simply drop this in the top-level directory, together with the data file that contains the old names and new names of the files. And so, a double-click of "RRename.bat" will parse the content of "Filenames.txt" and work its way through all subfolders, renaming wherever matches are encountered. Boom.
To that end:
1. How do I make it so {SET "sourcedir=} indicates the current directory (i.e. the directory in which the batch file is located)? This way I wouldn't ever need to change this variable. (I should note that I am running this script on a network location, which requires me to map the drive, resulting in a different drive letter every time.)
2. How do I hard-code the name of the data file into the script itself? My goal is an easily replicated process minimizing user input (save for the content of the data file).
3. How do I stop the individual command windows from appearing? I'll be renaming thousands of files at a time and don't want to see thousands fo corresponding command windows.
Thank you!
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
:: read parameters
SET "filename1=%~1"
SET "filename2=%~2"
IF DEFINED filename2 GOTO name
IF NOT DEFINED filename1 GOTO :EOF
:: 1 parameter - must be filename
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO START /min "ren %%a" "%~f0" %%a
GOTO :eof
:: we have 2 parameters so rename pattern 1 to pattern 2
:name
FOR /r "%sourcedir%" %%a IN ("%filename1%*") DO CALL :process "%%a"
PAUSE
GOTO :EOF
:: Process the filenames and actually do the rename
:process
:: Name of file to be changed - name and extension of %1
SET "changeme=%~nx1"
:: REPLACE up-to-from-pattern with nothing = remainder of name/extension
CALL SET "endpart=%%changeme:*%filename1%=%%"
:: and RENAME...
ECHO(REN "%~1" "%filename2%%endpart%"
GOTO :eof
You would need to change the setting of sourcedir to suit your circumstances.
Revised data file
STWP01_00669087 BCBSRI-01849351
BCBSRI-01849357 2011-12-19_BCBSRI-01849357
STWP01_00669094 BCBSRI-01849369
Aimed at processing the above file, renaming files starting (column1 entries) to start (column2 entries.)
Method:
Run the batch as
batchname filename
This will execute the batch, processing filename
How:
having set the directory name to start processing from, set filename1&2 to the values of the parameters supplied.
If only 1 is supplied, it is the filename, so process it line-by-line and START a new process /min minimised "with the window name in the first set of quotes" and execute this same batch with the data from each line of the file in turn, then finish by going to :eof (end-of-file - built-in to CMD)
The sub-processes all have 2 parameters (eg BCBSRI-01849357 2011-12-19_BCBSRI-01849357) so processing passes to :name. This runs a for /r loop, from the specified source directory, with the name specified from the first column+* and executes :process passing the filenames found as parameter 1.
:process sets changeme to the filename in question, calculates endpart by removing the string filename1 from changeme which will deliver the er, end part.
Then simply rename the supplied filename to the replacement name+that endpart calculated.
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.
The PAUSE is just to allow the proposed changes to be seen. Once the process has been verified, change the PAUSE to EXIT.
AAMOI, running
*batchname* STWP01_00669094 BCBSRI-01849369
for instance, would execute the recursive-rename from STWP01_00669094* to BCBSRI-01849369*
Sadly, "No luck" is meaningless.
I have made a minor, but significant change to the instructions. The PAUSE should be changed to an EXIT after testing.
After testing, the ECHO(... line should become
REN "%~1" "%filename2%%endpart%"
which actually executes the rename. If you've just deleted the line, it would explain the no-visible-result.
Having restored the original code and verified against a small representative dummy subtree, change the echo(... line and test again. The filenames should change. If not, something is dreadfully wrong. Needless to say, this works perfectly happily for me...
Then try again with the PAUSE changed to EXIT. This time, the windows generated will appear on the taskbar and then disappear when the rename for that line of the input file has finished. This will happen once for BCBSRI-01849357 rentwo for instance - not once for each individual file rename occurring.
To hard-code the filename, remove the line
IF NOT DEFINED filename1 GOTO :EOF
and replace
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO START /min "ren %%a" "%~f0" %%a
with
FOR /f "usebackqdelims=" %%a IN ("YOURFILENAMEHERE") DO START /min "ren %%a" "%~f0" %%a
For the "run from here" command, change
SET "sourcedir=U:\sourcedir"
to
SET "sourcedir=."
. means "the current directory"
If you place thisbatchfilename.bat into any directory on your PATH then you can run the routine simply by executing thisbatchfilename.
You can display your path by typing
path
at the prompt. PATH is the sequence of directories searched by windows to find an executable if it isn't found in the current directory. To chane path, google "change path windows" - experienced batchers create a separate directory on the path for batch files. Sometimes, they name the directory "Belfry".

How to Increment a FileName in batch? (Part2)

I already tried the answer given by Mofi in my last question regarding this topic. But I changed the base name and it does not seem to work by now. If you want to see the previous question:How do I increment a filename in batch? What is wrong with this new code? It does not make a new file it just overwrites the previous made file.
:MainProcessNew
cd /D "%USERPROFILE%\Desktop"
for /F %%G in (*.json) do (
set "FileName=%%G"
set "BaseName=Device"
set "FileNumber=0"
)
:FileNameLoop
set /A FileNumber+=1
if exist "%BaseName%%FileNumber%.json" (
goto FileNameLoop
)
echo.>"%USERPROFILE%\Desktop\%BaseName%%FileNumber%.json"
I'm quite sure the batch code in question is not complete or reduced to a script code not really suitable to reproduce the problem because with Device.json existing in folder Desktop and no other Device*.json file existing, the empty line is first written to Device1.json. A file with name Device.json is never overwritten by the batch code in question because variable FileNumber has always at least the value 1.
Well, the FOR option /F is most likely wrong here as I suppose the FOR loop should search for *.json files as done without /F. Using a wildcard pattern like *.json together with option/F results in error message:
The system cannot find the file *.json.
Run in a command prompt window for /? or help for for help on syntax of this command.
It is completely unclear what is the purpose of the FOR loop because the FileName variable is not used at all. This variable should perhaps hold the name of last found *.json if there was any *.json file found at all. But that also does not make sense if not further used anywhere.
It is also unclear why BaseName and FileNumber are defined inside the loop and not outside.
In the complete batch code the label FileNameLoop is perhaps the beginning of a subroutine. But in the reduced batch code in question there is no call :FileNameLoop "%%G" which I would expect in this case.
So the question is hard to answer as it is unclear what is really the problem with posted batch code.
:MainProcessNew
cd /D "%USERPROFILE%\Desktop"
rem Useless FOR loop without /F commented out.
rem for %%G in (*.json) do set "FileName=%%G"
set "BaseName=Device"
set "FileNumber="
rem Skip searching for files with a file number
rem after Device if there is no Device.json file.
if not exist "%BaseName%.json" goto CreateFile
rem Otherwise keep Device.json as is and search for Device1.json,
rem Device2.json, ... until a Device*.json file with current number
rem is not found and use this number for next Device*.json file.
set "FileNumber=0"
:FileNameLoop
set /A FileNumber+=1
if exist "%BaseName%%FileNumber%.json" goto FileNameLoop
:CreateFile
rem Note: FileNumber is replaced in the line below by an empty
rem string if there is no Device.json in desktop folder.
echo.>"%USERPROFILE%\Desktop\%BaseName%%FileNumber%.json"
Hint: For debugging a batch file
comment out or remove all #echo off or echo off in the batch file or change off to on,
open a command prompt window,
enter "Path to batch file\BachFileName.bat" and press RETURN.
Now it can be seen in the command prompt window each line executed by Windows command processor after preprocessing each line and each command block which means after replacing all %VariableName% by the current value of the variable in current line or entire command block.
And error messages can be also seen as the command prompt window remains open after processing batch file stopped, except it contains the command exit without option /B which always terminates the current command process.

The basics of batch processing: Where should I start? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 9 years ago.
Improve this question
I would like to get into batch file processing for Windows, but I have zero experience in this area. If you can point me in a general direction, it would be greatly appreciated.
Other potential questions:
What are the risks to be avoided with batch processing?
What is the basic structure of a batch file?
Are there examples of basic types of batch files, where I can put into action?
What are some basic types of batch commands?
Bach is a very methodical and ordered programming language that runs heavily on the Windows internal CMD or runline commands you're most likely familiar with. The main use of Batch from my personal experience is to use scripts to automate mundane tasks that need to be run every time something happens, for example at user startup.
I bet you could quite easily make a basic script now just buy learning what a) every bactch file should ideally have/need and b) by recognising that by default a batch file will read from top to bottom executing one command after the previous one finishes or fails.
In answer to your specific questions:
The risks to batch processing aren't really existent if you script something properly and test it every step of the way. - I would of said that anyone can easily see your code but third party tools will convert batch to .exe files without a problem.
I've listed some example codes with a few explanations at the bottom of this answer.
Again see the bottom of my answer for some basic scripts that will work on your computer (providing it's a version of Windows).
As I mentioned above any command you can type in to a CMD window will work within a batch file, so you can use command /? within a CMD window to check parameters and the way you should write your commands. For example type in to a CMD window ipconfig /?.
Basic Batch File Structure (Save as test.bat)
#echo off
title Test Batch Script
color a
echo.
ipconfig /all
echo.
echo Above is your IP information.
pause>nul
exit
#echo off - This turns off the CMD prompt until turned on again, without this every action is displayed, on the above script remove the line or turn it on to see the effects.
title - This is quite self explanitory, this command titles the CMD window to your choosing.
color xx - color xx changes the colour of the background and text, the first value is the text and the second is the window background. - Use color /? in a CMD window to find all the possible combinations and choose one for yourself, for example color 1f
echo. - This will enter a line break in to the file itself, I use this mainly for spacing text or adding a few lines under commands so my text can be seen clearly after commands have run.
echo - This will print the line of text you're trying to say, for example echo hello will display the text hello in the window.
pause>nul - This will pause the batch script from advancing to the next command in the series. - however just using the command pause will display the text "Press any key to continue..." using pause>nul will remove this message. Not essential but personal preference really.
exit - Exit will of course close your program, however not necessary when batch scripting as the script will close when no more commands are left to run, providing that no user input is required.
I feel that I've given you a basic and common batch file using all the most common commands that you'll use nearly every time you make a batch file.
The other answers have listed a lot of resources you can use to learn more. - Batch is an extremely easy language to learn but can get tricky depending on how much user input is required or how automated you want to make something.
Get to grips with my example and replace ipconfig /all with different/multiple commands to practice using them within your scripts.
In relation to the risks inolvd with batch programming, the worst that can happen is that when working with some files on your computer your batch script may malfunction, causing the files to be either erased or ruined. That is why, it is recommended to back-up all files before testing a batc script.
The basic structure of a batch file varies quite oftenly. When you start of you will mostly rely on goto loop structures. Where your script will start of with a series of commands (normally the first one being #echo off) and then making the script goto diffferent parts of your program.
Later on you will rely on for loops and batch calling.
To find some half decent batch app's I recommend you look online as there are some sights which can contain quite a few.
For basic tutorials, I recommend you start of by making simple batch apps to do very basic things like count.
Hope this helped, Mona.
P.S. Here are some I recently uploaded some of my old batch apps on instructables, here are the links:
Naughts and crosses
Calculator + Tutorial
Encryption with 7zip
go to http://www.dostips.com/
list of basic commands:
echo hello world ::will write hello world to the screen
echo %time% :: will write the value of the variable time to the screen
cd dir ::will go into the directory named dir
type file.txt ::will print the contents of a file to the screen
dir ::will echo the contents of the current directory to the screen
cd .. ::will goto the parent directory
help :: will show a list of commands
set ::will show all current variables
if "%var%" EQU "hello" (echo is) ::will echo is if the varible var is set to hello
set var=hello ::will set var equal to hello
set /p var=how are you: ::will get input from the user and stroe it in var
for /l %%i in (1,1,5) do echo %%i ::will echo 1 to five on the screen
ping host.com ::will check if you can connect to host.con and diplay some results.
copy a.txt dir\dir2\b.txt ::will copy a.txt to the second argument.
move a.txt ..\a.txt ::will move a.txt to the parent directory.
ren a.txt b.pdf ::will rename a.txt to b.pdf
command /? ::will display help on a command
here are some sample files I made:(blank lines matter)
file1:
setlocal enabledelayedexpansion
set NLM=^
set NL=^^^%NLM%%NLM%^%NLM%%NLM%
for /f "delims=" %%i in (a.txt) do set a=!a!!nl!%%i
echo %a%
file2:(each for is a single line)
#echo off&setlocal enabledelayedexpansion
if '%1'=='' (echo missing parameter&pause>nul&exit /b)
for /l %%i in (%1,-1,0) do for /l %%j in (%%i,1,%1) do set %%i=!%%i!
for /l %%i in (0,1,%1) do (for /l %%j in (0,1,%%i) do set nums=!nums! %%j)&echo !%%i!!nums!&set nums=
for /l %%i in (%1,-1,0) do (for /l %%j in (0,1,%%i) do set nums=!nums! %%j)&echo !%%i!!nums!&set nums=
file3:
setlocal enabledelayedexpansion
for /f "delims=" %%i in (a.txt) do set a=%%i&set a=!a:""=! &echo !a!>>new.txt
file4:(for is one line)
#echo off
for /d %%i in (*) do for /f %%j in ("%%i") do (dir "%%i" /b|for /f %%k in ('find "%%j" /v') do #dir "%%k" /b /s|find "thumbs.db" /v)
email me if you have questions. I know batch better than any other language and love it. my email is: 12nephi12#gmail.com
have fun!!! ;)

Batch or VB Script to copy logs from multi directories to one directory and rename to avoid conflicts

Hope you can help. I have been trying to resolve this for a week but not getting anywhere and can't quite piece together what I need! - My scripting skills are far from great so please forgive my naivety!
Ok, The Problem......
I have an IIS server that has multiple sites that all save their logs in a separate directory, I need to copy the logs from the last 24 hours to a local directory on my machine so I can analyse these in Log Parser Lizard (GUI Version) on a daily basis.
I can map a drive from the remote server to my local machine via a hardware VPN, so this makes things a bit easier. Using the forfiles command I can re-curse the directories to find the logs that are only a day old, and using either copy/xcopy/ or Robocopy I can set a command to copy. My problem is that the IIS logs all have the same name so my copy command just keeps overwriting the previous file, rather than creating a new file. I have tried using the %random% parameter for the file name, but this again creates one random file that is overwritten with the next file, keeping the same name instead of creating lots of randomly named files in one directory.
I know that Log Parser commands include recurse, which I have used successfully, however the format of the log is changed slightly and the GUI Lizard cannot read the data within, so this is not a solution.
My code as it stands at this time is shown below, with IP's changed for obvious reasons. Any help would be greatly appreciated!
#echo off
NET USE Q: /Delete /yes
NET USE Q: \255.255.255.255\D$\Logs
cd C:
RD /S /Q C:\Weblogs\Production
MD C:\Weblogs\Production
forfiles.exe /p Q:\ /s /m *.log /d 0 /c "cmd /c robocopy /S /XC /XN /XO #file C:\Weblogs\Production\%random%.log"
NET USE Q: /delete
exit
%RANDOM% does not work for you in this case because it does not get solved per each iteration but only once at the forfiles invocation.
You'll need either to use in FORFILES some unique identifier, maybe concatenating #RELPATH and #FNAME may work for you in case you have only one level deep recursion.
Or either replace FORFILES with a FOR loop. Inside the loop you may have more freedom to calculate a unique ID, maybe a simple counter might work for you.
Edit: see this simple code sample, to get you started
#ECHO off
SETLOCAL enabledelayedexpansion
SET destdir=C:\LOGS
SET count=%RANDOM%
FOR /R %%A IN (*.log) DO (
SET /A count += 1
#ECHO COPY %%A !destdir!\%%~nA.!count!.log
)

Resources