Arrays, linked lists and other data structures in

2018-12-30 22:21发布

I was playing with cmd.exe, but in its help I didn't find any info, how to define arrays.

I have found, how to define simple variables:

set a=10
echo %a%

But, I want to create arrays, linked list etc...

So, does it able in cmd.exe ( I mean: does in cmd.exe exist any array keywords? )

I want to realize some algorithms as:

  • bubble sort
  • quick sort
  • gnome sort

etc...

So, I also want to know, does Cmd.exe have references or instances, structs etc?

Cause its help not full in: /?

Could Cmd.exe be defined as full by Turing-Machine definition? ( Turing-Complete )

8条回答
深知你不懂我心
2楼-- · 2018-12-30 22:53

Ok. I'll try to be as clear as possible to not be misunderstood...

In Windows Batch files a variable name should begin with a letter and may include any valid character, where valid characters are: #$'()*+,-.?@[]_`{}~ besides letters and digits.

This means that from the cmd.exe point of view, SET NORMAL_NAME=123 is exactly the same as SET A#$'()*+,-.?@[\]_{}~=123 and also the same as SET VECTOR[1]=123; all three are normal variables. This way, it is up to you to write variable names in the form of array elements:

set elem[1]=First element
set elem[2]=Second one
set elem[3]=The third one

This way, echo %elem[2]% will show Second one.

If you want to use another variable as index, you must know that the replacement of variables enclosed in percent symbols by their values is parsed from left to right; this means that:

set i=2
echo %elem[%i%]%

doesn't give the desired result because it means: show the value of the elem[ variable, followed by i, followed by the value of the ] variable.

To solve this problem you must use Delayed Expansion, that is, insert setlocal EnableDelayedExpansion command at the beginning, enclose index variables in percent symbols, and enclose the array elements in exclamation marks:

setlocal EnableDelayedExpansion
set elem[1]=First element
set elem[2]=Second one
set elem[3]=The third one
set i=2
echo !elem[%i%]!

You may also use parameters of FOR commands as indexes: for /L %%i in (1,1,3) do echo !elem[%%i]!. You must use !index! to store values in array elements when the index is changed inside a FOR or IF: set elem[!index!]=New value. To get the value of an element when the index changes inside FOR/IF, enclose the element in double percent symbols and precede the command with call. For example, to move a range of array elements four places to the left:

for /L %%i in (%start%,1,%end%) do (
   set /A j=%%i + 4
   call set elem[%%i]=%%elem[!j!]%%
)

Another way to achieve the previous process is to use an additional FOR command to change the delayed expansion of the index by an equivalent replaceable parameter, and then use the delayed expansion for the array element. This method runs faster than previous CALL:

for /L %%i in (%start%,1,%end%) do (
   set /A j=%%i + 4
   for %%j in (!j!) do set elem[%%i]=!elem[%%j]!
)

This way, the Batch file behaves like it manages arrays. I think the important point here is not to discuss if Batch manages arrays or not, but the fact that you may manage arrays in Batch files in an equivalent way of other programming languages.

@echo off
setlocal EnableDelayedExpansion

rem Create vector with names of days
set i=0
for %%d in (Sunday Monday Tuesday Wednesday Thrusday Friday Saturday) do (
   set /A i=i+1
   set day[!i!]=%%d
)

rem Get current date and calculate DayOfWeek
for /F "tokens=1-3 delims=/" %%a in ("%date%") do (
   set /A mm=10%%a %% 100, dd=10%%b %% 100, yy=%%c
)
if %mm% lss 3 set /A mm=mm+12, yy=yy-1
set /A a=yy/100, b=a/4, c=2-a+b, e=36525*(yy+4716)/100, f=306*(mm+1)/10, jdn=c+dd+e+f-1523, dow=jdn %% 7 + 1
echo Today is !day[%dow%]!, %date%

Note that index values are not limited to numbers, but they may be any string that contain valid characters; this point allows to define what in other programming languages are called associative arrays. At this answer there is a detailed explanation of the method used to solve a problem using an associative array. Note also that the space is a valid character in variable names, so you must pay attention to not insert spaces in variable names that may go unnoticed.

I elaborated on the reasons I have to use array notation in Batch files at this post.

In this post there is a Batch file that reads a text file and stores the indexes of the lines in a vector, then does a Buble Sort of vector elements based on line contents; the equivalent result is a sort over file contents.

In this post there is a basic Relational Data Base application in Batch based on indexes stored in files.

In this post there is a complete multiple linked-list application in Batch that assembles a large data structure taken from a subdirectory and displays it in the form of TREE command.

查看更多
萌妹纸的霸气范
3楼-- · 2018-12-30 23:00

Concerning this statement:

I have found, how to define simple variables:

set a = 10
echo %a%

This is simply wrong! Variable a will remain empty (supposing it was empty initially) and echo %a% will return ECHO is on. A variable called aSPACE will actually be set to the value SPACE10.

So for the code to work, you must get rid of the SPACEs around the equal-to sign:

set a=10
echo %a%

To make the assignment safe against all characters, use the quoted syntax (supposing you have the command extensions enabled, which is the default for the Windows command prompt anyway):

set "a=1&0"
echo(%a%

For all the rest of your question I recommend to read Aacini's great and comprehensive answer.

查看更多
长期被迫恋爱
4楼-- · 2018-12-30 23:00
@echo off

set array=

setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION

set nl=^&echo(

set array=auto blue ^!nl!^
  bycicle green ^!nl!^
  buggy   red

echo convert the String in indexed arrays

set /a index=0

for /F "tokens=1,2,3*" %%a in ( 'echo(!array!' ) do (

 echo(vehicle[!index!]=%%a color[!index!]=%%b 
 set vehicle[!index!]=%%a
 set color[!index!]=%%b
 set /a index=!index!+1   

)

echo use the arrays

echo(%vehicle[1]% %color[1]%
echo oder

set index=1
echo(!vehicle[%index%]! !color[%index%]!
查看更多
爱死公子算了
5楼-- · 2018-12-30 23:07

Windows shell scripting really isn't designed to work with arrays, let alone complex data structures. For the most part, everything's a string in the windows shell, but, there are some things you can do to "work with" arrays, like declaring n variables VAR_1, VAR_2, VAR_3... using a loop and filtering on the prefix VAR_, or creating a delimited string and then using the FOR construct that iterates over a delimited string.

Similarly, you can use the same basic idea to create a struct-like set of variables like ITEM_NAME, ITEM_DATA or w/e. I even found this link that talks about simulating an associative array in CMD.

It is all terribly hackish and inconvenient when it comes down to it. The command-line shell just wasn't designed for heavy programming. I agree with @MatteoItalia -- if you need serious scripting, use a real scripting language.

查看更多
牵手、夕阳
6楼-- · 2018-12-30 23:07

The following program simulates vectors (arrays) operations in cmd. The subroutines presented in it were initially designed for some special cases like storing the program parameters in an array or looping through filenames in a "for" loop and storing them in an array. In these cases, in an enabled delayed expansion block, the "!" characters - if present in values of the parameters or in the "for" loop variable's value - would get interpreted. That's why, in these cases, the subroutines must be used inside a disabled delayed expansion block:

@echo off

rem The subroutines presented bellow implement vectors (arrays) operations in CMD

rem Definition of a vector <v>:
rem      v_0 - variable that stores the number of elements of the vector;
rem      v_1..v_n, where n=v_0 - variables that store the values of the vector elements.


rem :::MAIN START:::

setlocal disabledelayedexpansion

    rem Getting all the parameters passed to the program in the vector 'params':
    rem Delayed expansion is left disabled in order not to interpret "!" in the program parameters' values (%1, %2, ... );
    rem If a program parameter is not quoted, special characters in it (like "^", "&", "|") get interpreted at program launch.
:loop1
    set "param=%~1"
    if defined param (
        call :VectorAddElementNext params param
        shift
        goto :loop1
    )
    rem Printing the vector 'params':
    call :VectorPrint params

    pause&echo.

    rem After the vector variables are set, delayed expansion can be enabled and "!" are not interpreted in the vector variables's values:
    echo Printing the elements of the vector 'params':
    setlocal enabledelayedexpansion
        if defined params_0 (
            for /l %%i in (1,1,!params_0!) do (
                echo params_%%i="!params_%%i!"
            )
        )
    endlocal

    pause&echo.

    rem Setting the vector 'filenames' with the list of filenames in the current directory:
    rem Delayed expansion is left disabled in order not to interpret "!" in the %%i variable's value;
    for %%i in (*) do (
        set "current_filename=%%~i"
        call :VectorAddElementNext filenames current_filename
    )
    rem Printing the vector 'filenames':
    call :VectorPrint filenames

    pause&echo.

    rem After the vector variables are set, delayed expansion can be enabled and "!" are not interpreted in the vector variables's values:
    echo Printing the elements of the vector 'filenames':
    setlocal enabledelayedexpansion
        if defined filenames_0 (
            for /l %%i in (1,1,!filenames_0!) do (
                echo filenames_%%i="!filenames_%%i!"
            )
        )
    endlocal

    pause&echo.

endlocal
pause

rem :::MAIN END:::
goto :eof


:VectorAddElementNext
rem Vector Add Element Next
rem adds the string contained in variable %2 in the next element position (vector length + 1) in vector %1
(
    setlocal enabledelayedexpansion
        set "elem_value=!%2!"
        set /a vector_length=%1_0
        if not defined %1_0 set /a vector_length=0
        set /a vector_length+=1
        set elem_name=%1_!vector_length!
)
(
    endlocal
    set "%elem_name%=%elem_value%"
    set %1_0=%vector_length%
    goto :eof
)

:VectorAddElementDVNext
rem Vector Add Element Direct Value Next
rem adds the string %2 in the next element position (vector length + 1) in vector %1
(
    setlocal enabledelayedexpansion
        set "elem_value=%~2"
        set /a vector_length=%1_0
        if not defined %1_0 set /a vector_length=0
        set /a vector_length+=1
        set elem_name=%1_!vector_length!
)
(
    endlocal
    set "%elem_name%=%elem_value%"
    set %1_0=%vector_length%
    goto :eof
)

:VectorAddElement
rem Vector Add Element
rem adds the string contained in the variable %3 in the position contained in %2 (variable or direct value) in the vector %1
(
    setlocal enabledelayedexpansion
        set "elem_value=!%3!"
        set /a elem_position=%2
        set /a vector_length=%1_0
        if not defined %1_0 set /a vector_length=0
        if !elem_position! geq !vector_length! (
            set /a vector_length=elem_position
        )
        set elem_name=%1_!elem_position!
)
(
    endlocal
    set "%elem_name%=%elem_value%"
    if not "%elem_position%"=="0" set %1_0=%vector_length%
    goto :eof
)

:VectorAddElementDV
rem Vector Add Element Direct Value
rem adds the string %3 in the position contained in %2 (variable or direct value) in the vector %1
(
    setlocal enabledelayedexpansion
        set "elem_value=%~3"
        set /a elem_position=%2
        set /a vector_length=%1_0
        if not defined %1_0 set /a vector_length=0
        if !elem_position! geq !vector_length! (
            set /a vector_length=elem_position
        )
        set elem_name=%1_!elem_position!
)
(
    endlocal
    set "%elem_name%=%elem_value%"
    if not "%elem_position%"=="0" set %1_0=%vector_length%
    goto :eof
)

:VectorPrint
rem Vector Print
rem Prints all the elements names and values of the vector %1 on sepparate lines
(
    setlocal enabledelayedexpansion
        set /a vector_length=%1_0
        if !vector_length! == 0 (
            echo Vector "%1" is empty!
        ) else (
            echo Vector "%1":
            for /l %%i in (1,1,!vector_length!) do (
                echo [%%i]: "!%1_%%i!"
            )
        )
)
(
    endlocal
    goto :eof
)

:VectorDestroy
rem Vector Destroy
rem Empties all the elements values of the vector %1
(
    setlocal enabledelayedexpansion
        set /a vector_length=%1_0
)
(
    endlocal
    if not %vector_length% == 0 (
        for /l %%i in (1,1,%vector_length%) do (
            set "%1_%%i="
        )
        set "%1_0="
    )
    goto :eof
)

It is also possible to store the program parameters in an "array" or loop through the filenames in a directory using a "for" loop and store them in an "array" (without interpreting "!" in their values) without using the presented subroutines in the program above:

@echo off

setlocal disabledelayedexpansion

    rem Getting all the parameters passed to the program in the array 'params':
    rem Delayed expansion is left disabled in order not to interpret "!" in the program parameters' values (%1, %2, ... );
    rem If a program parameter is not quoted, special characters in it (like "^", "&", "|") get interpreted at program launch.
    set /a count=1
:loop1
    set "param=%~1"
    if defined param (
        set "params_%count%=%param%"
        set /a count+=1
        shift
        goto :loop1
    )
    set /a params_0=count-1

    echo.

    rem After the array variables are set, delayed expansion can be enabled and "!" are not interpreted in the array variables's values:
    rem Printing the array 'params':
    echo Printing the elements of the array 'params':
    setlocal enabledelayedexpansion
        if defined params_0 (
            for /l %%i in (1,1,!params_0!) do (
                echo params_%%i="!params_%%i!"
            )
        )
    endlocal

    pause&echo.

    rem Setting the array 'filenames' with the list of filenames in the current directory:
    rem Delayed expansion is left disabled in order not to interpret "!" in the %%i variable's value;
    set /a count=0
    for %%i in (*) do (
        set "current_filename=%%~i"
        set /a count+=1
        call set "filenames_%%count%%=%%current_filename%%"
    )
    set /a filenames_0=count

    rem After the array variables are set, delayed expansion can be enabled and "!" are not interpreted in the array variables's values:
    rem Printing the array 'filenames':
    echo Printing the elements of the array 'filenames':
    setlocal enabledelayedexpansion
        if defined filenames_0 (
            for /l %%i in (1,1,!filenames_0!) do (
                echo filenames_%%i="!filenames_%%i!"
            )
        )
    endlocal

endlocal
pause

goto :eof
查看更多
心情的温度
7楼-- · 2018-12-30 23:09

I made a bubble sort implementation in batch using pseudo-arrays a while ago. Not sure why you'd use it (although I will admit to doing so in another batch file) as it gets pretty slow as the list size increases. It was more to set myself a little challenge. Someone might find this useful.

:: Bubblesort
:: Horribly inefficient for large lists
:: Dave Johnson implementation 05/04/2013
@echo off
setlocal enabledelayedexpansion
:: Number of entries to populate and sort
set maxvalue=50
:: Fill a list of vars with Random numbers and print them
for /l %%a in (1,1,%maxvalue%) do (
    set /a tosort%%a=!random!
)
:: echo them
set tosort
:: Commence bubble sort
Echo Sorting...
set /a maxvalue-=1
set iterations=0
for /l %%a in (%maxvalue%,-1,1) do ( REM Decrease by 1 the number of checks each time as the top value will always float to the end
    set hasswapped=0
        for /l %%b in (1,1,%%a) do (
            set /a next=%%b+1
            set next=tosort!next!
            set next=!next!
            call :grabvalues tosort%%b !next!
            rem echo comparing tosort%%b = !tosortvalue! and !next! = !nextvalue!
            if !nextvalue! LSS !tosortvalue! (
            rem set /a num_of_swaps+=1
            rem echo Swapping !num_of_swaps!
                set !next!=!tosortvalue!
                set tosort%%b=!nextvalue!
                set /a hasswapped+=1
            )
        )
    set /a iterations+=1
    if !hasswapped!==0 goto sorted
)
goto:eof
:grabvalues
set tosortvalue=!%1!
set nextvalue=!%2!
goto:eof
:sorted
::nice one our kid
set tosortvalue=
echo Iterations required: %iterations%
set tosort
endlocal
查看更多
登录 后发表回答