wake-up-neo.com

Arrays, verknüpfte Listen und andere Datenstrukturen im Skript cmd.exe (Batch)

Ich habe mit cmd.exe gespielt, aber in seiner Hilfe habe ich keine Informationen gefunden, wie man Arrays definiert.

Ich habe gefunden, wie man einfache Variablen definiert:

set a=10
echo %a%

Aber ich möchte Arrays, verknüpfte Liste usw. erstellen ...

Kann es also in cmd.exe (ich meine: gibt es in cmd.exe Array-Schlüsselwörter?)

Ich möchte einige Algorithmen realisieren als:

  • blase sortieren
  • schnelle Sorte
  • gnome sortieren

usw...

Also möchte ich auch wissen, hat Cmd.exe Referenzen oder Instanzen, Strukturen usw.?

Weil seine Hilfe nicht voll in: /?

Könnte Cmd.exe durch Turing-Machine-Definition als voll definiert werden? (Turing-Komplett)

56
user1131997

OK. Ich werde versuchen so klar wie möglich zu sein, um nicht missverstanden zu werden ...

In Windows-Stapeldateien sollte ein Variablenname mit einem Buchstaben beginnen und darf ein beliebiges gültiges Zeichen enthalten. Gültige Zeichen sind: # $ '() * +, -.? @ [] _` {} ~ neben Buchstaben und Ziffern.

Dies bedeutet, dass aus der Sicht von cmd.exe SET NORMAL_NAME=123 genau dasselbe ist wie SET A#$'()*+,[email protected][\]_{}~=123 und auch dasselbe wie SET VECTOR[1]=123; Alle drei sind normale Variablen. Auf diese Weise schreibt es liegt an Ihnen, Variablennamen in Form von Array-Elementen zu schreiben:

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

Auf diese Weise zeigt echo %elem[2]%Second one.

Wenn Sie eine andere Variable als Index verwenden möchten, müssen Sie wissen, dass die Ersetzung von in Prozentzeichen eingeschlossenen Variablen durch ihre Werte von links nach rechts ist. das bedeutet, dass:

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

gibt nicht das gewünschte Ergebnis aus, weil dies bedeutet: Zeigen Sie den Wert der Variablen elem[, gefolgt von i, gefolgt vom Wert der Variablen ].

Um dieses Problem zu lösen, müssen Sie Delayed Expansion verwenden, d. H. Den Befehl setlocal EnableDelayedExpansion am Anfang einfügen, Indexvariablen in Prozentzeichen einschließen und die Arrayelemente in Ausrufezeichen einschließen:

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

Sie können auch Parameter von FOR-Befehlen als Indizes verwenden: for /L %%i in (1,1,3) do echo !elem[%%i]!. Sie müssen! Index verwenden! um Werte in Array-Elementen zu speichern, wenn der Index in einem FOR oder IF geändert wird: set elem[!index!]=New value. Um den Wert eines Elements zu erhalten, wenn sich der Index innerhalb von FOR/IF ändert, schließen Sie das Element in doppelte Prozentzeichen ein und stellen Sie dem Befehl call voran. Um beispielsweise einen Bereich von Array-Elementen vier Stellen nach links zu verschieben:

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

Eine andere Möglichkeit, den vorherigen Prozess zu erreichen, besteht darin, einen zusätzlichen FOR-Befehl zu verwenden, um die verzögerte Erweiterung des Index durch einen entsprechenden ersetzbaren Parameter zu ändern, und dann die verzögerte Erweiterung für das Array-Element zu verwenden. Diese Methode läuft schneller als beim vorherigen CALL:

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

Auf diese Weise verwaltet die Batch-Datei (verhält sich so, wie sie Arrays verwaltet. Ich denke, es ist nicht wichtig zu diskutieren, ob Batch Arrays verwaltet oder nicht, sondern die Tatsache, dass Sie Arrays in Batch-Dateien auf eine ähnliche Weise wie andere Programmiersprachen verwalten können.

@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%

Beachten Sie, dass die Indexwerte nicht auf Zahlen beschränkt sind, sondern es kann sich um eine beliebige Zeichenfolge handeln, die gültige Zeichen enthält. In diesem Punkt können Sie definieren, was in anderen Programmiersprachen als assoziative Arrays bezeichnet wird. Bei dieser Antwort gibt es eine detaillierte Erklärung der Methode, die zur Lösung eines Problems unter Verwendung eines assoziativen Arrays verwendet wird. Beachten Sie auch, dass das Leerzeichen ein gültiges Zeichen in Variablennamen ist. Daher müssen Sie darauf achten, dass Leerzeichen nicht in Variablennamen eingefügt werden, die möglicherweise nicht bemerkt werden.

Ich habe die Gründe für die Verwendung der Array-Notation in Batch-Dateien unter diesem Beitrag erläutert.

In this post gibt es eine Batch-Datei, die eine Textdatei liest und die Indexe der Zeilen in einem Vektor speichert. Anschließend wird eine Buble-Sort von Vektorelementen basierend auf dem Zeileninhalt ausgeführt. Das äquivalente Ergebnis ist eine Sortierung des Dateiinhalts.

In this post gibt es eine grundlegende Anwendung der relationalen Datenbank in Batch, die auf Indizes basiert, die in Dateien gespeichert sind.

In this post gibt es eine komplette Anwendung mit mehreren verknüpften Listen in Batch, die eine große Datenstruktur aus einem Unterverzeichnis zusammenstellt und in Form des TREE-Befehls anzeigt.

147
Aacini

Windows Shell Scripting ist nicht für Arrays gedacht, geschweige denn für komplexe Datenstrukturen. Meistens ist alles eine Zeichenfolge in der Windows-Shell. Es gibt jedoch einige Möglichkeiten, Arrays zu "bearbeiten", z. B. n variables VAR_1, VAR_2, VAR_3... mit einer Schleife zu deklarieren und nach dem Präfix VAR_ zu filtern oder eine begrenzte Zeichenfolge zu erstellen Verwenden Sie dann das Konstrukt FOR, das eine durch Trennzeichen getrennte Zeichenfolge durchläuft.

Ebenso können Sie dieselbe Grundidee verwenden, um eine strukturähnliche Menge von Variablen wie ITEM_NAME, ITEM_DATA oder w/e zu erstellen. Ich habe sogar diesen Link gefunden, der über die Simulation eines assoziativen Arrays in CMD spricht.

Es ist alles schrecklich hackig und unbequem, wenn es darauf ankommt. Die Befehlszeilen-Shell wurde nicht für umfangreiche Programmierung konzipiert. Ich stimme @MatteoItalia zu - wenn Sie ernsthafte Skripte benötigen, verwenden Sie eine echte Skriptsprache.

7
trutheality

Ich habe vor einiger Zeit eine Batch-Implementierung für die Blasensortierung mit Pseudo-Arrays durchgeführt. Ich bin mir nicht sicher, warum Sie es verwenden würden (obwohl ich es in einer anderen Batch-Datei zugeben werde), da es mit zunehmender Listengröße ziemlich langsam wird. Es war mehr, mich einer kleinen Herausforderung zu stellen. Jemand könnte dies nützlich finden.

:: 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
6
Dave_J

Ernsthaft gesagt: Ich habe nie gehört, dass Batch Arrays enthält. Vielleicht können Sie sie mit einem merkwürdigen Trick nachahmen, aber ich würde das nicht als gute Idee bezeichnen.

Referenzen/Instanzen/Strukturen sind eine echte Sprache. Cmd Scripting ist nur eine Reihe von Erweiterungen, die über den sehr primitiven Interpreter Command.com hinauswachsen. Sie können einige grundlegende Skripte ausführen, aber alles andere als komplizierte Aufrufe andere Befehle sind dazu verurteilt, hässlich und unverständlich zu werden.

Das einzige "fortgeschrittene" Konstrukt ist die Do-It-All-Weirdo-Variable for, die, gemischt mit den seltsamen "Regeln" der Variablensubstitution (%var%, %%var, !var!, ist wegen des idiotischen Parsers etwas anderes), das Schreiben sogar trivial macht Algorithmen eine Sammlung von seltsamen Hacks (siehe hier für eine Implementierung von Quicksort ).

Mein Tipp: Wenn Sie Ihre Skripterstellung auf vernünftige Weise durchführen möchten, verwenden Sie die Skriptsprache real und lassen Sie batch für einfache, schnelle Hacks und für die Rückwärtskompatibilität.

6
Matteo Italia

Zu dieser Aussage:

Ich habe herausgefunden, wie man einfache Variablen definiert:

set a = 10
echo %a%

Das ist einfach falsch! Die Variable a bleibt leer (vorausgesetzt, sie war anfangs leer) und echo %a% gibt ECHO is on. zurück. Eine Variable namens aSPACE wird tatsächlich auf den Wert gesetzt SPACE10.

Damit der Code funktioniert, müssen Sie den entfernen SPACEs um das Gleichheitszeichen:

set a=10
echo %a%

Um die Zuweisung vor allen Zeichen zu schützen, verwenden Sie die in Anführungszeichen gesetzte Syntax (vorausgesetzt, Sie haben Befehlserweiterungen aktiviert, was ohnehin die Standardeinstellung für die Windows-Eingabeaufforderung ist):

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

Für den Rest Ihrer Frage empfehle ich, Aacini s großartig und umfassend Antwort zu lesen.

5
aschipfl

Das folgende Programm simuliert Vektoren (Arrays) in cmd. Die darin vorgestellten Unterprogramme waren ursprünglich für einige Sonderfälle konzipiert, wie das Speichern der Programmparameter in einem Array oder das Durchlaufen von Dateinamen in einer "for" -Schleife und das Speichern in einem Array. In diesen Fällen werden in einem enabled delayed expansion-Block die Zeichen "!" - sofern in Werten der Parameter oder im Wert der Variablenvariable "for" vorhanden - interpretiert. Aus diesem Grund müssen die Unterprogramme in diesen Fällen innerhalb eines disabled delayed expansion-Blocks verwendet werden:

@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
)

Es ist auch möglich, die Programmparameter in einem "Array" oder einer Schleife durch die Dateinamen in einem Verzeichnis mit einer "for" -Schleife zu speichern und sie in einem "Array" zu speichern (ohne "!" in ihren Werten zu interpretieren), ohne das dargestellte zu verwenden Unterprogramme im Programm oben:

@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
2
user5280669

TLDR:

Ich kam auf die Idee, eine "For" -Schleife und den "Set" -Befehl zu verwenden, um das Parsing von Variablen zuzulassen, was mir erlaubt, Pseudo-Arrays zu erstellen, die sowohl einen geordneten als auch einen verketteten Listenstil haben, und was noch wichtiger ist, Pseudo-Objekte, die Strukturen ähneln.

Ein typisches Batch-Pseudo-Array und wie man es analysiert:

SET "_Arr.Names="Name 1" "Name 2" ... "Name N""

FOR %A IN (%_Arr.Names%) DO @( Echo.%~A )

REM Results:

REM Name 1
REM Name 2
REM ...
REM Name N

Im Folgenden werden einige dumme Pseudo-Arrays und ein manuell angeordnetes Pseudo-Array sowie ein geordnetes Pseudo-Array erstellt, das die Ausgabe eines DIR-Befehls abfängt.

Wir nehmen auch die Dumb Pseudo Arrays und konvertieren sie in geordnete Arrays (Entfernen der ursprünglichen Dumb Pseudo Array-Variablen).

Wir aktualisieren dann alle bestellten Arrays, um manuell weitere Elemente zu enthalten.

Schließlich berichten wir dynamisch einige Werte aus dem Array, indem wir eine vordefinierte For L-Schleife für die Werte 7 bis 9 ausführen und einen Zufallswert generieren, um den vierten Beispielwert des Arrays zu drucken.

Hinweis:

Ich erstelle eine Variable, in der die Methode zum Hinzufügen von Mitgliedern enthalten ist, um das Hinzufügen zu vereinfachen.

Ich weise darauf hin, da es leicht zu sehen sein sollte, wie wir den kleinen Sprung von geordneten Arrays zu Pseudo-Objekten machen.

@(
 SETLOCAL ENABLEDELAYEDEXPANSION
 ECHO OFF

 REM Manually Create a shortcut method to add more elements to a specific ordered array
 SET "_Arr.Songs.Add=SET /A "_Arr.Songs.0+=1"&&CALL SET "_Arr.Songs.%%_Arr.Songs.0%%"

 REM Define some 'dumb' Pseudo arrays
 SET "_Arr.Names="Name 1" "Name 2" "Name 3" "Name 4" "Name 5" "Name 6" "Name 7" "Name 8""
 SET "_Arr.States="AL" "AK" "AZ" "AR" "CA" "CO" "CT" "DE" "FL" "GA" "HI" "ID" "IL" "IN" "IA" "KS" "KY" "LA" "ME" "MD" "MA" "MI" "MN" "MS" "MO" "MT" "NE" "NV" "NH" "NJ" "NM" "NY" "NC" "ND" "OH" "OK" "OR" "PA" "RI" "SC" "SD" "TN" "TX" "UT" "VT" "VA" "WA" "WV" "WI" "WY""

)

REM Manually Create One Ordered Array
%_Arr.Songs.Add%=Hey Jude"
%_Arr.Songs.Add%=The Bartman"
%_Arr.Songs.Add%=Teenage Dirtbag"
%_Arr.Songs.Add%=Roundabout"
%_Arr.Songs.Add%=The Sound of Silence"
%_Arr.Songs.Add%=Jack and Diane"
%_Arr.Songs.Add%=One Angry Dwarf and 200 Solumn Faces"

REM Turn All Pre-Existing Normal Pseudo Arrays into Element Arrays
REM Since Ordered Arrays use Index 0, we can skip any manually created Ordered Arrays:
FOR /F "Tokens=2 Delims==." %%A IN ('SET _Arr. ^| FIND /V ".0=" ^| SORT') DO (
 IF /I "%%~A" NEQ "!_TmpArrName!" (
  SET "_TmpArrName=%%~A"
  IF NOT DEFINED _Arr.!_TmpArrName!.Add (
   REM Create a shortcut method to add more members to the array
   SET "_Arr.!_TmpArrName!.Add=SET /A "_Arr.!_TmpArrName!.0+=1"&&CALL SET "_Arr.!_TmpArrName!.%%_Arr.!_TmpArrName!.0%%"
  )
  FOR %%a IN (!_Arr.%%~A!) DO (
   CALL SET /A "_Arr.!_TmpArrName!.0+=1"
   CALL SET "_Arr.!_TmpArrName!.%%_Arr.!_TmpArrName!.0%%=%%~a"
  )
 )
 IF DEFINED _Arr.!_TmpArrName! (
  REM Remove Unneeded Dumb Psuedo Array "_Arr.!_TmpArrName!"
  SET "_Arr.!_TmpArrName!="
 )
)

REM Create New Array of unknown Length from Command Output, and Store it as an Ordered Array
 SET "_TmpArrName=WinDir"
 FOR /F "Tokens=* Delims==." %%A IN ('Dir /B /A:D "C:\Windows"') DO (
  IF NOT DEFINED _Arr.!_TmpArrName!.Add (
   SET "_Arr.!_TmpArrName!.Add=SET /A "_Arr.!_TmpArrName!.0+=1"&&CALL SET "_Arr.!_TmpArrName!.%%_Arr.!_TmpArrName!.0%%"
  )
  CALL SET /A "_Arr.!_TmpArrName!.0+=1"
  CALL SET "_Arr.!_TmpArrName!.%%_Arr.!_TmpArrName!.0%%=%%~A"
 )
)

REM Manually Add additional Elements to the Ordered Arrays:
%_Arr.Names.Add%=Manual Name 1"
%_Arr.Names.Add%=Manual Name 2"
%_Arr.Names.Add%=Manual Name 3"

%_Arr.States.Add%=51st State"
%_Arr.States.Add%=52nd State"
%_Arr.States.Add%=53rd State"

%_Arr.Songs.Add%=Live and Let Die"
%_Arr.Songs.Add%=Baby Shark"
%_Arr.Songs.Add%=Safety Dance"

%_Arr.WinDir.Add%=Fake_Folder 1"
%_Arr.WinDir.Add%=Fake_Folder 2"
%_Arr.WinDir.Add%=Fake_Folder 3"

REM Test Output:

REM Use a For Loop to List Values 7 to 9 of each array and A Psuedo Rnadom 4th value
REM We are only interested in Ordered Arrays, so the .0 works nicely to locate those exclusively.
FOR /F "Tokens=2,4 Delims==." %%A IN ('SET _Arr. ^| FIND ".0=" ^| SORT') DO (
 CALL :Get-Rnd %%~B
 ECHO.
 ECHO.%%~A 7 to 9, Plus !_Rnd#! - Psuedo Randomly Selected
 FOR /L %%L IN (7,1,9) DO (
  CALL Echo. * Element [%%L] of %%~A Pseudo Array = "%%_Arr.%%~A.%%L%%"
 )
 CALL Echo. * Random Element [!_Rnd#!] of %%~A Pseudo Array = "%%_Arr.%%~A.!_Rnd#!%%"
)
ENDLOCAL 
GOTO :EOF

:Get-Rnd
 SET /A "_RandMax=(32767 - ( ( ( 32767 %% %~1 ) + 1 ) %% %~1) )", "_Rnd#=!Random!"
 IF /I !_Rnd#! GTR !_RandMax! ( GOTO :Get_Rnd# )
 SET /A "_Rnd#%%=%~1"
GOTO :EOF

Beispielergebnisse:

Results:

Names 7 to 9, Plus 5 - Psuedo Randomly Selected
 * Element [7] of Names Pseudo Array = "Name 7"
 * Element [8] of Names Pseudo Array = "Name 8"
 * Element [9] of Names Pseudo Array = "Manual Name 1"
 * Random Element [5] of Names Pseudo Array = "Name 5"

Songs 7 to 9, Plus 5 - Psuedo Randomly Selected
 * Element [7] of Songs Pseudo Array = "One Angry Dwarf and 200 Solumn Faces"
 * Element [8] of Songs Pseudo Array = "Live and Let Die"
 * Element [9] of Songs Pseudo Array = "Baby Shark"
 * Random Element [5] of Songs Pseudo Array = "The Sound of Silence"

States 7 to 9, Plus 9 - Psuedo Randomly Selected
 * Element [7] of States Pseudo Array = "CT"
 * Element [8] of States Pseudo Array = "DE"
 * Element [9] of States Pseudo Array = "FL"
 * Random Element [9] of States Pseudo Array = "FL"

WinDir 7 to 9, Plus 26 - Psuedo Randomly Selected
 * Element [7] of WinDir Pseudo Array = "Assembly"
 * Element [8] of WinDir Pseudo Array = "AUInstallAgent"
 * Element [9] of WinDir Pseudo Array = "Boot"
 * Random Element [26] of WinDir Pseudo Array = "Fonts"

Anfangs würde ich Dinge ähnlich wie Aacini tun, eine einfache Variablenzeile mit einem Inkrementalzähler, manuell oder durch eine einfache Schleife aus einer Schnellliste von Variablen zuweisen.

Dies war in Ordnung für kleine 2D-Arrays.

Ich empfinde es jedoch als schmerzhaft für lange Datenreihen, insbesondere wenn ich mehrwertigen Inhalt benötige.

Um nicht zu sagen, wann ich Inhalte in diesen mehrdimensionalen Arrays dynamisch abgleichen und füllen muss, wo die einfache Verwendung dort zusammenbricht.

Ich stellte fest, dass es schwierig wurde, als Sie mehrere Arrays von Informationen benötigten, die Sie für das Aktualisieren oder Hinzufügen von Funktionen auf der ganzen Welt brauchten.

Ein solches Array ist im Wesentlichen eine Liste von Teilstrings, die Sie als Variablen exportieren müssen. Wenn Sie deren Reihenfolge ändern oder ändern, müssen Sie Ihren Code ändern.

Nehmen Sie zum Beispiel ein Szenario, in dem Sie sich bei mehreren FTP-Servern anmelden müssen, und löschen Sie Dateien, die älter als X Tage sind, aus bestimmten Pfaden.

Anfangs können Sie einfache Arrays von Teilzeichenfolgen erstellen, die ich folgendermaßen definiere:

Site.##=[Array (String)] [Array (String)] @(
       IP=[SubSting],
       Username=[SubString],
       Password[SubString])

Oder wie in diesem Beispielcode gezeigt.

(
  SETOCAL
  ECHO OFF

  REM Manage Sites:
  SET "Sites=13"
  SET "MaxAge=28"

  SET "Site.1="[IP]" "[User Name]" "[Password]" "[Path]""
  SET "Site.2="[IP]" "[User Name]" "[Password]" "[Path]""
  SET "Site.3="[IP]" "[User Name]" "[Password]" "[Path]""
  REM  ...
  SET "Site.11="[IP]" "[User Name]" "[Password]" "[Path]""
  SET "Site.12="[IP]" "[User Name]" "[Password]" "[Path]""
  SET "Site.13="[IP]" "[User Name]" "[Password]" "[Path]""
)

FOR /L %%L IN (1,1,%Sites%) DO (
   FOR /F "Tokens=*" %%A IN ('CALL ECHO %%Site.%%L%%') DO (
      Echo. Pulled this example from a more complex example of my actual code, so the example variables may not need this loop, but it won't hurt to have if they don't need the extra expansion.
     Call :Log
     CALL :DeleteFTP %%~A
   )
)

GOTO :EOF
:DeleteFTP
   REM Simple ftp command for cygwin to delete the files found older than X days.
   SET "FTPCMD="%~dp0lftp" %~1 -u %~2,%~3 -e "rm -rf %~4%MaxAge% "
   FOR /F "Tokens=*" %%F IN ('"%FTPCMD% 2^>^&1"') DO @(
     ECHO.%%~F
   )
GOTO :EOF

Nun, 13 Sites, das ist nicht so schlimm, ich bin sicher, du sagst es. Recht? Sie können einfach eine am Ende hinzufügen und dann die Informationen eingeben und fertig.

Dann müssen Sie die Namen der Websites für die Berichterstellung hinzufügen, sodass Sie an Stelle 5 an Stelle 5 einen weiteren Begriff hinzufügen, damit Sie Ihre Funktion nicht ändern müssen.

::...
SET "Site.1="[IP]" "[User Name]" "[Password]" "[Path]" "[Site Name]""
::...

Dann wissen Sie, dass Sie sie nach ihrem Site-Namen (oder IP-Adressen) in der richtigen Reihenfolge halten müssen. Die Namen sind für die meisten Benutzer jedoch einfacher zu merken und Sie müssen in der Lage sein, andere Personen nachzusehen, damit Sie die Reihenfolge ändern können alle 13 Spots, der Aufruf zum Erweitern der Variablen und die Funktion.

::...
SET "Site.1="[Site Name]" "[IP]" "[User Name]" "[Password]" "[Path]""
::...
FOR /F "Tokens=*" %%A IN ('CALL ECHO %%Site.%%L%%')
::...
SET "FTPCMD="%~dp0lftp" %~2 -u %~3,%~4 -e "rm -rf %~5%MaxAge% "
::...

Dann wird es immer schlimmer:

  • Die Anzahl der Verzeichnisse, die Sie an einem Standort mit verschiedenen Benutzern prüfen müssen, steigt an.
  • Sie wissen, dass Sie unterschiedliche Aufbewahrungszeiten pro Standort und später pro Verzeichnis benötigen.
  • Am Ende haben Sie 30, 40,50 davon und es ist schwer zu merken, welche davon ist, indem Sie am Ende einer langen Schnur nachsehen und sie herum kopieren usw.

  • Sie haben zwar keine weiteren Pfade mehr hinzugefügt, aber manchmal müssen Sie alte Pfade entfernen, oder es verursacht Probleme, wenn sie verschwunden sind. Wenn Sie vergessen, die Gesamtzahl der Sites zu aktualisieren, könnte die Liste möglicherweise nicht ausgeführt werden.

  • wenn ein Verzeichnis hinzugefügt oder entfernt wird, müssen Sie es für jede Site hinzufügen/entfernen. Dies erschwert die Verwendung der Bestellung und das Missing von Sites, da diese nicht leicht zu identifizieren sind.

Nur was für ein Schmerz, und das ist nicht einmal der Fall, wenn Sie einen dynamischen Satz von Objekten benötigen, dies ist alles manuell.

Also, was kannst du machen? Nun, das habe ich getan:

Am Ende habe ich eine Art Armen- oder Object-Array (von Strings) in meine cmd-Skripte implementiert, wo die Bedürfnisse passen.

IE die Struktur wäre ein "Site-Objekt", das mehrere Eigenschaften hätte, die Objekte mit Untereigenschaften sein könnten. Da CMD eigentlich nicht objektorientiert ist, ist es ein bisschen kludge, genau wie Arrays.

Da das Beispiel, mit dem ich angefangen habe, der erste Ort war, an dem ich diese ausprobiert habe, kann man diesen Zwischenschritt sehen, den ich so definiere:

eg: Site.[ID].[Object Property]=[Value, or array of values]

   Site
     .ID=[int]
      .Name=[string]
      .Path=[String]
      .MaxAge=[Int]
      .Details=[Array (String)] @(
       IP=[SubSting],
       Username=[SubString],
       Password[SubString])

Um dem Problem zu begegnen, dass Datensätze im Handumdrehen neu angeordnet werden mussten, habe ich über eine Form von verknüpften Listen nachgedacht, mit denen ich gespielt habe. Da ich jedoch zu jeder Gruppierung von Websites Elemente hinzufügen wollte, während die Reihenfolge zwischen den Websites beibehalten wurde, auf die ich mich festgelegt hatte einfache Methode.

Hier ist ein weiteres Codebeispiel für diesen Verwendungsschritt:

@(
    SETLOCAL ENABLEDELAYEDEXPANSION
    ECHO OFF

    SET "_SiteCount=0"
    SET "_SiteID=0"

    SET /A "_SiteID= !_SiteID! + 1"
    SET "Site.!_SiteID!.MaxAge=Day5Ago"
    SET "Site.!_SiteID!.Name=[SITE NAME HEADER FOR EMAIL]"
    SET "Site.!_SiteID!.Detail="[IP]" "[UserName]" "[Password]" "[Path]""

    REM ...

    SET /A "_SiteID= !_SiteID! + 1"
    SET "Site.!_SiteID!.MaxAge=Day15Ago"
    SET "Site.!_SiteID!.Name=[SITE NAME HEADER FOR EMAIL]"
    SET "Site.!_SiteID!.Detail="[IP]" "[UserName]" "[Password]" "[Path]""
)

CALL :Main

(
    ENDLOCAL
    Exit /b %eLvl%
)

:Main
   REM In some forms of these the order isn't meaningful, but in others you need to follows the order and so we just count he number of site objects by counting one of their properties.
   FOR /F %%A IN ('SET ^| FIND /I "Site." ^| FIND /I ".Name="') DO ( CALL SET /A "_SiteCount+=1" )
    FOR /L %%L IN (1,1,34) DO (
        CALL :PSGetDate_DaysAgo %%L
    )
    FOR /L %%L IN (1,1,%_SiteCount%) DO (
        SET "Site.%%L.Create=NONE"
    )
    FOR /L %%L IN (1,1,%_SiteCount%) DO (
        FOR /F "Tokens=*" %%A IN ('CALL ECHO ""%%Site.%%L.Name%%" %%Site.%%L.Detail%% "Site.%%L" "%%%%Site.%%L.MaxAge%%%%""') DO (
            CALL ECHO CALL :DeleteFTP %%~A
            CALL :DeleteFTP %%~A
        )
    )
    CALL :SendMail "%EMLog%" "%_EMSubject%"

GOTO :EOF

:DeleteFTP
    REM ECHO.IF "%~7" EQU "%skip%" (
    IF "%~7" EQU "%skip%" (
        GOTO :EOF
    )
    SET "FTPCMD="%~dp0lftp" %~2 -u %~3,%~4 -e "rm -rf %~5%~7 "
    SET "FTPCMD=%FTPCMD%; bye""
    FOR /F "Tokens=*" %%F IN ('"%FTPCMD% 2^>^&1"') DO @(
        ECHO."%%F"
        ECHO."%%~F"
        REM CALL :Output "%Temp%\%~2_%~7.log" "%%F"
        %OP% "%Temp%\%~2_%~7.log"
        SET "FTPOut=%%~F"
    )
GOTO :EOF

Wie Sie wahrscheinlich sehen können, funktionieren diese Strukturen sehr gut, wenn Sie über hierarchische Daten verfügen, die Sie manuell anwenden müssen und die Daten in einer bestimmten Reihenfolge anzeigen. Obwohl ich heute sicher bin, dass die Basis der Strukturen normalerweise der Name des Skripts ist, ist dies, wie ich finde, nützlicher und kann je nach Bedarf geordnete Arrays verwenden. .

SET "_GUID=^%Time^%_^%Random:~-1^%^%Random:~-1^%^%Random:~-1^%^%Random:~-1^%^%Random:~-1^%^%Random:~-1^%^%Random:~-1^%^%Random:~-1^%^%Random:~-1^%" eg: %~n0.[ObjectName].[Object Property].[Object Sub Property]=[Value, or array of values] [Script Name] .[Object Name](May Hold Count of Names)=[int] .Name=[string] .Paths(May Hold Count of IDs)=[INT] .GUID=%_GUID% .Path=String .MaxAge=[Int] .Details=[Array (String)] @( IP=[SubSting], Username=[SubString], Password[SubString])

@(
    SETLOCAL ENABLEDELAYEDEXPANSION
    ECHO OFF

    SET /A "_SiteID= !_SiteID! + 1"
    SET "SiteName=SiteA"
    SET "%~n0.!SiteName!=%%_SiteID%%
    SET "%~n0.!SiteName!.SiteID=!_SiteID!
    SET "%~n0.!SiteName!.Paths="PathA" "PathB" "PathC" "PathD" "PathE""
)

CALL :CheckFTP [FTP Login variables from source object including Site ID]

:CheckFTP
 REM Not necessary to assign Variables, doing this for exposition only:
 CALL SET "TempSiteName=%~6"
 CALL SET "TempPaths=%%%~n0.%~1.Paths%%"
 REM Clear the site Temp KB variables
 FOR \F "Tokens=2* Delims== " %%H IN (%TempPaths% "Total" "Temp") DO (
  CALL SET /A "%%%~n0.%~1.Paths.%%~H.KB=0"
 )
 FOR %%J IN (%TempPaths%) DO (
   FOR /F "Tokens=1-2" %%F IN ('[FTP Command using source object options]') DO @(
     CALL :SumSite "%~6" "%%~F" "%%~G"
     FOR /F "Tokens=1,2,* delims=/" %%f IN ("%%~G") DO (
       CALL :ConvertFolder "%~6" "%%~F" "%%~g" "%%~h" "%~6_%%~g_%%~h"
     )
   )
 )

FOR /F "Tokens=3,4,7 Delims==_." %%g IN ('SET ^| FIND /I "%~6_" ^| FIND /I ".KB" ^| FIND /I /V "_."') DO (
    CALL :WriteFolder "%%g/%%~h" "%TmpFile%" "%~6_%%~g_%%~h"
    REM echo.CALL :WriteFolder "%%g/%%~h" "%TmpFile%" "%~6_%%~g_%%~h"
)
CALL :ConvertSite "%~1"
CALL :WriteTotalFolder "%~7" "%TmpFile%" "%~6"
CALL :SendMail "%TmpFile%" "Backup_%~1"
GOTO :EOF

:SumSite
  CALL SET "TSumPaths=%%%~n0.%~1.Paths%% "Total""
   FOR %%H IN (%TSumPaths%) DO (
    CALL SET /A "%~n0.%~1.Paths.%%~H.KB=%%%~n0.%~1.Paths.%%~H.KB%%+%~2"
  )

:SumSite
  CALL SET "TSumPaths=%%%~n0.%~1.Paths%% "Total""
   FOR %%H IN (%TSumPaths%) DO (
    CALL SET /A "%~n0.%~1.Paths.%%~H.KB=%%%~n0.%~1.Paths.%%~H.KB%%+%~2"
  )
GOTO :EOF

:ConvertFolder
    REM Convert's Folder values to MB and GB
    SET /A "%~1.Temp.KB=%~2"
    CALL SET /A "%~1.Temp.MB=%%%~1.Temp.KB%%/1024"
    CALL SET /A "%~1.Temp.GB=(%%%~1.Temp.KB%%/1024)/1024"
    CALL SET /A "%~5.Temp.KB=%%%~5.Temp.KB%%+%~2"
    CALL SET /A "%~5.Temp.MB=%%%~5.Temp.KB%%/1024"
    CALL SET /A "%~5.Temp.GB=(%%%~5.Temp.KB%%/1024)/1024"
GOTO :EOF

:WriteFolder

    CALL :PickGMKBytes "%~1" "%~2" "G" "M" "K" "%%%~3.Temp.GB%%" "%%%~3.Temp.MB%%" "%%%~3.Temp.KB%%"

GOTO :EOF

:PickGMKBytes

    IF /I "%~6" NEQ "" (
        IF /I "%~6"=="0" (
            CALL :PickGMKBytes "%~1" "%~2" "%~4" "%~5" "%~6" "%~7" "%~8"
        ) ELSE (
            CALL :Output "%~2" "%~6%~3  %~1"
        )
    ) ELSE (
        CALL :Output "%~2" "0B  %~1"
    )

GOTO :EOF


:ConvertSite
 CALL SET "TempPaths=%%%~n0.%~1.Paths%%"
    FOR %%V IN (%TempPaths% "Total") DO (
        CALL SET /A "%~1.%%~V.MB=%%%~1.%%~V.KB%%/1024"
        CALL SET /A "%~1.%%~V.GB=(%%%~1.%%~V.KB%%/1024)/1024"
    )

GOTO :EOF

Um ehrlich zu sein, kann dieses Skriptbeispiel nicht eindeutig sein, um zu zeigen, was passiert, und ich musste im Handumdrehen Änderungen vornehmen, um einen neuen Objektstil zu korrigieren, im Wesentlichen jedoch: 
 Es erstellt Verbindungsobjekte und erweitert diese dynamisch Um Unterordner einzubeziehen und die laufenden Summen für jeden Unterordner und Standort in KB, MB und GB sowie Bilder zu verwalten, wird angegeben, welche Werte nach dem Summieren aller Verzeichnisse für einen bestimmten Ordner usw. dynamisch gemeldet werden.

Ich musste es zwar ein wenig bearbeiten, da dies auch eine frühere Version davon ist, aber ich dachte, es war einer der Fälle, in denen es die Vorteile am besten zeigen könnte. Wenn ich in einem meiner anderen Skripts ein besseres Beispiel finde, kann ich es dort auch aktualisieren.

While i had to edit it a bit because this is also an earlier version of these as well, I thought it was one of the instances where it might best show the benefits. If I find a better example in one of my other scripts I might update it there as well.

0
Ben Personick