Batch File to list folders and allow user selection

…衆ロ難τιáo~ 提交于 2021-02-08 08:21:35

问题


I have searched and searched to find a solution to (what feels like) a unique problem. Many answers here have been quite helpful and have gotten me a long way, but the last bit has me stumped.

  • My batch file needs to open an executable in a folder with a variable name
  • This folder may be in one of two directories
  • This is what I originally had and it works

    @ECHO OFF
    SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
    
    SET DIR1="%USERPROFILE%\AppData\Local\Application Workspace\Profiles"
    SET DIR2=C:\Application\SRW\Profiles
    
    IF NOT EXIST %DIR1% (
       GOTO PROFILE_2
     ) ELSE (
       GOTO PROFILE_1
     )
    
    :PROFILE_1
    for /f "delims=" %%A in ('dir %DIR1% /ad /b') do (
         set foldername=%%~nxA
         )
    SET DIR1=%DIR1:"=%
    SET DIR=%DIR1%\%foldername%\app.exe
    GOTO START_APP
    
    :PROFILE_2
    for /f "delims=" %%A in ('dir %DIR2% /ad /b') do (
         set foldername=%%~nxA
         )
    SET DIR=%DIR2%\%foldername%\app.exe
    GOTO START_APP
    
    :START_APP
    START "" /b "%DIR%"
    
    :EOF
    ENDLOCAL
    EXIT
    

Then I was thrown a curveball when I discovered that some users may have multiple profiles in the variable profile folder and they need to be able to select which one to use for that given task. Now I have a variable profile folder and either one or multiple variable profiles within that folder.

I have found code to list the folder names and display them in the command window for selection.

@echo off
cls
setlocal EnableDelayedExpansion

set /a count=0

for /d %%d in (*) do (
    set /a count+=1
    @echo !count!. %%d 
)
setlocal DisableDelayedExpansion

set /P selection="select folder number:"

I also found this routine which allows the user to select a file from a list then it is supposed to translate that file name into a variable.

Batch Script Programming -- How to allow a user to select a file by number from a list of files in a folder?

Unfortunately, I cannot get the example in the link to work as is and I have no idea how to make it work with folder names as the folder name example and the file name example are close but not close enough for me to understand what to do. And even if it somehow does manage to work, how then can I make such a routine work within the original code posted above?

In addition, I really don't want the user to be forced to make a folder selection if there is only one folder. If only one folder exists, it should be placed into the folder name variable automatically and nothing displays to the user at all.

Is what I'm trying to do even possible at all?

Any help is most appreciated.


回答1:


@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION 
::
:: Set count-of-targets and application name
::
SET /a targets=0
SET appname=app.exe
SET "dots=..."
::
:: clear all "$" variables
::
FOR /f "delims==" %%i IN ('set $ 2^>nul') DO SET "%%i="
::
:: look for app.exe in (1) userprofile... (2) \application\srw...
::
SET dir1="%USERPROFILE%\AppData\Local\Application Workspace\Profiles"
SET dir2="C:\Application\SRW\Profiles"
:: My testing - my directory names
SET dir1="c:\sourcedir"
SET dir2="C:\destdir\test dir"
FOR %%s IN (%dir1% %dir2%) DO (
 FOR /f "delims=" %%i IN ('dir /s /b "%%~s\%appname%"') DO ( CALL :process "%%~dpi"
 )
)
::
:: Now have TARGETS=#hits; $_n=full pathname; $n=directoryname
::
IF %targets%==1 SET mychoice=1&GOTO chosen
::
:: repeat a menu
::
:again
CLS
FOR /l %%i IN (1,1,%targets%) DO (
 IF %%i lss 10 (ECHO(%%i%dots%%dots:~0,1%!$%%i!) ELSE (ECHO(%%i%dots%!$%%i!)
)
SET mychoice=
SET /p mychoice="Please choose 1..%targets% : "
IF NOT DEFINED $_%mychoice% GOTO again
:chosen

CALL SET application="%%$_%mychoice%%%%appname%"

ECHO START "" /b %application%

GOTO :EOF
::
:: Parameter is quoted-full-pathname
::
:process
SET /a targets+=1
SET $_%targets%=%~1
SET $=%~1
FOR %%n IN ("%$:~0,-1%") DO SET $%targets%=%%~nxn
GOTO :eof

From what you've said, this should work.

First steps are to set up. Set the number of targets found and the real application name and clear out any variablenames that start "$".

Next step is to specify the directories to use. I simply copied yours, then overrode those settings with settings to suit my machine - you'd need to delete those overriding lines, of course. Note that the names should be set quoted...

Next, scan from each of the directories for the application. Each time an instance is found, call :process passing the full -terminated pathname.

:process increments the count of targets found and sets the variable $_n to the full pathname passed (dequoted) It then sets $ to the dequoted-full-pathname and uses a syntax-trick to have FOR find the application's immediate directory. %$:~0,-1% is the full pathname minus the last character - the \ so the results looks like a filename. That "filename" is then assigned to $n

Once the directory-scans are finished, %targets% will contain the er, number of targets. If that's 1, then we have all we need - we can only select target number 1, so that's chosen for us.

Otherwise, clear the screen and display a number of lines - %targets% to be precise. The format isn...immediatedirname` and an extra dot is added if the number of targets is less than 10 so that a long list will line up nicely. Then ask for a line number.

At this point, the only $_ variables that exist are $_1..$_%targets% so if $_%mychoice% exists, it must be a valid choice. If it's not defined, then repeat the question...

CALLing SET application="%%$_%mychoice%%%%appname%" first parses the command, then parses it again. After the first parse, the line becomes (if mychoice=3) SET application="%$_3%app.exe" so on the second occasion,application` is properly set to the full fulename of the application - and quoted.

There wasn't much point in my starting the app, since it doesn't exist, so I just echoed it.


Given the extended requirement:

You's probably be starting this from a shortcut. Suppose you run that shortcut minimised and insert after the SETLOCAL

SET "poweruser=%1"

Add, after

IF %targets%==1 SET mychoice=1&GOTO chosen

the line

IF NOT DEFINED poweruser START "%username% - poweruser" "%~dpnx0" poweruser&GOTO :EOF 

And change the

GOTO :EOF

just prior to the label :process to

EXIT

So that if it wasn't run in poweruser mode (therefore poweruser is NOT defined) AND %targets% is NOT 1 (I'm presuming it won't be 0) Then this user hasn't been set as a poweruser in the shortcut, but does have more than one target, so the job is restarted in poweruser mode (ie. maximised.)

Note that it doesn't actually matter what the string is after the "~dpnx0" - so long as it's a string. I just used poweruser What it does is tell the batch that it's being run in a normal window, not minimised.

For powerusrs, you could if you like set the shortcut to normal window or maximised AND put a parameter in the command line after the batchname, which will marginally quicken the procedure. Leaving it without parameters and minimised would suit ALL users as it will auto-adjust if more than 1 target is found.

I have tried this. It worked for me.




回答2:


I answered your question in two steps. In the first one I translated your original code into a more readable one. Here it is:

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION

SET "DIR1=%USERPROFILE%\AppData\Local\Application Workspace\Profiles"
SET DIR2=C:\Application\SRW\Profiles

IF NOT EXIST "%DIR1%" (
   for /f "delims=" %%A in ('dir %DIR2% /ad /b') do (
      SET DIR=%DIR2%\%%~nxA\app.exe
   )
) ELSE (
   for /f "delims=" %%A in ('dir %DIR1% /ad /b') do (
      SET DIR=%DIR1%\%%~nxA\app.exe
   )
)

START "" /b "%DIR%"
ENDLOCAL
EXIT

You should check first that previous code is equivalent to your original one.

In the second step I added the profile selection to the previous code:

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION

SET "DIR1=%USERPROFILE%\AppData\Local\Application Workspace\Profiles"
SET DIR2=C:\Application\SRW\Profiles

IF NOT EXIST "%DIR1%" (
   for /f "delims=" %%A in ('dir %DIR2% /ad /b') do (
      SET DIR=%DIR2%\%%~nxA\app.exe
   )
) ELSE (
   call :SelectProfile
)

START "" /b "%DIR%"
ENDLOCAL
EXIT


:SelectProfile

rem Get a list of folders in User Profile dir
set count=0
for /f "delims=" %%A in ('dir %DIR1% /ad /b') do (
   set /A count+=1
   SET DIR[!count!]=%DIR1%\%%~nxA
)

rem Initialize the selected profile
set select=1
if %count% equ 1 goto endSelection

rem Show the profiles menu
cls
echo Available profiles:
echo/
for /L %%i in (1,1,%count%) do echo   %%i- !DIR[%%i]!
echo/

rem Let's the user to select the profile
:select
set select=1
set /P "select=Enter number of desired profile [first one]: "
if not defined DIR[!select!] goto select

:endSelection
SET DIR=!DIR[%select%]!\app.exe
exit /B

Please note that previous code fail if the user enter spaces in the answer. This detail may be fixed, if needed.



来源:https://stackoverflow.com/questions/17057321/batch-file-to-list-folders-and-allow-user-selection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!