IF NOT EXIST against Prompting user to select a directory - batch-file

If I were to create a condition where a user selects a directory by number, how would I test that to make sure it’s valid?
In other words, if a user were prompted:
C:\Program Files
C:\Userdata
Please select a directory: 3
In this unique scenario, how can I test against that and inform the user that 3 is a wrong selection and then loop back to the sub-routine?
I’ve sort of hit a dead end as I thought the “if not exist” would work.
I’ve tried the following without success:
If %errorlevel% equ 1 (command) else (command)
If errorlevel 1 (command) else (command)
If not exist !acctDir!!userDir! (command) else (command).
This is what I have:
:dir
echo.
set c=0
For /f %%a in ('dir !acctDir! /B /A:D') do (
set /a c+=1
echo !c! %%a >>!log!
echo !c! %%a
set dir!c!=%%a
)
echo.
set /p userIn=" Please select a directory [1-!c!]: "
set userDir=!dir%userIn%!
echo ***DEBUG*** Path=[!acctDir!\!userDir!]
pause
if not exist "!acctDir!\!userDir!" (
echo.
echo That is not a valid selection. Please try again. >>!log!
echo That is not a valid selection. Please try again.
echo.
ping 127.0.0.1 -n 3 -w 1000 >nul
cls
goto dir
) else (
echo.
echo You selected !userDir! for your data retrieval. >>!log!
echo You selected !userDir! for your data retrieval.
echo.
ping 127.0.0.1 -n 3 -w 1000 >nul
goto string
)
)
When I do some DEBUG testing to echo out the path before the IF statement is ran I get:
1 Jan_14_2014_12_49_11_900PM
2 Jan_20_2014_6_56_55_953PM
Please select a directory [1-2]: 3
***DEBUG*** Path=[x:\101004357\]
Press any key to continue . . .
So it returns a path as having the account number but not the sub-directory because of obvious reasons as input 3 is nothing. However, IF NOT EXIST should be followed then, right?
I don’t think Windows knows the numbers are related to the sub-directories?
So, I need a condition stating, IF NOT EXIST any subdirectory (which I thought would be the variable !userDir!). But the !userDir! is being assigned as nothing, so this is why it’s not working. Then I think I would need to get a list of the sub-directories to run this statement against.
**This is just 1 of 12 sub-routines, so these are the correct variables.
Any idea of how I can complete this task?

I was able to resolve the issue by solving the issue if there was a NULL output. Setting the variable around brackets and setting it to equal the null output surrounded by empty brackets allows me to test against the user input.
Simply did the following:
if [!userDir!]==[] (
echo.
echo That is not a valid selection. Please try again. >>!log!
echo That is not a valid selection. Please try again.
echo.
ping 127.0.0.1 -n 3 -w 1000 >nul
cls
goto dir
) else (
echo.
echo You selected !userDir! for your data retrieval. >>!log!
echo You selected !userDir! for your data retrieval.
echo.
ping 127.0.0.1 -n 3 -w 1000 >nul
goto string
)
)

Try this:
#echo off
setlocal Enabledelayedexpansion
set c=0
set "acctDir=U:\scripts\batch"
For /f %%a in ('dir !acctDir! /B /A:D') do (
set /a c+=1
echo !c! %%a
set dir!c!=%%a
set valid=!valid! !c!
)
:StartOver
set /p userIn=" Please select a directory [1-!c!]: "
set userDir=!dir%userIn%!
REM echo valid=%valid%
for %%a in (%valid%) do (
if %userIn% NEQ %%a (
set txt=!userIn! is not a valid selection. Please try again.
) ELSE (
set txt=You selected !userDir! for your data retrieval.
Goto :out
)
)
:out
If "%txt:~0,3%" EQU "You" Echo %txt%
if "%txt:~0,3%" NEQ "You" Echo %txt% & goto :StartOver

This should work: The trailing backslash tests only for a folder and not for a filename.
If not exist "!acctDir!\!userDir!\" (command) else (command)

#ECHO OFF
SETLOCAL
SET acctdir=.
:: remove variables starting $
FOR /F "delims==" %%a In ('set $ 2^>Nul') DO SET "%%a="
SET "$alphabet=0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
FOR /f "delims=" %%a IN ('dir "%acctDir%" /B /A:D') DO CALL :setchoices "%%a"
IF NOT DEFINED $_%$alphabet:~0,1% GOTO :EOF
FOR /f "tokens=2*delims=_=" %%a IN ('set $_') DO ECHO(%%a : %%b
choice /c %$$% /m "Choose from menu "
SET /a chosen=%ERRORLEVEL%-1
CALL SET $$=%%$$:~%chosen%,1%%
CALL SET $$=%%$_%$$%%%
ECHO CD "%acctDir%\%$$%"
GOTO :EOF
:setchoices
FOR %%i IN (%$alphabet%) DO IF NOT DEFINED $_%%i (
SET "$_%%i=%~1"
SET "$$=%$$%%%i"
GOTO :EOF
)
GOTO :eof
This works - I used . as acctdir for testing. Your choice of letters and numbers for choicenames in $alphabet
Target directory just echoed - remove the echo before the cd to activate.

Related

display a variable like %%i in for statements in variable? batch

How do I use a variable like %%i in a permanent variable?
(I don't really know the correct terms, so I hope anyone can figure out what I mean)
This is the code I am using:
#echo off
color 0f
goto number
:number
title number
cls
echo number of options?
set /p num=
goto option
:option
for /l %%i in (1,1,%num%) do (
cls
echo Name nr. %%i
echo Enter a option
set /p n%%i=
echo %%i = %n%%i% >> log.txt
)
goto select
:select
cls
echo %n2%
pause >nul
the "%n2%" works for whatever you put in second, but when I try to print it into a file ( echo %%i = %n%%i% >> log.txt ) it doesn't work.
I know the "%n%%i%" is not correct, But I don't really know what to actually put there.
:option
for /l %%i in (1,1,%num%) do (
cls
echo Name nr. %%i
echo Enter an option
set /p option%%i=
)
goto select
:select
set option>log.txt
cls
echo %option2%
You may like to consider this.
The command
set option
will show every environment variable that starts option in the format option1=Gido, which is why I changed n to option (there are other variables set that start n)

How to map numbers to a variable

I have the following script but I want to map the numbers to the variable but do not know how to. So if a user enters 0, I want it to search for the event name NewUser.
#echo off
mode con:cols=40 lines=40
set /p customerID="Enter Customer ID: "
CLS
:MENU
ECHO.
ECHO ...............................................
ECHO PLEASE SELECT THE EVENT FROM THE MENU BELOW
ECHO ...............................................
ECHO.
ECHO Event Code Event Name
ECHO.
ECHO 0 NewUser
ECHO 1 ExistingUser
ECHO 2 EmailAdded
ECHO 3 AccountProblem
ECHO 4 Lost
ECHO.
SET /P event=Type the event code and then press ENTER:
IF %event%==0 set event=NewUser GOTO find
IF %M%==1 set event=ExistingUser GOTO find
IF %M%==2 set event=EmailAdded GOTO find
IF %M%==3 set event=AccountProblem GOTO find
IF %M%==4 set event=Lost GOTO find
pause
:find
pause
setlocal enableextensions disabledelayedexpansion
pause
set "sourceFolder=C:\test"
set "targetFolder=C:\test2"
pause
set "customerID=%customerID%"
set "NewUser=%Event%"
pause
for /f "delims=" %%a in ('
findstr /m /s /l /c:"%customerID%" "%sourceFolder%\*"
^| findstr /f:/ /m /l /c:"%event%"
') do (
copy "%%~fa" "%targetFolder%"
)
pause
The first version of your code was closer to correct solution than current one; you need to use two nested for /F loops as follows:
for /f "delims=" %%a in ('
findstr /I /m /s /l /c:"%customerID%" "%sourceFolder%\*.txt"
') do for /f "delims=" %%A in ('
findstr /I /m /l /c:"%event%" "%%~fa"
') do (
rem 'copy' command is merely displayed using ECHO for debugging purposes
ECHO copy "%%~fa" "%targetFolder%"
)
Next commented code snippet could help to improve the script:
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "customerID="
:customerID
set /p customerID="Enter Customer ID: "
if not defined customerID goto :customerID
REM instead of above test, you could apply next statement about `set /P`:
REM If the user does not enter anything (just presses return)
REM then the variable will be unchanged and an ERRORLEVEL will be set.
rem CLS
:MENU
ECHO.
ECHO ...............................................
ECHO PLEASE SELECT THE EVENT FROM THE MENU BELOW
ECHO ...............................................
ECHO.
ECHO Event Code Event Name
ECHO.
ECHO 0 NewUser
ECHO 1 ExistingUser
ECHO 2 EmailAdded
ECHO 3 AccountProblem
ECHO 4 Lost
ECHO.
REM SET /P M=Type the event code and then press ENTER:
choice /C 01234 /M "Type the event code"
REM 0 means that an user's reply to "Terminate batch job (Y/N)?" was not "y"
IF %ERRORLEVEL% EQU 0 echo "Ctrl-C" or "Ctrl-Break" pressed&goto :NoSearch
REM 255 means another error
IF ERRORLEVEL 255 goto :MENU
REM ERRORLEVEL number Specifies a true condition if the last program run
REM returned an exit code EQUAL TO OR GREATER THAN the number
REM specified.
IF ERRORLEVEL 1 set "event=NewUser"
IF ERRORLEVEL 2 set "event=ExistingUser"
IF ERRORLEVEL 3 set "event=EmailAdded"
IF ERRORLEVEL 4 set "event=AccountProblem"
IF ERRORLEVEL 5 set "event=Lost"
REM Note that in above code, ERRORLEVEL parameters are tested in INCREASING order
REM Used only in this special case
REM (no need of GOTO statement, 'event' variable is set correctly)
REM
REM although CHOICE /? says:
REM When you use ERRORLEVEL parameters in a batch program,
REM list them in decreasing order.
:NOTE
set "sourceFolder=D:\test"
set "targetFolder=D:\test2"
rem ??? set "customerID=%customerID%"
rem ??? set "event=%event%"
rem debugging output
echo looking for "customerID=%customerID%" "event=%event%"
for /f "delims=" %%a in ('
findstr /I /m /s /l /c:"%customerID%" "%sourceFolder%\*.txt"
') do for /f "delims=" %%A in ('
findstr /I /m /l /c:"%event%" "%%~fa"
') do (
rem 'copy' command is merely displayed using ECHO for debugging purposes
ECHO copy "%%~fa" "%targetFolder%"
)
:NoSearch
pause
Change your For loop like this and retest:
FOR /F "DELIMS=" %%A IN (
'FINDSTR/MSC:"%customerID%" "%sourceFolder%\*"^|FINDSTR/MIF:/ /C:"%event%"'
) DO COPY "%%A" "%targetFolder%"
EDIT
I have added a modified version of your script to include the new requirement of a three digit event code. Just adjust it's value in the script as necessary, (lines nineteen to twenty three).
#ECHO OFF
MODE CON:COLS=52 LINES=40
SETLOCAL ENABLEEXTENSIONS DISABLEDELAYEDEXPANSION
(SET sourceFolder=C:\test)
(SET targetFolder=C:\test2)
SET /P "customerID=Enter Customer ID: "
:MENU
CLS
ECHO=
ECHO= ..................................................
ECHO= PLEASE SELECT THE OPTION FROM THE EVENT MENU BELOW
ECHO= ..................................................
ECHO=
ECHO= Option Event Name Event Code
ECHO=
ECHO. 1. NewUser A00
ECHO. 2. ExistingUser A01
ECHO. 3. EmailAdded A02
ECHO. 4. AccountProblem A03
ECHO. 5. Lost A04
ECHO=
CHOICE /C 12345 /M "CHOOSE AN OPTION"
SET "Option=%ERRORLEVEL%"
FOR /F "TOKENS=1-4 DELIMS=. " %%A IN ('FINDSTR/BC:"ECHO. " "%~f0"'
) DO IF "%%B"=="%Option%" (SET "Name=%%C" & SET "Code=%%D")
FOR /F "DELIMS=" %%A IN (
'FINDSTR/MISC:"%customerID%" "%sourceFolder%\*"^|FINDSTR/MIF:/ /C:"%Name%"'
) DO COPY "%%A" "%targetFolder%"
PAUSE
Please make sure that %sourceFolder% is a full path and %targetFolder% already exists, (see line six).

Removing values from set in Batch

I'm trying to write a script for work that tells me which machines on a network are online and which are offline.
Currently I have it showing me online/offline status, taking the PC names from a text file as input into a set variable.
The code I have so far is:
#echo off
setlocal EnableDelayedExpansion
for /F "tokens=*" %%a in (VC.txt) do call :append %%a
:ping
for %%i in (%VC) do (
ping %%1 -n 1 >nul
call :test %%i
)
echo.
goto :ping
:test
IF %ERRORLEVEL% EQU 0 (
echo Pinging %1
) else (
echo Off %1
)
goto :eof
:append
if defined VC (
set VC=%VC% %1
) else (
set VC=%1
)
What I want to happen is once a machine comes online, to remove it from the list. Basically just only show the list of the offline machines.
Is this possible without wiping the whole set and creating it fresh?
This mod do what you want:
:test
IF %ERRORLEVEL% EQU 0 (
echo Pinging %1
REM Remove this machine from the list
set "VC=!VC: %1=!"
) else (
echo Off %1
)
goto :eof
However, you have a couple small errors in your code. This is the fixed section:
:ping
for %%i in (%VC%) do (
ping %%i -n 1 >nul
call :test %%i
)
echo.
goto :ping
A couple points unrelated to your problem. If the machine names are single words with no spaces, then the "tokens=*" part in the for command is not needed. Also, all those call's and subroutines just complicate the code. This is the way I would do this:
#echo off
setlocal EnableDelayedExpansion
set "VC="
for /F %%a in (VC.txt) do set "VC=!VC! %%a"
:ping
for %%i in (%VC%) do (
ping %%i -n 1 >nul
IF !ERRORLEVEL! EQU 0 (
echo Pinging %%i
REM Remove this machine from the list
set "VC=!VC: %%i=!"
) else (
echo Off %%i
)
)
echo/
if defined VC goto :ping
echo All machines are on line

How to find a certain word in a file without using ERRORLEVEL (batch)

I can use ERRORLEVEL, but tried and with a loop it failed.
I am writing a batch "shell."
Since I have tried and tried, I am finally asking for help.
The reason I don't want to use errorlevel is because the loop.
(FULL) SHELL
#set /p build=<"C:\Users\%username%\Desktop\WellOS2\WellOS\Build".txt
#title WellOS V.%build%
#echo off
goto boot
:register
cls
echo You are registering...
echo If this is an error press CTRL + C NOW...
pause
cls
set /p user= Enter your username:
set /p passwordreg= Enter your password:
mkdir "C:\Users\%username%\Desktop\WellOS2\Users\%user%"
mkdir "C:\Users\%username%\Desktop\WellOS2\Users\%user%\Documents"
echo %passwordreg% >"C:\Users\%username%\Desktop\WellOS2\Users\%user%\password".txt
echo 2 >"C:\Users\%username%\Desktop\WellOS2\OSfiles\bootset".txt
echo Your done.
pause
goto welloslog
:booterror
echo Sorry the boot file has an error. Check the user manual for BOOT$
pause
:boot
set /p boot=<"C:\Users\%username%\Desktop\WellOS2\OSfiles\bootset".txt
if %boot% == 1 goto register
if %boot% == 2 goto welloslog
goto booterror
cls
:ERROR
cls
echo ----------ERROR-------------------
echo %error%
pause
goto %back%
:welloslog
cls
echo Welcome to WellOS2!
echo ----------------LOGIN-------------
set /p user= Username:
if exist "C:\Users\%username%\Desktop\WellOS2\Users\%user%" goto pass
set error= Sorry that account doesn't exist.
set back=welloslog
goto welloslogerror
:pass
set /p password=<"C:\Users\%username%\Desktop\WellOS2\Users\%user%\password".txt
set /p passwordlog= Password:
if /i %passwordlog% == %password% goto wellos
set error= Sorry! wrong password.
set back= welloslog
goto error
:wellos
cls
:wellosnocls
echo --------------MAIN---------------
echo type help for help
set /p command= #:
if exist "C:\Users\%username%\Desktop\WellOS2\Programdata\%command%.sys" set type=sys
if exist "C:\Users\%username%\Desktop\WellOS2\Programdata\%command%.pro" set type=pro
if exist "C:\Users\%username%\Desktop\WellOS2\Programdata\%command%.sys" goto po
if exist "C:\Users\%username%\Desktop\WellOS2\Programdata\%command%.pro" goto po
set error= !Unreconized program/system program!
set back=wellos
goto error
:po
set lines=0
echo --------------%command%.%type%---------------
:porep
set /a lines=%lines% + 1
set /p "code="<"C:\Users\%username%\Desktop\WellOS2\Programdata\%command%.%type%\%command%.%type%-%lines%".wellcode
if "%code%"=="GOWELL" goto wellosnocls
findstr /I /L "if" "C:\Users\%username%\Desktop\WellOS2\Programdata\%command%.%type%\%command%.%type%-%lines%.wellcode"
:skip
call %code%
goto porep
::Tools
:iftl
%code%
goto porep
PROGRAM OPENER (What I am talking about, and having problems with...)
:po
set lines=0
echo --------------%command%.%type%---------------
:porep
set /a lines=%lines% + 1
set /p "code="<"C:\Users\%username%\Desktop\WellOS2\Programdata\%command%.%type%\%command%.%type%-%lines%".wellcode
if "%code%"=="GOWELL" goto wellosnocls
findstr /I /L "if" "C:\Users\%username%\Desktop\WellOS2\Programdata\%command%.%type%\%command%.%type%-%lines%.wellcode" goto iftl
:skip
call %code%
goto porep
::Tools
:iftl
%code%
goto porep
findstr "targetstring" datafilename >flagfilename
for %%a in (flagfilename) do if %%~za==0 echo not found
for %%a in (flagfilename) do if %%~za neq 0 echo found
beyond that, your question is too vague.
The following command returns all lines of a text file textfile.txt that contain the word word (remove the /I switch if you want the search to be case-sensitive):
findstr /I /L "word" "textfile.txt"
With for /F you can capture the output and test whether it is empty, as the loop does not iterate if no match is encountered:
set "FOUND="
for /F "delims=" %%F in ('
findstr /I /L "word" "textfile.txt"
') do (
set "FOUND=Yes"
)
if defined FOUND (
echo One or more matches found.
rem do something...
) else (
echo No match found.
rem do something else...
)
Type for /? and if /? in command prompt to get details about the used commands.
There is also a way to use ErrorLevel implicitly, meaning you do not have to query its value by something like %ErrorLevel%, !ErrorLevel! or if ErrorLevel, namely when using conditional command separators:
the && separator executes the following command only in case the previous one succeeded, that is, it returned an ErrorLevel of 0; (findstr returns 0 in case a match is encountered;)
the || separator executes the following command only in case the previous one failed, that is, it returned an ErrorLevel other than 0; (findstr returns a non-zero ErrorLevel in case no match is encountered;)
The following line of code demonstrates the usage:
findstr /I /L "word" "textfile.txt" && (echo One or more matches found.) || echo (No match found.)

Batch Script Substring from string

Ok, I have been playing around with this for a while and am getting no where. I need to pull the KB number out from a line.
The issue i am having is that some of the KB numbers are 6 characters and some are 7, and can't seem to find a way that will work to error detect the two.
The Two types of errors this makes is as follows
The First one should only have been displayed 6 characts so it added the extra "-" at the end.
x64 KB890830- 2012\MS12-000\WinSec-KB890830-006-P58310-Windows-KB890830-x64-V4.9.exe
While the second error shows the random "_" because it uses the first KB shown not the second.
ia64 KB_942288 2012\MS12-000\WinSec-KB_942288-007-P58312-WindowsServer2003-KB942288-v4-ia64.exe
EDIT
Batch File So Far
#ECHO OFF
SETLOCAL enableDelayedExpansion
IF EXIST Export.csv DEL Export.csv
FOR /F "tokens=*" %%I in ('dir /s /b *.*') DO CALl:Generate "%%I"
pause
:Generate
SETLOCAL
IF "%~x1" NEQ ".exe" (
If "%~x3" NEQ ".msu" (
GOTO:EOF
)
)
CALL:FindArchitecture %1
CALL:FindKB %1
CALL:PathFix %1
ECHO %Architecture%,%KB%,%FilePath%>>Export.csv
CALL:Cleanup
ENDLOCAL
GOTO:EOF
:FindArchitecture
ECHO %1 | FINDSTR "x64"
IF "%ERRORLEVEL%"=="0" (
SET Architecture=x64
SET Count+=1
)
ECHO %1 | FINDSTR "x86"
IF "%ERRORLEVEL%"=="0" (
SET Architecture=x86
SET Count+=1
)
ECHO %1 | FINDSTR "ia64"
IF "%ERRORLEVEL%"=="0" (
SET Architecture=ia64
SET Count+=1
)
IF "%Count%" GTR "1" (
SET Architecture=Error
)
SET Count=0
GOTO:EOF
:FindKB
set KBNum="%~1"
set "KBNum=!KBNum:*-KB=!"
ECHO !KBNum!|findstr /I /E /R /c:"-KB[0-9][0-9][0-9][0-9][0-9][0-9][0-9]" >nul
SET "KB=KB!KBNum:~0,7!"
IF "%KB%" NEQ "" GOTO:EOF
ECHO !KBNum!|findstr /I /E /r /c:"-KB[0-9][0-9][0-9][0-9][0-9][0-9]" >nul
SET "KB=KB!KBNum:~0,6!"
GOTO:EOF
:PathFix
set Path_to_convert=%~1
set Reference_path=%~dp0
set FilePath=!Path_to_convert:*%Reference_path%=!
GOTO:EOF
:Cleanup
SET KBNum=
SET KB=
SET Count=
SET Architecture=
set InstallerPath=
set PathRemoval=
set Path=
GOTO:EOF
OK - siginificant edit after seeing comments from Ken White and the OP.
I'm not sure if you need this, but FINDSTR with a regular expression can validate that the line has the pattern: "-KB" followed by 7 digits, followed by "-".
echo somestring|findstr /r /c:"-KB[0-9][0-9][0-9][0-9][0-9][0-9][0-9]-" >nul
Then use substitution to remove everything from the beginning through "KB-".
Then use FINDSTR to verify that the first 7 remaining characters are digits. If not, then loop back and replace up to the next "-KB", etc.
Then you just need to take the first remaining 7 characters.
#echo off
:parseVal
setlocal enableDelayedExpansion
set val="%~1"
echo !val!|findstr /r /c:"-KB[0-9][0-9][0-9][0-9][0-9][0-9][0-9]-" >nul || (
echo Invalid format - KB value not found
exit /b
)
:parseVal2
set "val=!val:*-KB=!"
echo !val!|findstr /brc:"[0-9][0-9][0-9][0-9][0-9][0-9][0-9]-" >nul || goto :parseVal2
set "val=KB!val:~0,7!"
echo val=!val!
exit /b
EDIT
I'm not sure what you did to accept the 6 digit numbers, but the following regex expressions will work with numbers of any length.
For the 1st regex: findstr /rc:"-KB[0-9]*-"
For the 2nd regex: findstr /brc:"[0-9]*-"
Then you can use either of the following to extract out the number when you don't know the length:
for /f "delims=-" %%A in ("!val!") do set "val=KB%%A"
or
set val=KB%val:-=&REM %"

Resources