Подтвердить что ты не робот

Массивы, связанные списки и другие структуры данных в cmd.exe(пакет) script

Я играл с cmd.exe, но в его справке я не нашел никакой информации, как определить массивы.

Я нашел, как определить простые переменные:

set a=10
echo %a%

Но я хочу создать массивы, связанный список и т.д...

Итак, может ли он в cmd.exe (я имею в виду: существует ли в cmd.exe какие-либо ключевые слова массива?)

Я хочу реализовать некоторые алгоритмы как:

  • пузырьковая сортировка
  • быстрая сортировка
  • сортировка гномов

так далее...

Итак, я также хочу знать, есть ли в Cmd.exe ссылки или экземпляры, структуры и т.д.?

Потому что его помощь не полная в:/?

Может ли Cmd.exe быть определен как полный по определению машины Тьюринга? (Тьюринг-Полная)

4b9b3361

Ответ 1

Ok. Я постараюсь быть как можно более ясным, чтобы вас не поняли...

В командных файлах Windows имя переменной должно начинаться с буквы и может содержать любой допустимый символ, где допустимые символы: # $'() * +, -.? @[] _ ​​`{} ~ кроме букв и цифр.

Это означает, что с точки зрения cmd.exe SET NORMAL_NAME=123 точно совпадает с SET A#$'()*+,[email protected][\]_{}~=123 и также совпадает с SET VECTOR[1]=123; все три являются нормальными переменными. Таким образом, вам придется писать имена переменных в виде элементов массива:

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

Таким образом, echo %elem[2]% отобразит Second one.

Если вы хотите использовать другую переменную в качестве индекса, вы должны знать, что замена переменных, заключенных в процентах символов по их значениям, анализируется слева направо; это означает, что:

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

не дает желаемого результата, потому что он означает: показывает значение переменной elem[, за которой следует i, за которым следует значение переменной ].

Чтобы решить эту проблему, вы должны использовать Delayed Expansion, то есть вставить команду setlocal EnableDelayedExpansion в начале, заключить индексы в процентах символов и заключить элементы массива в восклицательные знаки:

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

Вы также можете использовать параметры команд FOR как индексы: for /L %%i in (1,1,3) do echo !elem[%%i]!. Вы должны использовать! Index! для хранения значений в элементах массива, когда индекс изменяется внутри FOR или IF: set elem[!index!]=New value. Чтобы получить значение элемента, когда индекс изменяется внутри FOR/IF, заключите элемент в символы с двойным процентом и перед командой call. Например, чтобы перемещать диапазон элементов массива в четыре места слева:

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

Другим способом достижения предыдущего процесса является использование дополнительной команды FOR для изменения замедленного расширения индекса эквивалентным сменным параметром, а затем использовать задержанное расширение для элемента массива. Этот метод работает быстрее предыдущего CALL:

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

Таким образом, командный файл ведет себя так, как будто он управляет массивами. Я думаю, что важным моментом здесь является не обсуждение того, управляет ли Batch массивами или нет, но тем фактом, что вы можете управлять массивами в пакетных файлах эквивалентным образом других языков программирования.

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

Обратите внимание, что значения индекса не ограничены числами, но они могут быть любой строкой, содержащей допустимые символы; эта точка позволяет определить, что на других языках программирования называется ассоциативными массивами. В этом ответе приводится подробное объяснение метода, используемого для решения проблемы с использованием ассоциативного массива. Также обратите внимание, что пространство является допустимым символом в именах переменных, поэтому вы должны обратить внимание на то, чтобы не вставлять пробелы в имена переменных, которые могут остаться незамеченными.

Я подробно остановился на причинах, по которым я должен использовать запись массива в пакетных файлах в этом сообщении.

В этот пост есть пакетный файл, который читает текстовый файл и сохраняет индексы строк в векторе, а затем делает Buble Типы векторных элементов на основе содержимого строки; эквивалентный результат - это сортировка содержимого файла.

В этот пост существует базовое приложение базы реляционных данных в пакетном режиме на основе индексов, хранящихся в файлах.

В этот пост в Batch есть полное несколько приложений с связанным списком, который собирает большую структуру данных, взятую из подкаталога, и отображает ее в форма команды TREE.

Ответ 2

Сценарии оболочки Windows действительно не предназначены для работы с массивами, не говоря уже о сложных структурах данных. По большей части все строки в оболочке Windows, но есть некоторые вещи, которые вы можете сделать, чтобы "работать с" массивами, например объявлять n variables VAR_1, VAR_2, VAR_3..., используя цикл и фильтрацию на префиксе VAR_, или создание разделительной строки, а затем используя конструкцию FOR, которая выполняет итерацию по разделительной строке.

Аналогичным образом вы можете использовать одну и ту же основную идею для создания структурного набора переменных типа ITEM_NAME, ITEM_DATA или w/e. Я даже нашел эту ссылку, в которой говорится о моделировании ассоциативного массива в CMD.

Все это ужасно хаки и неудобно, когда дело доходит до него. Командная строка просто не была разработана для интенсивного программирования. Я согласен с @MatteoItalia - если вам нужны серьезные сценарии, используйте настоящий скриптовый язык.

Ответ 3

Серьезно: я никогда не слышал, что у партии есть массивы, возможно, вы можете имитировать их каким-то странным трюком, но я бы не назвал это хорошей идеей.

Ссылки/экземпляры/структуры - это материал для реального языка, скрипты cmd - это всего лишь куча расширений, которые росли над самым примитивным интерпретатором, который был command.com, вы можете сделать некоторые основные сценарии, но что-то более сложное, чем пучок вызовов другим командам обречена стать уродливой и непонятной.

Единственная "продвинутая" конструкция - это цикл do-it-all weirdo for, который, смешанный со странными "правилами" замены переменных (%var%, %%var, !var!), - это разные вещи из-за идиотского парсера), делает даже тривиальные алгоритмы сборником странных хаков (см., например, здесь для реализация quicksort).

Мой совет: если вы хотите сделать свой сценарий разумным способом, используйте реальный язык сценариев и оставите пакет для простых, быстрых хаков и для обратной совместимости.

Ответ 4

Я сделал реализацию сортировки пузырьков в пакетном режиме с использованием псевдо-массивов некоторое время назад. Не уверен, почему вы его используете (хотя я соглашусь сделать это в другом пакетном файле), так как он становится довольно медленным, поскольку размер списка увеличивается. Это было больше для того, чтобы поставить себе небольшой вызов. Кто-то может найти это полезным.

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

Ответ 5

Относительно этого утверждения:

Я нашел, как определить простые переменные:

set a = 10
echo %a%

Это просто неправильно! Переменная a останется пустой (предположим, что она была пустой изначально) и echo %a% вернется ECHO is on. Переменная с именем a SPACE будет фактически установлена ​​в значение SPACE 10.

Итак, чтобы код работал, вы должны избавиться от SPACEs вокруг знака равенства:

set a=10
echo %a%

Чтобы сделать присвоение безопасным для всех символов, используйте цитированный синтаксис (предположим, что вы активировали расширения команд, который по умолчанию используется для командной строки Windows):

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

Для всего остального вашего вопроса я рекомендую читать Aacini отличный и всеобъемлющий ответ.

Ответ 6

Следующая программа имитирует операции векторов (массивов) в cmd. Представленные в нем подпрограммы изначально были предназначены для некоторых особых случаев, таких как сохранение параметров программы в массиве или циклическое прохождение имен файлов в цикле "for" и сохранение их в массиве. В этих случаях в блоке enabled delayed expansion символы "!", если они присутствуют в значениях параметров или в значении переменной цикла "for" , будут интерпретированы. Поэтому в этих случаях подпрограммы должны использоваться внутри блока disabled delayed expansion:

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

Также возможно сохранить параметры программы в "массиве" или закодировать имена файлов в каталоге с помощью цикла "for" и сохранить их в "массиве" (без интерпретации "!" в их значения) без использования представленных подпрограмм в программе выше:

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

TL;DR:

Я натолкнулся на идею использования цикла "For" и команды "set", чтобы разрешить синтаксический анализ переменных, что позволило мне создавать псевдо-массивы как в упорядоченном стиле, так и в стиле связанного списка, и, что более важно, псевдообъекты, сродни структурам.

Типичная партия Pseudo Array и как ее анализировать:

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

Ниже мы сделаем несколько Dumb Pseudo Arrays и упорядоченный вручную Pseudo Array, а также создадим Ordered Pseudo Array, перехватывающий вывод команды DIR.

Мы также берем Dumb Pseudo Arrays и конвертируем их в Ordered (после удаления оригинальных переменных Dumb Pseudo Array).

Затем мы обновляем все упорядоченные массивы, чтобы они содержали больше элементов вручную.

Наконец, мы динамически сообщаем о некоторых значениях из массива, выполняя предопределенный цикл For L для значений от 7 до 9 и генерируя случайное значение, чтобы напечатать 4-й пример значения массива.

Замечания:

Я создаю переменную для хранения метода добавления членов, чтобы упростить их добавление.

Я подчеркиваю это, поскольку это должно облегчить понимание того, как мы совершаем незначительный переход от упорядоченных массивов к псевдообъектам.

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

Пример результатов:

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"

Первоначально я делал бы что-то похожее на Aacini, простую строку переменных с инкрементным счетчиком, вручную или назначая их с помощью простого цикла из быстрого списка переменных.

Это было хорошо для небольших 2-D массивов.

Однако я нахожу это болью для длинных массивов данных, особенно когда мне нужен многозначный контент.

Не говоря уже о том, когда мне нужно динамически сопоставлять и заполнять контент в этих многомерных массивах, где простое использование там нарушается.

Я обнаружил, что это стало трудно, когда вам понадобилось несколько массивов информации, которые вам нужно было обновить или добавить функции по всем направлениям.

Поскольку такой массив - это, по сути, список подстрок, которые необходимо экспортировать как переменные, а добавление или изменение их порядка означает изменение вашего кода.

Возьмем, к примеру, сценарий, в котором вам нужно войти на несколько FTP-серверов, удалить файлы старше X дней с определенных путей.

Изначально вы можете создать простые массивы подстрок, которые я определю так:

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

Или как показано в этом примере кода.

(
  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

Теперь, 13 сайтов, это не так уж и плохо, я уверен, что вы говорите. право? Вы можете просто добавить один в конце, а затем ввести информацию и готово.

Затем вам нужно добавить имена сайтов для отчетов, чтобы вы добавили еще один термин в каждую строку в месте 5, чтобы вам не пришлось менять свою функцию.

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

Затем вы понимаете, что вам нужно будет поддерживать их в порядке по именам сайтов (или IP-адресам, но большинству людей легче запомнить эти имена, и вам нужно иметь возможность позволить другим взглянуть на них), поэтому вы меняете порядок в все 13 мест, вызов для расширения переменных и функции.

::...
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% "
::...

Тогда это только ухудшается:

  • Количество каталогов, которые вы должны проверить, используя разных пользователей, на одном сайте начинает увеличиваться.
  • Вы понимаете, что для каждого сайта, а затем и для каждого каталога, требуется разное время хранения.
  • В итоге получается 30, 40, 50 таких, и трудно вспомнить, что есть, посмотрев на конец длинной строки, скопировав их и т.д.

  • Вы перестали добавлять дополнительные пути, но иногда вам приходится удалять старые, или это вызывает проблемы, когда они исчезают, и если вы забудете обновить общее количество сайтов в списке, вы можете пропустить запуск сценария на некоторых.

  • Когда каталог добавляется или удаляется, вы должны добавлять/удалять его на каждом сайте, что затрудняет использование порядка и упрощает пропуск сайтов, поскольку их нелегко идентифицировать.

Просто, какая боль, и это даже не когда вам нужен динамический набор объектов, это все вручную.

Так что ты можешь сделать? Ну, вот что я сделал:

Я закончил тем, что прибегнул к реализации своего рода структуры или массива объектов (строк) для бедняков в моих сценариях cmd, где это необходимо.

IE структура будет "Объект объекта", который будет иметь несколько свойств, которые могут быть объектами с собственными вложенными свойствами. Так как CMD на самом деле не является объектно-ориентированным, это как кусочек, как и для массивов.

Так как пример, с которого я начинал, оказался первым местом, где я попробовал это, вы можете увидеть этот промежуточный шаг амальгамы, который я определю так:

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

Чтобы решить проблему с необходимостью переупорядочения наборов данных на лету, я подумал об использовании формы связанных списков, с которыми я играл, но так как я хотел легко добавлять элементы в каждую группу сайтов, сохраняя порядок между сайтами, я остановился на простой метод.

Вот еще один пример кода этого шага в использовании:

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

Как вы, вероятно, видите, эти структуры работают очень хорошо, когда у вас есть наборы разветвленных иерархических данных, которые необходимо применить вручную и отображать данные в определенном последовательном порядке.

Хотя, чтобы быть уверенным сегодня, я обычно делаю основу структур именем скрипта, так как считаю, что это более полезно, и может или не может использовать упорядоченные массивы в зависимости от необходимости.

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

Но как насчет того, где вам, возможно, придется собирать большие наборы динамически сгенерированных данных, группировать их по заранее подготовленным категориям и затем смешивать, чтобы сообщить об этом.

Ну, и здесь они тоже могут быть полезны, и вы можете создавать их на лету в своем коде, добавляя дополнительные свойства по мере необходимости.

В сценарии, аналогичном удалению с FTP, нам нужно проверить размеры нескольких каталогов, я собираюсь немного обойти эту одну и взгляну только на одну проверку:

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

Чтобы быть справедливым, этот пример сценария может быть не очень явным, чтобы показать, что происходит, и мне пришлось вносить изменения на лету, чтобы исправить новый стиль объекта, но по существу: он создает объекты подключения, а затем динамически расширяет их, чтобы включить подпрограмму папок и поддерживает промежуточные итоги для каждой подпапки и сайта в килобайтах, мегабайтах и гигабайтах, а также показывает, какое из значений будет отображаться после суммирования всех каталогов для данной папки и т.д. динамически.

Хотя мне пришлось немного его отредактировать, потому что это тоже более ранняя версия, я подумал, что это один из случаев, когда он может лучше всего показать преимущества. Если я найду лучший пример в одном из моих других сценариев, я также могу обновить его там.

Ответ 8

В пакете Windows нет массивов, связанных списков, ассоциативных массивов. Существует, однако, самый загадочный, причудливый, интуитивно понятный синтаксис, с которым я когда-либо сталкивался. Серьезно, для этого вам нужно использовать настоящий скриптовый язык. Все, кроме партии.

Ответ 9

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