How to use findstr within a loop? - batch-file

I am trying to read the filename and according to the filename set the Output variable.
I have tried using findstr directly on %%F (findstr /i "M002" %%F >nul 2>&1 ) and also writing to a temp text file (as below) to test and read it, but nothing worked.
What I'm doing wrong?
P.S. If I remove this out from the loop the code works, but I need it within the loop due to the last line.
rem ===========================================
set FileType=pdf
for %%F in (%~dp0*.%FileType%) do (
rem ECHO %%F
echo "%%F" > test.txt
findstr /i "M002" test.txt >nul 2>&1
echo %errorlevel%
if not %errorlevel% == 0 (
echo "4pp"
echo %%F
set Output=4PP
) ELSE (
echo "Letter"
echo %%F
set Output=Letter
)
set NetComm="XYZ" "%Output%" etc etc etc
)
rem ====================================

Generation 5,961 of delayedexpansion.
Batch parses the entire statement from the for through to the last closing parenthesis and replaces any %var% with the value of that variable at the time the statement is parsed.
Consequently, attempting to use a value which is established within the loop will fail. This applies to %output% and %errorlevel% in the current instance.
Dealing with %errorlevel% is easy. The syntax
if errorlevel n
works on the run-time value of errorlevel and is true if errorlevel has been set to n or greater than n.
Personally, I find the if not condition ... else clumsy. I can't see why it's so common. Fuzzy thinking in my book...
There are three common ways to overcome the problem, each has its own advantages and disadvantages, proponents and critics.
First, the "documented" method. Use a setlocal enabledelayedexpansion instruction. Once this instruction has been executed, !var! will access the current value and %var% the initialvalue ofvar`.
Second, the subroutine method. CALL :sub within a loop executes a subroutine (see the documentation - call /? from the prompt) and within that subroutine, %var% will have the value as established within the loop.
Third, it's sometimes possible to use call echo %%var%% (or call someotherinsruction) where the call is executiong the target as if it was a subroutine.
Hence, in your case, a fix might be
rem ===========================================
set FileType=pdf
for %%F in (%~dp0*.%FileType%) do (
rem ECHO %%F
findstr /i "M002" "%%F" >nul 2>nul
CALL echo %%errorlevel%%
if errorlevel 1 (
echo "4pp"
echo %%F
set Output=4PP
) ELSE (
echo "Letter"
echo %%F
set Output=Letter
)
CALL set NetComm="XYZ" "%%Output%%" etc etc etc
)
rem ====================================
depending entirely on your definition of "works" (which is not an absolute - it has meaning only to you.)

Related

If a variable gets set to the name of a variable, then can it become that variable? (Batch)

All the P(numbers) are paths. For example, %P1% might be D:\ . Then, I get them to say a number. If they choose 1, I combine that with a P. That gives P1, the first variable. Can I set A to the variable P1? It would make the code way more optimized. I want to try and fit everything in a ridiculously tiny amount of space.
Download: https://mega.nz/file/QYZA3RbQ#Uvr9acXEBssYdXSHkdas1OjFt0X6xdmyl1WLH9HbmP8
echo 1. %P1%
echo 2. %P2%
echo 3. %P3%
echo 4. %P4%
echo 5. %P5%
set /p Pc=
set "a=p%pc%"
echo %a%
pause```
Short answer, based on the initial code:
Using delayedexpansion
set "a=!P%Pc%!"
echo %a%
or use call
call set "a=%%P%Pc%%%"
echo %a%
However. to do this right, You will need to change the way your config file works.
For now, your config has a major flaw, you explicitly want to ignore certain line numbers, which if someone removes or add a line, it will break your code. You need to use some for loops and then skip lines that starts with REM
So the config needs to look something like this (I made it smaller for the sake of a shorter answer):
REM Please set this to 1, or the program will NOT run!
1
REM ------------------------------------------------------------------------------------------
REM Please put paths to the folder your [PWADS] are in. Not the pwads themselves.
REM ------------------------------------------------------------------------------------------
pwads=C:\Some path
pwads=D:\Another
REM ------------------------------------------------------------------------------------------
REM Please put the path to your [IWADS]. Only ten though! Make sure it's the iwads themselves.
REM ------------------------------------------------------------------------------------------
iwads=D:\iwads1
iwads=D:\some path2
REM ------------------------------------------------------------------------------------------
REM Please put the path to your [Source Port] exe's. You must be doing something weird if you have more than ten.
REM ------------------------------------------------------------------------------------------
sp=D:\
sp=E:\
REM ------------------------------------------------------------------------------------------
REM Now, please leave this alone. It's so I know everything works.
REM ------------------------------------------------------------------------------------------
1
Now the code. portion.
#echo off
setlocal enabledelayedexpansion
set pwnum=0
set iwnum=0
set spnum=0
for /f "tokens=1,* delims==" %%i in ('type launcherconfiguration.txt ^| findstr /v "REM "') do (
if "%%i" equ "0" (
echo not configured
goto :eof
)
if "%%~i" == "pwads" (
set /a pwnum+=1
set "PWADlocat!pwnum!=%%~j"
) else (
if "%%~i" == "iwads" (
set /a iwnum+=1
set "IWADlocat!iwnum!=%%~j"
) else (
if "%%~i" == "sp" (
set /a spnum+=1
set "SPlocat!spnum!=%%~j"
)
)
)
)
echo PWADLocation:
for /l %%a in (1,1,%pwnum%) do echo %%a. !PWADLocat%%a!
set /p pw=Choose:
call echo you chose %%PWADlocat!pw!%%
call set "PW=%%PWADlocat!sp!%%"
echo you chose %PW%
pushd "%PW%" rem This will goto the dir
rem do other things
popd
echo IWADLocation:
for /l %%a in (1,1,%iwnum%) do echo %%a. !IWADLocat%%a!
set /p pw=Choose:
call set "IW=%%IWADlocat!sp!%%"
echo you chose %IW%
pushd "%IW%" rem This will goto chosen the dir
rem do other things
popd
echo SPLocation:
for /l %%a in (1,1,%spnum%) do echo %%a. !SPLocat%%a!
set /p sp=Choose:
call set "SP=%%SPlocat!sp!%%"
echo you chose %SP%
pushd "%SP%" rem This will goto the chosen SP dir
rem do other things
popd
So let me explain it briefly. We set a number per datatype, we simply increase the count as more lines matched are found and then we use another for /l loop to just use the count again.
To test this, you need to copy this code into a new batch file, place it in the same location as your config file and test it. There are alot more to your code, but I have only focused on what you asked.
Note, I would still prefer to use choice but because of the beeping you mentioned, I retained the set /p option in this code.

check for spaces in file name inside of for loop in batch file

I have a batch script that is calling a VBscript file. It reiterates through all files in a watched folder.
It needs to verify if the file name has spaces in it and if so reject the file and not process it with the VBS.
I must have an error on the code as I get the error message:
ELSE was not expected at this time.
I have looked at the documentation and searched for the answer for quite some time, including this question: check "IF" condition inside FOR loop (batch/cmd)
But still, I can't see what is wrong in my syntax:
#ECHO OFF
setlocal ENABLEDELAYEDEXPANSION
call :ReadIni Infolder inFolder
call :ReadIni Outfolder outFolder
echo %inFolder%
echo %outFolder%
pause
:StartLoop
FOR %%I in (%inFolder%\*.srt) DO (
ECHO %%I
ECHO %%~nxI
SET TESTNAME=%%~nxI
ECHO !TESTNAME!
ECHO !TESTNAME: =_!
PAUSE
IF NOT !TESTNAME!==!TESTNAME: =_! (
move "%~sdp0%%~nxI" "%outFolder%\ERROR_IN_FILE_NAME_%%~nxI"
) ELSE (
copy /y "%%I" "%~sdp0%%~nxI"
%~sdp0SRToffset.vbs "%~sdp0%%~nxI" "%~sdp0%%~nxI"
IF %ERRORLEVEL%==1 (
Goto StartLoop
) else (
move "%~sdp0%%~nxI" "%outFolder%\"
move "%~sdp0QC_%%~nxI" "%outFolder%\"
del "%%I"
)
)
)
timeout /t 1
goto StartLoop
:ReadIni
FOR /F "tokens=2 delims==" %%a in ('find "%~1=" config.ini') do set %~2=%%a
exit /b
Any help would be appreciated.
IF NOT "!TESTNAME!"=="!TESTNAME: =_!" (
...
IF %ERRORLEVEL%==1 (
Quoting the strings causes cmd to regard the string as a single entity.
Note that the following if %errorlevel% will be executed using the value of errorlevel at :startloop. (See delayed expansion for reasoning.)
Cure by using if !errorlevel!==1 (. (Using the runtime value of errorlevel as established by the vbs routine.)

Delayed expansion and exclamation marks in strings

Ok, so I'm still pretty new to batch scripting and I have this problem with my code that I'm using setlocal enabledelayedexpansion in my code for the for loop, the For loop goes through folder names in a specific directory, but the problem is that some of the names may include "!"(exclamation marks) and if that is the case they are not counted for in the "!filename!" and when the code creates a new directory it does not include the "!". Also when the xcopy tries to copy the files from the original folder, the folder is not found, because the variable "!filename!" is not the same as the original(does not have the exclamation point).
So I found that for this I need to only add "setlocal enable delayed expansion" to some parts of the code and turn it off at other, but I cant seem to find the right places.
The code:
#ECHO OFF
setlocal enabledelayedexpansion
SET Location_Folder=V:
SET Destination_folder=V:\Nonimportable
SET Check_file_validation=V:\Nonimportable\result.txt
SET Excluded_folder=Nonimportable
set "lineNr=12"
For /f "tokens=*" %%O in ('dir /b /a:d "%Location_Folder%"') do (
set filename=%%O
call D:\somefolder\otherfolder\batscriptname.bat !filename!
set Validation=
echo !filename!| FINDSTR /i /c:"%Excluded_folder%" >NUL
IF ERRORLEVEL 1 (
for /F "skip=12 delims=" %%a in (%Check_file_validation%) do if not defined Validation (
set Validation=%%a
call :valid
)
) else (
echo This folder name is excluded: !filename!
)
)
goto Finish
:valid
echo !Validation!| FINDSTR /c:"1" >NUL
if ERRORLEVEL 1 (
set Folder_path=%Location_Folder%\!filename!
set New_Folder_path=%Destination_folder%\!filename!
mkdir "!New_Folder_path!"
echo D | xcopy /o /y /q /s /v "!Folder_path!" "!New_Folder_path!"
rmdir /s /q "!Folder_path!"
) else (
echo Folder is valid !filename!
goto Finish
)
:Finish
exit /b
The Call part calls another small (~5lines) batch file that checks the sqlplus server if the "!filename!" is valid
EDIT: The whole code works fine and does what it should, unless there is a "!" in the name of some folder.
The problem is the parameter expansion in set filename=%%O.
In %%O is still the exclamation mark, but when delayed expansion is enabled, the bangs are dropped.
The conclusion is simple, delayed expansion have to be disabled when you expand a FOR parameter.
But when you also need delayed expansion?
You simply toggle the mode.
setlocal DisableDelayedExpansion
For /f "tokens=*" %%O in ('dir /b /a:d "%Location_Folder%"') do (
set "filename=%%O" -- Here the DelayedExpansion have to be disabled
setlocal EnableDelayedExpansion
call D:\somefolder\otherfolder\batscriptname.bat filename
set "Validation="
...
endlocal
)
See also my modification of the CALL myBat.bat filename instead of CALL myBat.bat !filename!.
You shouldn't use content with CALL, better use a variable by reference and in your function take the content by
set "_filename=!%1!"
It's because CALL itself has some nasty behaviour with spaces, carets, etc
If you use a variable within a code block (parenthesised series of commands) then %var% will yield the value of the variable when the block is originally encountered (ie parse-time value) and !var! the value of the variable as it changes during the block (ie "run-time" value).
If you call a procedure - internal or external, then the values of the variables that the procedure sees are the run-time values from the parent. If these values are changed within the called procedure then the same rules apply, and the changed values are returned to the parent procedure.
However if you invoke setlocal then any value-variation made is reverted to its original value if you execute an endlocal instruction or reach end-of-file within the context of the setlocal.
OK - so that's how delayedexpansion works.
In your case, there is no need for delayedexpansion at all. In the loop in the mainline (%%O) you can use %%O in place of !filename!. In the :valid procedure, you can move the two set commands outside of the code block and then there's no need at all to use !vars! since no access is required to variables whose values change within blocks.
#ECHO OFF
setlocal
SET Location_Folder=V:
SET Destination_folder=V:\Nonimportable
SET Check_file_validation=V:\Nonimportable\result.txt
SET Excluded_folder=Nonimportable
set "lineNr=12"
For /f "tokens=*" %%O in ('dir /b /a:d "%Location_Folder%"') do (
set filename=%%O
call D:\somefolder\otherfolder\batscriptname.bat %%O
set Validation=
echo %%O| FINDSTR /i /c:"%Excluded_folder%" >NUL
IF ERRORLEVEL 1 (
for /F "skip=12 delims=" %%a in (%Check_file_validation%) do if not defined Validation (
set Validation=%%a
call :valid
)
) else (
echo This folder name is excluded: %%O
)
)
goto Finish
:valid
set Folder_path=%Location_Folder%\%filename%
set New_Folder_path=%Destination_folder%\%filename%
echo %Validation%| FINDSTR /c:"1" >NUL
if ERRORLEVEL 1 (
mkdir "%New_Folder_path%"
echo D | xcopy /o /y /q /s /v "%Folder_path%" "%New_Folder_path%"
rmdir /s /q "%Folder_path%"
) else (
echo Folder is valid %filename%
rem redundant instruction : goto Finish
)
:Finish
exit /b

Batch file cannot re-assign value in loop

I want to make the batch file that checking the account information.
Within below codes, I need 2,3,4 line from "net accounts" command outputs, and each line needs the individual process.
For to do this, I use the 'cnt' variable, and increase to checking.
However, 'cnt' doesn't increase at #1.
Why this happened, and how to solve it?
#echo off
SETLOCAL EnableDelayedExpansion
net accounts > accountInfo.txt
set cnt=0
echo """"""""""""""""""""""""""
for /f "tokens=1* delims=:" %%G in (accountInfo.txt) do (
ECHO.%%G | FIND /I "mum">Nul && (
SET var=%%~nH
Set myvar=!var!
set myvar=!myvar: =!
echo %%G !myvar!
echo %cnt%
set /A cnt+=1 ---------- here! #1
) || ( ECHO. )
)
echo """"""""""""""""""""""""""
ENDLOCAL
pause
You are using DelayedExpansion everywhere else in your code. Why not there as well?
Just change echo %cnt% to echo !cnt! and you should be fine.
Reason for that is that in batch blocks of code beeing surrounded by parenthesis are calculated at once. To see the updated value use DelayedExpansion as said above.

Batch %errorlevel% returns 0 in FOR loop

I am trying to make a loop that will go through a set of text files in specified directory searching for a string. The result is reported based on whether the string is found. But the %errorlevel% always returns 0 and evaluates to 0.
SETLOCAL enabledelayedexpansion
FOR %%G IN (*.txt) DO (
find /i "My text string" "%%G"
ECHO %date% %time% : errorlevel is %errorlevel% >> %report_dir%\%computername%.txt
IF %errorlevel% EQU 1 (
ECHO %date% %time% : String found >> %report_dir%\%computername%.txt
GOTO:copy_log
)
)
ENDLOCAL
Raymond did you mean that?:
SETLOCAL enabledelayedexpansion
FOR %%G IN (*.txt) DO (
find /i "My text string" "%%G"
IF %errorlevel% (
ECHO %date% %time% : String found >> %report_dir%\%computername%.txt
GOTO:copy_log
)
)
ENDLOCAL
%ERRORLEVEL% is being expanded too soon. You can just avoid the problem entirely by using:
IF ERRORLEVEL 1
Or for further details, see this explanation of "delayed environment variable expansion" from the SET /? help text:
Finally, support for delayed environment variable expansion has been
added. This support is always disabled by default, but may be
enabled/disabled via the /V command line switch to CMD.EXE. See CMD /?
Delayed environment variable expansion is useful for getting around
the limitations of the current expansion which happens when a line of
text is read, not when it is executed. The following example
demonstrates the problem with immediate variable expansion:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" #echo If you see this, it worked
)
would never display the message, since the %VAR% in BOTH IF statements
is substituted when the first IF statement is read, since it logically
includes the body of the IF, which is a compound statement. So the IF
inside the compound statement is really comparing "before" with
"after" which will never be equal. Similarly, the following example
will not work as expected:
set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%
in that it will NOT build up a list of files in the current directory,
but instead will just set the LIST variable to the last file found.
Again, this is because the %LIST% is expanded just once when the FOR
statement is read, and at that time the LIST variable is empty. So the
actual FOR loop we are executing is:
for %i in (*) do set LIST= %i
which just keeps setting LIST to the last file found.
Delayed environment variable expansion allows you to use a different
character (the exclamation mark) to expand environment variables at
execution time. If delayed variable expansion is enabled, the above
examples could be written as follows to work as intended:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" #echo If you see this, it worked
)
Again, this is because the %LIST% is expanded just once when the FOR
statement is read, and at that time the LIST variable is empty. So the
actual FOR loop we are executing is:
for %i in (*) do set LIST= %i
which just keeps setting LIST to the last file found.
Delayed environment variable expansion allows you to use a different
character (the exclamation mark) to expand environment variables at
execution time. If delayed variable expansion is enabled, the above
examples could be written as follows to work as intended:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" #echo If you see this, it worked
)
set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%
I prefer to make For loops call branches. It prevents variable expansion problems:
For %%G In (*.txt) Do Call :ScanFile "%%G"
Exit /B
:ScanFile
Find /i "My text string" "%~1"
If %ErrorLevel%==1 (
Echo %date% %time% : String found >> %report_dir%\%computername%.txt
Goto :CopyLog
)
Exit /B
:CopyLog
...
Exit /B
As Raymond says you're evaluating %ERRORLEVEL% for the echo, which will almost always ( never say never ) return 0.
Something along the lines of the following will do better:
FOR %%G IN (*.txt) DO (
find /i "My text string" "%%G"
SET error = %errorlevel%
ECHO %date% %time% : errorlevel is %errorl% >> %report_dir%\%computername%.txt
IF %error% EQU 1 (
ECHO %date% %time% : String found >> %report_dir%\%computername%.txt
GOTO:copy_log
)
)
I had the same problem while looping through registry keys/values, and came to this post. My "reg query" always had ERRORLEVEL 0 in a for loop.
Here's my solution:
for %%s in (%ToBeUninstalled%) do (
REG QUERY "%KEY64%\%%s" | find /i "UninstallString" > NUL && (msiexec.exe /x %%s /qb) || (echo Software not installed)
)
KEY64 being HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall

Resources