1

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.

Community
  • 1
  • 1
jubilare
  • 25
  • 2
  • 7

2 Answers2

3
@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.

Magoo
  • 77,302
  • 8
  • 62
  • 84
  • Thanks! This is more sophisticated than what I'm used to, but I will definitely try it and report back. – jubilare Jun 12 '13 at 13:25
  • This routine is awesome and worked right away. I really appreciate the explanation, too. Only one last question. Is there a way to make the batch run minimized UNLESS the user has to make a selection? Other routines (such as copying files) will be added to the batch file and my goal is to make this invisible for everyone except those power users with multiple profiles. Thanks again! – jubilare Jun 12 '13 at 19:49
  • After research, it appears it is not possible to undo a minimize command in the middle of a batch file. However, it may be possible to pass the selection parameters (only if multiple folders exist) to another batch file that is not set to run minimized. Sounds like a lot of extra coding, though. I'll mull this over. – jubilare Jun 13 '13 at 03:09
  • Peter, thanks for the help. If I understand you right, the routine for the extra requirement must/should have a parameter of 'poweruser' passed from the shortcut. I'm afraid I don't understand how to pass a parameter from a shortcut, but I also won't know if they're a power user until the routine identifies multiple targets. So the 2nd option you posed looks like what will work - that if more than one target exists, they are defined as a poweruser and the file is restarted. For this to work, do I also have to include that first code after SETLOCAL? Or will that make it restart a second time? – jubilare Jun 14 '13 at 01:49
  • I tried inserting the 2nd additional code alone, and I tried it with the 1st and 2nd code options. Didn't work at all. It was quite funny, though. Both times CMD windows kept opening and opening and opening. Could only kill with reboot. I'd love this if it can work. – jubilare Jun 14 '13 at 02:01
  • Yes, it absolutely works for me now, too. And it is absolutely exactly what I wanted. I wound up placing this code in its own batch file and call it from the other that copies files. Otherwise the files are copied twice and the copy is displayed to the poweruser upon re-launch. But I'm perfectly okay with this because it works! Thank you so much! I love it when something just works. Thanks again. – jubilare Jun 14 '13 at 13:46
1

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.

Aacini
  • 65,180
  • 12
  • 72
  • 108
  • Thank you. I'll try it now and report back. – jubilare Jun 12 '13 at 13:19
  • Thanks for both routines. I tried the first one as you suggested and only had to make one small update. I understand why you enclosed DIR1 in quotes, but it threw an error and I had to change it back then add one line of code to remove the quotes after the folder name was found. I tried the second routine and had the same problem, but could not figure out where to add the code to remove the quotes. Each place it was put didn't give the desired results. In addition, I would need to modify the code to look for multiple profiles in both DIR1 and DIR2. I may play with it more later. Thanks though! – jubilare Jun 12 '13 at 19:43