TheRealMrCrowley
TheRealMrCrowley

Reputation: 976

How to use nested variable abstraction in batch file?

I'm trying to write my own batch files to switch npm versions (just for fun).

@echo off
for /f "delims=" %%i in ('npm -v') do set output=%%i
move "%NPM_PATH%" "%NPM_PATH%-%output%"

FOR %%A IN (%*) DO (
  IF "%%A"=="--change" (
    SETLOCAL EnableDelayedExpansion

    SET /A c=1
    ECHO "ID : VERSION"
    ECHO -------------------------

    FOR /F "tokens=*" %%F in ('dir /on /b /a:d /p "%NPM_DIR%"') DO ( 
      ECHO !c! : %%F
      SET dir_!c!=%%F
      SET /a c=c+1   
    )

    set /p id="enter version ID: (e.g. !dir_1! enter 1):"
    set target_dir= "dir_!id!"
    echo !target_dir!
  )
)

I can echo out the right variable I want to abstract.

But how do I actually output the value of !target_dir!?

Example:

User selects 1

echo !target_dir! will print dir_1 to the screen.

But when I use next echo !dir_1! then npm-4.0.0 would be printed.

How do I now output npm-4.0.0 when the user inputs 1?

Upvotes: 1

Views: 145

Answers (2)

TheRealMrCrowley
TheRealMrCrowley

Reputation: 976

As per @Aacini I just needed to change this line: set target_dir= "dir_!id!" to this one: : for %%i in (!id!) do set target_dir=!dir_%%i!

See this question

Upvotes: 0

Mofi
Mofi

Reputation: 49127

I suggest using the following code with the second suggestion offered by Aacini used in code and some other improvements:

@echo off
for /F "delims=" %%I in ('npm.bat -v') do set "output=%%I"
move "%NPM_PATH%" "%NPM_PATH%-%output%"

for %%A in (%*) do (
    if "%%A" == "--change" (
        setlocal EnableDelayedExpansion
        cls
        set "Count=1"
        echo ID : VERSION
        echo -------------------------

        for /F "tokens=*" %%F in ('dir /A:D /B /ON "%NPM_DIR%"') do (
            if !Count! LSS 10 (
                echo  !Count! : %%F
            ) else (
                echo !Count! : %%F
            )
            set "dir_!Count!=%%F"
            set /A Count+=1
        )

        echo.
        set "id=1"
        set /P "id=Enter version ID: (e.g. !dir_1! enter 1): "
        for %%I in (!id!) do set "TargetDir=!dir_%%I!"
        echo !TargetDir!
        endlocal
    )
)

Why for %%I in (!id!) do set "TargetDir=!dir_%%I!" assigns the value of the environment variable with name dir_X with X being the entered number stored in environment variable id is explained in detail in Aacini's answer on Arrays, linked lists and other data structures in cmd.exe (batch) script.

For the applied improvements see:

It is advisable using environment variables with a self-explaining name in CamelCase notation. This has the advantage of knowing what the variable is for and makes it easier to search for all occurrences of a variable. It is a bit difficult to search for a variable with name c as a single character often occurs in many strings.

The command set with option /A means everything next should be interpreted as arithmetic expression. This means that the string after the equal sign is split up into a list of strings using spaces/tabs as string separators. Each string which could be interpreted as decimal, octal or hexadecimal number is converted to a 32-bit signed integer. All other strings which are not operators are interpreted as name of a variable whose value should be converted to a 32-bit signed integer. If the variable does not exist or has not a number as value, 0 is used for the variable. Then the arithmetic expression is evaluated and the result is converted back from integer to string. Finally the result string is assigned to the environment variable left to the equal sign. So with SET /A c=1 at lot of extra work is done in comparison to SET "c=1" although the result is the same, a string with value 1 is assigned to variable with name c. Therefore it is recommended not using /A on assigning a value to an environment variable except there is a very good reason to do so like a primitive check for string being really a number. Note: An assignment like set /A c=009 results in an error message because of 009 being an invalid octal number and c is not defined or keeps its current value in case of being already defined while set c=009 works.

When a batch user is prompted for a string, the user has the freedom to just hit RETURN or ENTER without entering anything at all. In this case the environment variable keeps its current value or is still not defined if it was not defined already before prompting the user. This explains why set "id=1" is present in batch code above before prompting the user. Well, it is not checked if the user has really entered a number and if this number is within 1 ... !Count!, but perhaps this is not really necessary although a typing mistake could always occur.

Upvotes: 1

Related Questions