usebackq without delayedexpansion - windows

The following creates a numbered list of links from a remote directory like
1 Link!1
2 Link!2
3 Link!3
4 Link!4
5 Link!5
.
#echo off
setlocal ENABLEDELAYEDEXPANSION
megals --reload /Root/
set /p var1="enter folder name: " & megals /Root/var1
set /a c=0
FOR /F "tokens=1 usebackq" %%i in (`megals -n -e /Root/%%var1%%`) do (
set /a c=c+1
echo !c! %%i
set string[!c!]=%%i
)
set /P number=Enter number:
echo !string[%number%]!
pause
First Problem: All the links contain a ! character which gets removed by delayedexpansion, rendering the link useless. The links require the ! as it is part of the link.
Second Problem: I'm trying to integrate this into a program, and I can't use findstr because it will list the link and filename on the same line, and when the filenames contain parentheses the program crashes. So I have to use usebackq because it lets me get just the link, without needing to deal with the filenames.
Findstr will list Link!1 Filename (the whole line)
Usebackq lets me just get Link!1
I can't use Findstr because when filenames contain parentheses the program will crash, which can only be solved by delayedexpansion.
This is a follow-up post from here, which I got stuck on: (Shows the Program)
https://stackoverflow.com/questions/49564553/create-a-numbered-list-based-on-a-given-list-of-strings#=
You can see the findstr method there, and how it causes crashes when filenames contain parentheses, which can be fixed with delayedexpansion, but that removes the ! character which is essential as it is part of the link.
Edit: Seems to be working now, thanks
Working Code
#echo off
:start:
megals --reload /Root/
set /p var1="dir? " & megals /Root/%%var1%%
for /f "tokens=1,* delims=:" %%A in ('megals -n /Root/%%var1%% ^|findstr
/n "." ') do (
set Link[%%A]=%%B
Echo %%A %%B
)
setlocal DisABLEDELAYEDEXPANSION
set /a c=0
FOR /F "tokens=1 usebackq" %%i in (`megals -n -e /Root/%%var1%%`) do (
set /a c+=1
call set "string[%%c%%]=%%i"
)
set /P number="Enter number: "
FOR /F "tokens=*" %%g IN ('call echo %%string[%number%]%%') do (SET VAR2=%%g)
echo %Var2%
echo.
Megadl %VAR2% & echo. && goto :start:
pause
https://megatools.megous.com/man/megals.html#_megatools

You really should double quote your whole set commands.
Using the alternate delayed expansion type with a call:
#echo off
setlocal DisABLEDELAYEDEXPANSION
megals --reload /Root/
set /p var1="enter folder name: " & megals /Root/var1
set /a c=0
FOR /F "tokens=1 usebackq" %%i in (`megals -n -e /Root/%%var1%%`) do (
set /a c+=1
call echo %%c%% %%i
call set "string[%%c%%]=%%i"
)
set /P number=Enter number:
call echo %%string[%number%]%%
pause

The simple solution is not using delayed environment variable expansion, for example by using command CALL.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
megals.exe --reload /Root/
rem Prompt user for folder name and verify that the user has really entered
rem a folder name and remove double quotes to prevent an exit of batch file
rem execution on further processing because of an invalid command line syntax.
:EnterFolder
set "FolderName="
set /P "FolderName=Enter folder name: "
rem Has the user entered anything at all?
if not defined FolderName goto EnterFolder
rem Remove all double quotes from folder name string?
set "FolderName=%FolderName:"=%"
rem Is anything left from folder name after removing double quotes?
if not defined FolderName goto EnterFolder
megals.exe "/Root/%FolderName%"
rem Get first space/tab separated string of each line output by megals
rem assigned to an environment variable which form an array of strings.
rem Redefine end of line character from semicolon to vertical bar as it
rem is impossible that a line starts with a vertical bar. Command CALL
rem is used to double process the SET command line by Windows command
rem processor which is the alternate solution for delayed expansion.
echo/
set "Count=0"
for /F "eol=|" %%I in ('megals.exe -n -e "/Root/%FolderName%"') do (
set /A Count+=1
call echo %%Count%% %%~I
call set "string[%%Count%%]=%%~I"
)
if %Count% == 0 echo There is nothing! & goto EndBatch
echo/
rem Prompt user for number name and verify that the user has really entered
rem a decimal interpreted number which must be in range of 1-%Count%.
:EnterNumber
set "Number="
set /P "Number=Enter number (1-%Count%): "
rem Has the user entered anything at all?
if not defined Number goto EnterNumber
rem Remove all double quotes from number string?
set "Number=%Number:"=%"
rem Is anything left from number string?
if not defined Number goto EnterNumber
rem Contains the number string any non digit character?
for /F "delims=0123456789" %%I in ("%Number%") do goto EnterNumber
rem Remove all leading zeros from number string to avoid interpreting
rem the entered number as octal number on the two IF comparisons below.
:LeadingZeros
if not %Number:~0,1% == 0 goto CheckNumber
set "Number=%Number:~1%"
if defined Number goto LeadingZeros
rem The number was 0 which is less than 1.
goto EnterNumber
rem Check number in range of 1 to %Count% which requires to convert the
rem number strings on both sides of the comparison operators to signed
rem 32-bit integers by Windows command processor in background.
:CheckNumber
if %Number% GTR %Count% goto EnterNumber
if %Number% LSS 1 goto EnterNumber
rem Output the string according to entered number by forcing Windows command
rem processor again to double processing the command line because of CALL.
call echo %%string[%number%]%%
rem Restore previous environment with discarding all the
rem environment variables defined by this batch file.
:EndBatch
endlocal
pause
Another solution is using a subroutine like OutputAndSet.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
megals.exe --reload /Root/
rem Prompt user for folder name and verify that the user has really entered
rem a folder name and remove double quotes to prevent an exit of batch file
rem execution on further processing because of an invalid command line syntax.
:EnterFolder
set "FolderName="
set /P "FolderName=Enter folder name: "
rem Has the user entered anything at all?
if not defined FolderName goto EnterFolder
rem Remove all double quotes from folder name string?
set "FolderName=%FolderName:"=%"
rem Is anything left from folder name after removing double quotes?
if not defined FolderName goto EnterFolder
megals.exe "/Root/%FolderName%"
rem Get first space/tab separated string of each line output by megals
rem assigned to an environment variable which form an array of strings.
rem Redefine end of line character from semicolon to vertical bar as it
rem is impossible that a line starts with a vertical bar. Command CALL
rem is used to double process the SET command line by Windows command
rem processor which is the alternate solution for delayed expansion.
echo/
set "Count=0"
for /F "eol=|" %%I in ('megals.exe -n -e "/Root/%FolderName%"') do call :OutputAndSet "%%~I"
if %Count% == 0 echo There is nothing! & goto EndBatch
echo/
rem Prompt user for number name and verify that the user has really entered
rem a decimal interpreted number which must be in range of 1-%Count%.
:EnterNumber
set "Number="
set /P "Number=Enter number (1-%Count%): "
rem Has the user entered anything at all?
if not defined Number goto EnterNumber
rem Remove all double quotes from number string?
set "Number=%Number:"=%"
rem Is anything left from number string?
if not defined Number goto EnterNumber
rem Contains the number string any non digit character?
for /F "delims=0123456789" %%I in ("%Number%") do goto EnterNumber
rem Remove all leading zeros from number string to avoid interpreting
rem the entered number as octal number on the two IF comparisons below.
:LeadingZeros
if not %Number:~0,1% == 0 goto CheckNumber
set "Number=%Number:~1%"
if defined Number goto LeadingZeros
rem The number was 0 which is less than 1.
goto EnterNumber
:OutputAndSet
set /A Count+=1
echo %Count% %~1
set "string[%Count%]=%~1"
goto :EOF
rem Check number in range of 1 to %Count% which requires to convert the
rem number strings on both sides of the comparison operators to signed
rem 32-bit integers by Windows command processor in background.
:CheckNumber
if %Number% GTR %Count% goto EnterNumber
if %Number% LSS 1 goto EnterNumber
rem Output the string according to entered number by forcing Windows command
rem processor again to double processing the command line because of CALL.
call echo %%string[%number%]%%
rem Restore previous environment with discarding all the
rem environment variables defined by this batch file.
:EndBatch
endlocal
pause
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
rem /?
set /?
setlocal /?
See also:
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files
How to set environment variables with spaces?
Where does GOTO :EOF return to?

Related

How to create 1 to N files with a user input file name and file number?

I have a homework really important for tomorrow. I have to create a batch file.
The batch code has to ask the user to enter a name and a number. That number must be greater than 0 and less than 100.
Let's say the user enters hello and 2. Then the batch file should create the two files hello_1.txt and hello_2.txt in current directory.
The problem is that I have no idea of how to do it and it's really important. I've been in hospital for the last week. So I could not go to school and missed those lessons.
The cmd window should look similar to:
hello 2
Creation of 2 files:
hello_1.txt
hello_2.txt
Here is what I have already:
#echo off
set /p text=Enter a name
set /p number=Enter a number
set /a N=%number%
if %N% LSS 1 goto error
if %N% GTR 99 goto error
for /l %%i in (1,1,%N%) do (
mkdir %text%_%N%.txt
)
goto end
:error
echo Check your number (between 1 and 100)
:end
echo Script OK
The simple version of the batch file for this task which is not fail safe against invalid input.
#echo off
set /P "text=Enter a name: "
:PromptNumber
set /P "number=Enter a number: "
set /A N=number 2>nul
if %N% LSS 1 goto PrintError
if %N% GTR 99 goto PrintError
echo/
for /L %%I in (1,1,%N%) do echo Create "%text%_%%I.txt" 2>"%text%_%%I.txt"
echo/
pause
goto :EOF
:PrintError
echo/
echo Check your number which must be between 1 and 100.
echo/
goto PromptNumber
If environment variable number is not defined after prompting the user because the user has not entered anything at all, the value 0 is assigned to environment variable N. That happens also if the entered string is not a valid decimal, octal or hexadecimal number. The error message output to handle STDERR on invalid number string is suppressed by redirecting it to device NUL.
A FOR loop with option /L is used to create the files from 1 in incrementing steps of 1 to value of N.
The ECHO command outputs which file is created at the moment. The ECHO command has no error output because it can't fail which makes it an ideal candidate here to create the file with 0 bytes file size by redirecting STDERR of command ECHO to the file to create.
I have also a second version being much more fail safe against invalid or unexpected input. I do not recommend to present it to your teacher as it is obviously that a novice in batch file coding has not written it.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
:PromptName
set "FileName="
echo/
set /P "FileName=Enter a name: "
rem Has the user entered anything at all?
if not defined FileName goto PromptName
rem Remove all double quotes from file name string?
set "FileName=!FileName:"=!"
rem Is the file name string now an empty string?
if not defined FileName goto PromptName
rem The string can be still invalid as file name. So check on
rem first file creation if the file could be created successfully.
(set /P FileNumber=<nul >"!FileName!_1.txt") 2>nul
if not exist "!FileName!_1.txt" (
echo/
echo The string !FileName! is most likely not valid for a file name.
goto PromptName
)
:PromptNumber
set "FileNumber="
echo/
set /P "FileNumber=Enter a number in range 1 to 99: "
rem Has the user entered anything at all?
if not defined FileNumber goto PromptNumber
rem Has the file number string any other character than digits?
for /F "delims=0123456789" %%I in ("!FileNumber!") do (
echo/
echo !FileNumber! is not a valid decimal number.
goto PromptNumber
)
rem It is safe now to reference the file number consisting
rem only of digits 0-9 without usage of delayed expansion.
rem Has the file number more than two digits?
if not "%FileNumber:~2%" == "" (
echo/
echo %FileNumber% has more than two digits.
goto PromptNumber
)
rem Remove first digit of number if the number has two digits and
rem the first digit is 0 to get the number later always interpreted
rem as expected as decimal number and not as octal number.
if not "%FileNumber:~1%" == "" if "%FileNumber:~0,1%" == "0" set "FileNumber=%FileNumber:~1%"
rem The file number is now in range 0 to 99. But 0 is not allowed.
if "%FileNumber%" == "0" (
echo/
echo Number 0 is not in valid range.
goto PromptNumber
)
rem Create the remaining files. The first one was created already before.
echo/
echo Create "!FileName!_1.txt"
for /L %%I in (2,1,%FileNumber%) do echo Create "!FileName!_%%I.txt" 2>"!FileName!_%%I.txt"
echo/
endlocal
pause
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
rem /?
set /?
setlocal /?
Read also the Microsoft article about Using Command Redirection Operators and take a look on Microsoft's command-line reference as well as SS64's A-Z index of the Windows command line. DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/ explains why in both batch files echo/ is used instead of often used echo. for printing an empty line to console.

How to verify if variable contains valid filename in Windows Batch

I'm asking user to provide filename in Windows Batch, by using this command:
set /p my_filename=Enter filename for this project
How can I verify that %my_filename% contains valid Windows filename (not path) and re ask user if it does not?
EDIT
The file in question does not yet exists when running the script (it will be created by script)
#echo off
setlocal enableextensions disabledelayedexpansion
:askFile
rem Retrieve filename. On empty input ask again
set /p "my_file=Enter filename for this project: " || goto :askFile
rem Use delayed expansion to avoid problems with special characters
setlocal enabledelayedexpansion
rem Disable delimiters and end of line characters in for command
for /f delims^=^ eol^= %%a in ("!my_file!") do (
rem Cancel delayed expansion to avoid ! removal during expansion
endlocal
rem Until checked, we don't have a valid file
set "my_file="
rem Check we don't have a path, it is not a folder and the file exists
if /i "%%~a"=="%%~nxa" if not exist "%%~a\" if exist "%%~a" set "my_file=%%~nxa"
)
rem If we don't have a file name (it was not valid) ask again
if not defined my_file goto :askFile
echo Selected file is "%my_file%"
edited to adapt to comments
#echo off
setlocal enableextensions disabledelayedexpansion
:askFile
rem Retrieve filename. On empty input ask again
set /p "my_file=Enter filename for this project: " || goto :askFile
rem See Naming Files, Paths, and Namespaces
rem https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
rem NOTE: From here, we will be enabling/disabling delayed expansion
rem to avoid problems with special characters
setlocal enabledelayedexpansion
rem Ensure we do not have restricted characters in file name trying to use them as
rem delimiters and requesting the second token in the line
for /f tokens^=2^ delims^=^<^>^:^"^/^\^|^?^*^ eol^= %%y in ("[!my_file!]") do (
rem If we are here there is a second token, so, there is a special character
echo Error : Non allowed characters in file name
endlocal & goto :askFile
)
rem Check MAX_PATH (260) limitation
set "my_temp_file=!cd!\!my_file!" & if not "!my_temp_file:~260!"=="" (
echo Error : file name too long
endlocal & goto :askFile
)
rem Check path inclusion, file name correction
for /f delims^=^ eol^= %%a in ("!my_file!") do (
rem Cancel delayed expansion to avoid ! removal during expansion
endlocal
rem Until checked, we don't have a valid file
set "my_file="
rem Check we don't have a path
if /i not "%%~a"=="%%~nxa" (
echo Error : Paths are not allowed
goto :askFile
)
rem Check it is not a folder
if exist "%%~nxa\" (
echo Error : Folder with same name present
goto :askFile
)
rem ASCII 0-31 check. Check file name can be created
2>nul ( >>"%%~nxa" type nul ) || (
echo Error : File name is not valid for this file system
goto :askFile
)
rem Ensure it was not a special file name by trying to delete the newly created file
2>nul ( del /q /f /a "%%~nxa" ) || (
echo Error : Reserved file name used
goto :askFile
)
rem Everything was OK - We have a file name
set "my_file=%%~nxa"
)
echo Selected file is "%my_file%"
goto :eof
You could strip posible path with a for
Check with findstr Regular Expression for only allowed chars
Another findstr to exclude possible device names
:: Q:\Test\2017\08\20\SO_45780452.cmd
#Echo off
:loop
set "my_filename="
set /p "my_filename=Enter filename for this project: "
set "test="
for %%A in ("%my_filename%") Do Set "test=%%~nxA"
If "%test%" neq "%my_filename%" (
Echo no drive/path please
Goto :loop
)
echo:%my_filename%|findstr /i "^[0-9A-Z.-_]*$" >Nul 2>&1 ||(
Echo invalid chars
goto :loop
)
:: check possible device name and reject
echo:%my_filename%|findstr /i "^aux$ ^con$ ^com[0-9]*$ ^lpt[0-9]*$ ^nul$ ^prn$" >Nul 2>&1 && (
Echo invalid device name
goto :loop
)
:: my_filename should be a tolerable name
Very elementary filename validity control:
echo %my_filename%| findstr /R /C:"*\.*"
There could be glitches with reserved DOS-times names (CON, PRN, NUL, etc..) and/or some special characters, so maybe you could simply try to store something into file under provided name, and than check if file was created successfully. Something like:
:getfilename
set /p my_filename=Enter filename for this project
echo A>%my_filename%
if not exist %my_filename% (echo Wrong filename & goto getfilename) else (del %my_filename% >NUL)
ALL UNTESTED!! Check if working directory is empty first.

How do I get the number at the end of a string in batch?

I'm writing a batch command file that uses the local machine's hostname, and I need to extract the number at the end of the string. How do I get the number at the end of a string in batch?
INPUT:
W2008R2T001
W2008R2T002
W2008R2T003
W8_1_901
QATEST84
QATEST85
QATEST86
DESIRED OUTPUT:
001
002
003
901
84
85
86
Here is a little batch script just written by myself.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "HostName=W2008R2T001"
set /p "HostName=Enter a host name (default %HostName%): "
call :GetNumber "%HostName%"
if "%Number%" == "" (
echo This host name has no number at end.
) else (
echo Found number at end of host name is: %Number%
)
pause
endlocal
goto :EOF
:GetNumber
set "Number="
set "StringToParse=%~1"
set "Digits=0123456789"
:CheckLastChar
if "!Digits:%StringToParse:~-1%=!" EQU "%Digits%" goto:EOF
set "Number=%StringToParse:~-1%%Number%"
set "StringToParse=%StringToParse:~0,-1%"
if "%StringToParse%" NEQ "" goto CheckLastChar
goto :EOF
This batch file lets the user enter a string. For this string the subroutine GetNumber is called using delayed environment variable expansion enabled already at beginning of main batch routine to copy all digits at end of the string to parse in right order to an environment variable Number.
The main routine evaluates the value of the environment variable Number and continues processing accordingly.
For details on how this works, open a command prompt window, execute there the following commands, and read all help pages output for each command.
call /?
goto /?
if /?
set /?
I realized that the data you want is the last one separated by a certain delimiter. In your example, the delimiters are T_ and there are a maximum of 3 parts in W8_1_901 line. The Batch file below get the data you want accordingly to these rules:
#echo off
setlocal EnableDelayedExpansion
for /F "tokens=1-3 delims=T_" %%a in (test.txt) do (
for %%A in (%%a %%b %%c) do set "last=%%A"
echo !last!
)
If may be more delimiters, insert they in delims=T_ part. If may be more parts in a line, modify the "3" in tokens=1-3 part and add more letters in %%a %%b %%c part.

Redirecting contents of a file to a variable in command prompt

I have a file "file.txt" which contains the output of "dir /s /b *.c"
I want to write this whole content of file.txt in a single variable .
Any ideas?
The usual way to treat questions like this one is to reply: "What do you want this for?". However, your question is pure and simple, so here is the answer. The Batch file below not just store the contents of file.txt in a single variable, but it also later process the variable value as individual lines.
EDIT: I added the method to extract individual lines from the variable value as substrings.
#echo off
setlocal EnableDelayedExpansion
rem Create variables with LF and CR values:
set LF=^
%empty line 1/2, don't remove%
%empty line 2/2, don't remove%
for /F %%a in ('copy /Z "%~F0" NUL') do set CR=%%a
rem Store the contents of file.txt in a single variable,
rem end each line with <CR><LF> bytes
set "variable="
for /F "delims=" %%a in (file.txt) do (
set "variable=!variable!%%a!CR!!LF!"
)
rem 1- Show the contents of the variable:
echo !variable!
rem 2- Process the contents of the variable line by line
echo/
set i=0
for /F "delims=" %%a in ("!variable!") do (
set /A i+=1
echo Line !i!- %%a
)
rem Get the starting position and length of each line inside the variable
set /A i=0, lastStart=0
for /F "delims=:" %%a in (
'(cmd /V:ON /C set /P "=^!variable^!" ^& echo/^) ^<NUL ^| findstr /O "^^"'
) do (
set /A len[!i!]=%%a-lastStart-2, i+=1
set /A start[!i!]=%%a, lastStart=%%a
)
set "len[0]="
set "start[%i%]="
set /A lastLine=i-1
rem 3- Extract individual lines from the variable contents as substrings
:getNumber
echo/
set "num="
set /P "num=Enter line number (nothing to end): "
if not defined num goto end
if %num% gtr %lastLine% echo Invalid number & goto getNumber
for /F "tokens=1,2" %%i in ("!start[%num%]! !len[%num%]!") do (
echo Line %num%- !variable:~%%i,%%j!
)
goto getNumber
:end
You must note that Batch variables can only store a maximum of 8K characters.
No. But you can go through the lines one by one.
for /f "delims=" %A in (file.txt) do echo %A
Maybe say what you are trying to achieve. Knowledge of C won't help you in batch because it's contary for historical reasons.
You can use set /p:
set /p foo=<file.txt
See also this question.
Batch-Script is a very limited tool with a primitive support for multi-line variables (with specific hacks that will not work as expected for this sceneario but if you are interested see this).
The only reasonable solution is to move to a capable language, which is every else less Batch-Script.

Batch File: List Directory & File names to individual variables and display as selection menu

I use RDP on many different windows machines, and sometimes have to RDP into one, then rdp from there onto another.
I'd like to know if it is possible to create a Batch File that can read the Names of all Directories within a set path, then display them as numbered variables like a menu.
After i input my selection, it would do the same for all .rdp files in the selected directory.
Below is an example of how i could manually hardcode it for each file...but I need something that will adapt to dropping an new rdp file into a directory rather than having to manually add it each time inside the batch file, as the number of sites/pcs and names can change regularly.
:site
ECHO Location List
ECHO.
ECHO 1 NSW
ECHO 2 QLD
ECHO.
SET /p site=Enter Selection:
IF "%site%"=="1" GOTO NSW
IF "%site%"=="2" GOTO QLD
:NSW
SET dirname=C:\Machine\NSW\
ECHO Machine List
ECHO.
ECHO 1 Client01.rdp
ECHO 2 Server01.rdp
ECHO 3 Server02.rdp
ECHO.
SET /p machine0=Enter Selection:
IF "%machine0%"=="1" SET machine1=%dirname%Client01.rdp
IF "%machine0%"=="2" SET machine1=%dirname%Server01.rdp
IF "%machine0%"=="3" SET machine1=%dirname%Server02.rdp
GOTO connection
:connection
mstsc %machine1% /console
I've found several questions similar to this (such as here and here) but they all seem to be about just displaying a list and not getting them into a menu like option, also i still do not fully understand how the FOR command works.
Example of the directory structure.
C:\Batchfile.bat
C:\Machines\NSW\Client01.rdp
C:\Machines\NSW\Server01.rdp
C:\Machines\NSW\Server02.rdp
C:\Machines\QLD\Client01.rdp
C:\Machines\QLD\Client02.rdp
C:\Machines\QLD\Server01.rdp
The base directory would be set to C:\Machines then batch would store each sub-directory name to a numbered variable and echo them to the screen and prompt for selection.
Location List
1 NSW
2 QLD
Enter Selection:_
If user input 1 then then it would store each .RDP filename inside the QLD sub-directory to a numbered variable and echo them to the screen and prompt for selection.
Machine List for NSW
1 Client01.rdp
2 Server01.rdp
3 Server02.rdp
Enter Selection:_
After the user makes a selection at this point i would like to use the selected .rdp file with the mstsc command to launch an rdp session to the selected computer then loop back the beginning to allow to open a second connection at the same time.
I'd appreciate any help you could give.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /f "delims==" %%i IN ('SET s_ 2^>nul') DO SET "%%i="
SET "sourcedir=c:\sourcedir"
FOR /f "delims=" %%a IN ('dir/s/b/a-d "%sourcedir%\*.rdp"') DO (
SET s_=%%~dpa
FOR /f %%b IN ("!s_:~0,-1!") DO SET s_#%%~nb=Y&SET s_#%%~nb_%%~na=Y
)
CALL :showmenu "Location List" #
IF /i "%s_%"=="q" GOTO :EOF
SET s_site=%s_%
CALL :showmenu "Machine List for %s_site%" # %s_site%
IF /i "%s_%"=="q" GOTO :EOF
SET s_machine=%s_%
ECHO(==============
ECHO site=%s_site% machine=%s_machine%
GOTO :EOF
:showmenu
SET s_items=1
CLS
ECHO(%~1
ECHO(
FOR /f "tokens=2,3delims=_%2=" %%i IN ('set s_%2') DO (
IF "%3"=="" (
CALL :showline %%i
) ELSE (
IF "%3"=="%%i" CALL :showline %%j
)
)
ECHO(
SET "s_="
SET /p "s_=Enter Selection : "
IF NOT DEFINED s_ SET s_=Q
IF /i "%s_%"=="q" GOTO :EOF
IF DEFINED s_%s_% CALL SET s_=%%s_%s_%%%&GOTO :EOF
GOTO showmenu
:showline
SET "s_= %s_items%. "
ECHO %s_:~-4%%1
SET s_%s_items%=%1
SET /a s_items+=1
SET "s_%s_items%="
GOTO :eof
This way is self-adjusting. Unfortunately it also uses a few hieroglyphics...
The first step is to ensure that all variables with names starting s_ are removed from the environment. There's no special significance to s_ - it's just what I chose. The output of set s_ will be of the form s_whatever=something if an s_... variable exists. If none exist, the 2>nul suppresses the error message, but the > needs to be escaped by a caret (^) to tell cmd that the redirection is part of the command to be executed, not the for command. If s_whatever=something then the for /f will parse that line using = as a delimiter, hence assigning s_whatever to the nominated metavariable, %%i.
The next step is to find all of the .RDP filenames starting at the source directory. dir /s/b/a-d produces bare lines (no headers or footers) of the full filenames, suppressing any directorynames that happen to match the specified mask.
The entire filename is assigned to %%a because delims="" that is, there are no delimiters. S_ is used as a general-purpose variable and is assigned the drive and path parts of the filename in %%a. The last character of s_ is then removed (it will be a \) and the for /f %%b interprets the resultant string as though it was a filename. The variables s_#site and s_#site_machine are then set (to Y, but they could have been set to anything)
Note the use of !s_:~0,-1! which specifies to take character 0..last-1 of the run-time value of s_ - the ! specifies run-time value when SETLOCAL ENABLEDELAYEDEXPANSION is active.
The remainder of the main routine simply calls SHOWMENU twice with various parameters and assigns the value returned in s_ to the appropriate variable.
SHOWMENU sets the number of items (s_items) to 1 more than the count of items available, clears the screen and shows the menu title from the first subroutine parameter (%1) - but with the quotes suppressed (%~1) - which allows the parameter to contain spaces.
The following FOR/F tokenises the SET list for s_# (the SITE names) or s_# (the SITE+MACHINE names). Using delimiters of _ and = as well as the # or # means that for a line like s_#NSW=Y will assign NSW to %%i and a line like s_#NSW_Server01=Y assigns NSW to %%i and Server01 to %%j
The appropriate part is selected and passed to the SHOWLINE routine.
s_ is then used for user input. Forcing it to be cleared means that if the user presses just ENTER then it will remain unset - otherwise it would retain its original value.
I've arbitrarily assigned an input of Q to quit, and if there is no user input, then that quits, too. Otherwise, if the variable s_choicemade is set, then s_ is set to that value and reaching EOF returns from the subroutine. If an invalid choice is made, then s_invalidchoice will NOT be set, so the menu is re-displayed.
The SHOWLINE procedure sets s_ to spacethelinenumber.space and then the paraeeter is displayed (%1) preceded by the last 4 characters of that string. This means that if the item number exceeds 9 then the leading space wil be lopped off and the dots will be aligned. The item number is then incremented ready for the next choice.
Here is one way:
#echo off
setlocal enabledelayedexpansion
:Start
ECHO Location List
ECHO.
ECHO NSW
ECHO QLD
ECHO.
SET /p site=Enter Selection:
for /f %%a in ('dir /b/s "c:\Temp\%site%\*.rdp"') do (
set /a i+=1
echo !i! - %%~nxa
set mach[!i!]=%%~nxa
)
set /p m0=Enter Selection:
echo mstsc !mach[%m0%]! /console
set /p sel=Would you like to launch another [y/n]?
if /i "%sel%" EQU "y" Goto :start
Or
#echo off
setlocal enabledelayedexpansion
:Start
ECHO Location List
ECHO.
ECHO 1 - NSW
ECHO 2 - QLD
ECHO.
SET /p site=Enter Selection:
set i=0 & set a=0
set site[1]=NSW
set site[2]=QLD
for /f %%a in ('dir /b/s "c:\Temp\!site[%site%]!\*.rdp"') do (
set /a i+=1
echo !i! - %%~nxa
set mach[!i!]=%%~nxa
)
set /p m0=Enter Selection:
echo mstsc !mach[%m0%]! /console
set /p sel=Would you like to launch another [y/n]?
if /i "%sel%" EQU "y" Goto :start
Also, this is assuming all of your servers are 2003. Starting with 2008, I believe /console has been deprecated in lieu of /admin. If that's the case, it's easy enough to add a little more logic depending on which version of Server you're connecting to.

Resources