Массивы, связанные списки и другие структуры данных в сценарии cmd.exe (пакетной)
Я играл с cmd.exe, но в его справке я не нашел никакой информации, как определить массивы.
Я нашел, как определить простые переменные:
set a=10
echo %a%
Но я хочу создать массивы, связанный список и т.д...
Итак, может ли он в cmd.exe (я имею в виду: существует ли в cmd.exe какие-либо ключевые слова массива?)
Я хочу реализовать некоторые алгоритмы как:
- пузырьковая сортировка
- быстрая сортировка
- сортировка гномов
так далее...
Итак, я также хочу знать, есть ли в Cmd.exe ссылки или экземпляры, структуры и т. Д.?
Потому что его помощь не полная в: /?
Может ли Cmd.exe быть определен как полный по определению машины Тьюринга? (Тьюринг-Полная)
13 ответов
Хорошо. Я постараюсь быть максимально ясным, чтобы не быть неправильно понятым...
В пакетных файлах Windows имя переменной должно начинаться с буквы и может содержать любой допустимый символ, где допустимые символы: #$'()*+,-.?@[]_ `{}~, Кроме букв и цифр.
Это означает, что с точки зрения cmd.exe, SET NORMAL_NAME=123
точно так же, как SET A#$'()*+,-.?@[\]_{}~=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]!
, Вы должны использовать! Индекс! хранить значения в элементах массива при изменении индекса внутри 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.
Сценарии оболочки Windows на самом деле не предназначены для работы с массивами, не говоря уже о сложных структурах данных. По большей части, все это строка в оболочке Windows, но есть некоторые вещи, которые вы можете сделать, чтобы "работать" с массивами, например, объявление n
переменные VAR_1, VAR_2, VAR_3...
используя цикл и фильтрацию по префиксу VAR_
или создание строки с разделителями, а затем с помощью FOR
конструкция, которая перебирает строку с разделителями.
Точно так же вы можете использовать ту же самую базовую идею для создания подобного структуре набора переменных, таких как ITEM_NAME, ITEM_DATA
или ж / д. Я даже нашел эту ссылку, которая говорит об имитации ассоциативного массива в CMD.
Это все ужасно хакерское и неудобное, когда дело доходит до этого. Оболочка командной строки просто не была предназначена для тяжелого программирования. Я согласен с @MatteoItalia - если вам нужен серьезный сценарий, используйте настоящий язык сценариев.
Я сделал реализацию пузырьковой сортировки в пакетном режиме с использованием псевдо-массивов некоторое время назад. Не уверен, почему вы используете его (хотя я признаю это в другом пакетном файле), так как он становится довольно медленным с увеличением размера списка. Это было больше, чтобы поставить перед собой небольшую задачу.Кто-то может найти это полезным.
:: 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
По поводу этого утверждения:
Я нашел, как определить простые переменные:
set a = 10 echo %a%
Это просто неправильно! переменная a
останется пустым (если изначально он был пустым) и echo %a%
вернусь ECHO is on.
Переменная называется a
SPACE фактически будет установлен на значение SPACE10
,
Таким образом, чтобы код работал, вы должны избавиться от пробелов вокруг знака равенства:
set a=10
echo %a%
Чтобы сделать присваивание безопасным для всех символов, используйте синтаксис в кавычках (предположим, что у вас включены расширения команд, которые в любом случае используются по умолчанию для командной строки Windows):
set "a=1&0"
echo(%a%
На все остальные вопросы я рекомендую прочитать отличный и исчерпывающий ответ Aacini.
Серьезно говоря: я никогда не слышал, чтобы в пакете были массивы, может быть, вы можете эмулировать их каким-то странным трюком, но я бы не назвал это хорошей идеей.
Ссылки / экземпляры / структуры - это материал для реального языка, сценарии cmd - это просто набор расширений, выросших по сравнению с очень примитивным интерпретатором, который был command.com, вы можете выполнить некоторые базовые сценарии, но что-нибудь более сложное, чем набор обращений к другие команды обречены стать безобразными и непонятными.
Единственная "продвинутая" конструкция - это чудак for
цикл, который смешивается со странными "правилами" подстановки переменных (%var%
, %%var
, !var!
, это разные вещи из-за идиотского парсера), делает написание даже тривиальных алгоритмов набором странных хаков (см., например, здесь для реализации быстрой сортировки).
Мой совет: если вы хотите делать свои сценарии разумным способом, используйте настоящий язык сценариев и оставьте пакет для простых, быстрых хаков и обратной совместимости.
Следующая программа имитирует операции векторов (массивов) в 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'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
)
Также возможно сохранить параметры программы в "массиве" или перебрать имена файлов в каталоге, используя "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'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
TLDR:
Я натолкнулся на идею использования цикла "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'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
Чтобы быть справедливым, этот пример сценария может быть не очень явным, чтобы показать, что происходит, и мне пришлось вносить изменения на лету, чтобы исправить новый стиль объекта, но по существу: он создает объекты подключения, а затем динамически расширяет их, чтобы включить подпрограмму папок и поддерживает промежуточные итоги для каждой подпапки и сайта в килобайтах, мегабайтах и гигабайтах, а также показывает, какое из значений будет отображаться после суммирования всех каталогов для данной папки и т. д. динамически.
Хотя мне пришлось немного его отредактировать, потому что это тоже более ранняя версия, я подумал, что это один из случаев, когда он может лучше всего показать преимущества. Если я найду лучший пример в одном из моих других сценариев, я также могу обновить его там.
Ниже приведен пример обработки списков в виде массива с несколькими индексированными экземплярами элемента.
В основе этого метода лежит использование метапеременных For.~n
модификатор для удаления идентификатора экземпляра из строки, чтобы оставшуюся часть можно было использовать напрямую. Это позволяет использовать несколько элементов в списке для доступа к одному и тому же базовому значению без необходимости определения большого количества переменных. Дополнительно сохраняется возможность легкого удаления элемента из списка благодаря наличию идентифицирующего номера экземпляра.
Структура переменных требует, чтобы любые дополнительные экземпляры начинались с\
в качестве разделителя, например:
\1\itemName
Здесь строка «itemName» соответствует базовой переменной, содержащей нужные данные. Присутствие в переменной, содержащей список элементов, повторяемых через цикл for:
@Echo off
Setlocal EnableDelayedExpansion
Set "itemName=some value"
Set List="itemName" "someOtherItem" "\1\itemName"
For %%I in (!List!)Do (
If not "!%%~nI!" == "" (
Echo(%%~I=!%%~nI!
)Else Echo(%%~I not defined
)
Pause
По теме "Полнота по Тьюрингу в пакетном программировании"
Да, пакетная обработка является полной по Тьюрингу, насколько мне известно (и если вы проигнорируете "неограниченные" функции, такие как неограниченная память и время вычислений; так что можно утверждать, что пакетная обработка является только "теоретически эквивалентом Тьюринга").
Есть все основные логические и арифметические операторы, а также циклы (for
) и ветвления (if
). Также естьgoto
функциональность, позволяющая моделировать петли (while
/do while
/for
) а также sub routines
. Возможно вложение блоков.Variables
может быть назван, сохранен, удален / очищен, отображен / записан в файл. Аhalt
состояние может быть достигнуто с exit
(или goto eof).
В качестве примечания: можно записать командный файл из пакетной программы, записать его на диск и запустить (позволяет самостоятельно изменять / настраивать / подпрограммы / сохранять и восстанавливать состояние).
Однако нет неограниченного хранилища памяти. В вычислениях можно использовать только 32-битную арифметику. И, очевидно, компьютер, на котором запущен командный файл, также имеет аппаратные и физические ограничения (только конечное время, скорость или пространство).
Следует отметить, что все упомянутые вами концепции "более высокого уровня" не являются частью "языка пакетного программирования". Не существует концепции интегрированных классов, объектов, записей / структур, массивов, связанных списков, стеков, очередей и т. Д. Также отсутствуют какие-либо алгоритмы по умолчанию, такие как сортировка и т. Д. (Кроме, может быть, еслиsort
или findStr
, more
и др. с трубами). Рандомизация также очень проста с%RANDOM%
переменная.
Если вам нужны эти концепции, вам нужно смоделировать их с помощью указанных базовых языковых элементов, о которых я упоминал выше, самостоятельно (или использовать некоторые библиотечные / сторонние пакетные файлы).
Конечно, можноcall
не только командных файлов, но и любой дополнительной программы на компьютере, а затем вернуться к пакетному выполнению (обмен данными через файл, стандартные потоки ввода-вывода или коды выхода / уровня ошибки). Эти программы могли быть написаны на языках более высокого уровня, которые предоставляют подобные вещи более удобным способом.
С моей точки зрения Bash
(Linux) и Powershell
(Windows/Linux) гораздо более продвинуты в этих областях.
Универсальный скрипт обработки массива
@ECHO OFF
Set "UseErr=Echo/&Echo/Usage Error - Ensure command extensions and Delayed Expansion are enabled with: &Echo/Setlocal EnableExtensions EnableDelayedExpansion&Echo/ or from the command line:&Echo/CMD /V:On /K&Exit /B 1"
If Not "!Comspec!"=="%Comspec%" (%UseErr%)
(Set "GRPNm="&Set "TAB= "&Set "S_Offset="&Set "mode="&Set "#STDOut="&Set "nGRPNm="&Set "#Order="&Set "#Help="&Set "Inset="&Set "Usage=Echo/###&Exit /B 1") > Nul 2> Nul
(Set "SwParam="&Set "SwFParam="&Set "#ORP#=0"&Set "#FP#=0"&Set "Inset="&Set "#STDOut=0"&Set "GRPNm="&Set "!GRPNm!="&Set "SubEl="&Set "FlNm=%~n0"& Set "Mode="&Set "FindV=") > Nul 2> Nul
If "%~1"=="" (
Echo/&Echo/Modes:&Echo/ [Def]!TAB!!TAB!!TAB!Define, modify or clear an array.
Echo/ [Def]!TAB!!TAB!!TAB!Switches:!TAB![/A:Groupname] [/F:Filepath.ext] [/D] [/O:Index#Arg] [/E:Element Sub value] [[element0] ~ [element#]]
Echo/ [Sort-int]!TAB!!TAB!Sorts array by lowest or highest value using /L or /H switches
Echo/ [Sort-int]!TAB!!TAB!Switches:!TAB![/A:Groupname] [/N:New Groupname] [/L^|/H] [/D]
Echo/ [Sort-str]!TAB!!TAB!Sorts an array or text files string values using alphanumerical order of sort: [0-9][a-z]
Echo/ [Sort-str]!TAB!!TAB!Switches:!TAB![/A:Groupname] [/F:Filepath.ext] [/D]
Echo/ [Find] !TAB!!TAB!Searches an array for the string value supplied.
Echo/ [Find] [searchstring]!TAB!Switches: [/A:Groupname]&Echo/
%Usage:###=/M:Mode required&Echo/[Def][Sort-int^|str][Find-Value]%
) Else Call :GetArgs %*
If Errorlevel 1 Exit /B 1
If "!Mode!"=="" (%Usage:###=/M:Mode required&Echo/[Def][Sort-int^|str][Find-Value]%)
Call :!Mode! %* 2> Nul || (%Usage:###=Invalid Mode or switch error for /M:!Mode!&Echo/[Def][Sort-int^|str][Find-Value]%)
Exit /B 0
:str
Set "Usage=Echo/###&Echo/Call !FlNm! ["/F:filepath.ext" ^| "/A:Array Group Name"] & Exit /B 1"
Set "#!GRPNm!=0"
If "!#FP#!"=="1" (
(For /F "UseBackQ Delims=" %%G in (`Type "!FilePath!" ^| Sort`)Do (
For %%x in ("!GRPNm![!#%GRPNm%!]") Do (
Setlocal DisableDelayedExpansion
Endlocal & Set "%%~x=%%~G"
If "!#STDOut!"=="1" Echo/%%~x=%%~G
)
Set /A "#!GRPNm!+=1"
)) 2> Nul || (%Usage:###:=Echo/Invalid Filepath:"!FilePath!"%)
Exit /B 0
)
If Not "!#FP#!"=="1" (For /F "Tokens=1,2 Delims==" %%G in ('Set !GRPNm![')Do Echo/%%H)>"%TEMP%\__Sort.txt"
(For /F "UseBackQ Delims=" %%G in (`Type "%TEMP%\__Sort.txt" ^| Sort`)Do (
For %%x in ("!GRPNm![!#%GRPNm%!]") Do (
Setlocal DisableDelayedExpansion
Endlocal & Set "%%~x=%%~G"
If "!#STDOut!"=="1" Echo/%%~x=%%~G
)
Set /A "#!GRPNm!+=1"
)
)
Del /Q "%TEMP%\__Sort.txt"
Exit /B 0
:Find
Set "Usage=Echo/###&Echo/Call !FlNm! [/M:Find-Searchstring] [/A:Group Name]&Exit /B 1"
If "!FindV!"=="" (%Usage:###=/M:Find-Value Required%)
(For /F "Tokens=1,2 Delims==" %%i in ('Set !GRPNm![') Do Echo/"%%j"|"%__AppDir__%findstr.exe"/LIC:"!FindV!" > Nul 2> Nul && (Echo/!FindV! found:&Echo/%%~i=%%~j))
Exit /B 0
:Int
Set "Usage=Echo/###&Echo/Call !FlNm! [/M:Sort-Int] [/A:Group Name] [/N:New Group Name] [Sort-Int] [/H^|/L]&Echo/Call %~n0 [/M:Sort-Int] [/A:Groupname] [Sort-Int] [/H^|/L]&Exit /B 1"
If "!#Help!"=="1" (%Usage:###=/M:Sort-Int Usage:%)
If "!nGRPNm!"=="" Set "nGRPNm=!GRPNm!"
If Not "%#Order%"=="" (Call :Sort%#Order% !nGRPNm! #!nGRPNm! !Inset!) Else (%Usage:###=Sort Order Required /H or /L%)
Exit /B 0
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Subroutines for Population of Arrays with numeric values in sorted order.
:sortL <Element_VarName> <Element_Index_VarName> <Variable Names containing the values to be Sorted and Populated to the Array>
Set "%2=0"
FOR %%P In (%*) DO If Not "%%P"=="%1" If Not "%%P"=="%2" If Not "%%P"=="" (
Set "%1[!%2!]=!%%P!"
Set /A "%2+=1"
)
For /L %%a In (1,1,!%2!)Do (
Set /A "S_Offset=%%a - 1"
For /L %%b IN (0,1,%%a)Do (
If not %%b==%%a For %%c in (!S_Offset!)Do (
IF !%1[%%c]! LEQ !%1[%%b]! (
Set "tmpV=!%1[%%c]!"
Set "%1[%%c]=!%1[%%b]!"
Set "%1[%%b]=!tmpV!"
))))
Set /A %2-=1
If "!#STDOut!"=="1" For /L %%G in (0 1 !%2!)Do Echo/%1[%%G]=!%1[%%G]!
Exit /B 0
:sortH <Element_VarName> <Element_Index_VarName> <Variable Names containing the values to be Sorted and Populated to the Array>
Set "%2=0"
FOR %%P In (%*) DO If Not "%%~P"=="%~1" If Not "%%~P"=="%2" If Not "%%P"=="" (
Set "%1[!%2!]=!%%~P!"
Set /A "%2+=1"
)
For /L %%a In (1,1,!%2!)Do (
Set /A "S_Offset=%%a - 1"
For /L %%b IN (0,1,%%a)Do (
If not %%b==%%a For %%c in (!S_Offset!)Do (
If Not !%1[%%c]! LSS !%1[%%b]! (
Set "tmpV=!%1[%%c]!"
Set "%1[%%c]=!%1[%%b]!"
Set "%1[%%b]=!tmpV!"
))))
Set /A %2-=1
If "!#STDOut!"=="1" For /L %%G in (0 1 !%2!)Do Echo/%1[%%G]=!%1[%%G]!
Exit /B 0
:Def
Set "Usage=Echo/###&Echo/Call !FlNm! [/M:Def] [/A:Groupname] ["element0" ~ "element#"] [/F:Filepath.ext] [/E:"Element sub value"]&Echo/ - Assign each line in the given filepath plus element parameters to the Array&Echo/Call %~n0 [/M:Def] [/A:Groupname] REM : Clears the Array for the given Group Name&Echo/Call %~n0 [/M:Def] [/A:Groupname] [element] [element] [/O:Index#Arg] REM : Overides Elements from the index supplied&Exit /B 0"
If "!#ORP#!"=="1" Echo/!SwParam!|"%__AppDir__%findstr.exe" /RX [0-9]* > Nul 2> Nul
If not "!SwParam!"=="" If Errorlevel 1 (%Usage:###=O:!SwParam! #Arg invalid. Only Integers accepted.%)
If "!GRPNm!"=="" (%Usage:###=/A:Groupname Required%)
If "!#ORP#!"=="1" Set "#!GRPNm!=0"
If "!#%GRPNm%!"=="" Set "#!GRPNm!=0"
If "%#FP#%"=="1" (
If exist "!FilePath!" (
For /F "Delims=" %%G in (!FilePath!)Do If Not "%%~G"=="" (
For %%x in ("!GRPNm![!#%GRPNm%!]")Do (
Setlocal DisableDelayedExpansion
If "%#STDOut%"=="1" Echo/%%~x=%%~G
Endlocal & Set "%%~x=%%G"
)
Set /A "#!GRPNm!+=1" > Nul
)
) Else (%Usage:###=/F:!FilePath! Invalid path%)
)
If not "!Inset!"=="" (
For %%G in (!Inset!)Do (
For %%x in ("%GRPNm%[!#%GRPNm%!]")Do (
Setlocal DisableDelayedExpansion
If "%#STDOut%"=="1" Echo/%%~x=%%~G
Endlocal & Set "%%~x=%%~G"
)
If Not "!SubEL!"=="" Set "%%~G=!SubEl!"
Set /A "#!GRPNm!+=1" > Nul
)
) Else (
If Not "%#FP#%"=="1" (
For /F "Tokens=1,2 Delims==" %%I in ('Set %GRPNm%')Do Set "%%~I=" > Nul 2> Nul
Set "#!GRPNm!=" > Nul 2> Nul
)
)
Exit /B 0
:GetArgs
If Not "!#Help!"=="1" If "%~1" == "" (
If /I "!Mode!"=="int" If "!GRPNm!"=="" (Echo/Call %~n0 [/M:Sort-int] [/A:GroupName] [/H^|/L] [/D]&%Usage:###=/A:Groupname Required%)Else If /I "!Mode!"=="int" (For /F "Tokens=1,2 Delims==" %%G in ('Set !GRPNm![')Do Set "Inset=!Inset! %%G") > Nul 2> Nul || (%Usage:###=Usage Error - /A:!GRPNm! is not defined%)
If /I "!Mode!"=="str" If "!GRPNm!"=="" (Echo/Call %~n0 [/M:Sort-str] [/A:GroupName] [/N:New Groupname] [/F:Filepath.ext] [/D]&%Usage:###=/A:Groupname Required%)Else If /I "!Mode!"=="str" (For /F "Tokens=1,2 Delims==" %%G in ('Set !GRPNm![')Do Set "Inset=!Inset! %%G") > Nul 2> Nul || (%Usage:###=Usage Error - /A:!GRPNm! is not defined%)
Exit /B 0
) Else If "%~1" == "" Exit /B 0
Set "Param=%~1"
Echo/"!Param!"|"%__AppDir__%findstr.exe" /LIC:"Find-" > Nul 2> Nul && ((Set "FindV=!Param:/M:Find-=!"&Set "Mode=Find")&Shift&Goto :GetArgs)
Echo/"!Param!"|"%__AppDir__%findstr.exe" /LIC:"/M:" > Nul 2> Nul && (
Set "MODE=!Param:*/M:=!"& Echo/"!Mode!"|"%__AppDir__%findstr.exe" /LIC:"Sort-" > Nul 2> Nul && (Set "Mode=!Mode:*Sort-=!")
If "!Param:*/M:=!"=="" (
Echo/&Echo/Modes:&Echo/ [Def]!TAB!!TAB!Define, modify or clear an array.
Echo/ [Sort-int]!TAB!Sorts array by lowest or highest value using /L or /H switches
Echo/ [Sort-str]!TAB!Sorts an array or text files string values using alphanumerical order of sort: [0-9][a-z]
Echo/ [Find:Value]!TAB!Searches an array for the string value supplied.&Echo/
%Usage:###=/M:Mode required&Echo/[Def][Sort-int^|str][Find-Value]%
)
Shift&Goto :GetArgs
)
Echo/"%~1"|"%__AppDir__%findstr.exe" /LIC:"/H" > Nul 2> Nul && (Set "#Order=H"&Shift&Goto :GetArgs)
Echo/"%~1"|"%__AppDir__%findstr.exe" /LIC:"/L" > Nul 2> Nul && (Set "#Order=L"&Shift&Goto :GetArgs)
Echo/"%~1"|"%__AppDir__%findstr.exe" /LIC:"/D" > Nul 2> Nul && (Set "#STDOut=1"&Shift&Goto :GetArgs)
Echo/"%~1"|"%__AppDir__%findstr.exe" /LIC:"/F:" > Nul 2> Nul && ((If Not "!Param:/F:=!"=="" (Set "#FP#=1"&Set "FilePath=!Param:/F:=!")Else %Usage:###=/F:Filepath.ext not Supplied%)&Shift&Goto :GetArgs)
Echo/"%~1"|"%__AppDir__%findstr.exe" /LIC:"/N:" > Nul 2> Nul && (Set "nGRPNm=!Param:*/N:=!"&(If "!Param:*/N:=!"=="" %Usage:###=/N:New Group Name required%)&Shift&Goto :GetArgs)
Echo/"%~1"|"%__AppDir__%findstr.exe" /LIC:"/A:" > Nul 2> Nul && (Set "GRPNm=!Param:*/A:=!"&(If "!Param:*/A:=!"=="" %Usage:###=/A:Group Name required%)&Shift&Goto :GetArgs)
Echo/"%~1"|"%__AppDir__%findstr.exe" /LIC:"/O:" > Nul 2> Nul && (Set "SwParam=!Param:*/O:=!"&(If Not "!Param:/O:=!"=="" (Set "#ORP#=1")Else %Usage:###=/O:#Arg not Supplied%)&Shift&Goto :GetArgs)
Echo/"%~1"|"%__AppDir__%findstr.exe" /LIC:"/E:" > Nul 2> Nul && (Set "SubEl=!Param:*/E:=!"&(If "!Param:/S:=!"=="" %Usage:###=/E:Sub Element not Supplied%)&Shift&Goto :GetArgs)
Set Inset=!Inset! %1
Shift&Goto :GetArgs
- Режимы:
- [
Def
] Определите, измените или очистите массив. - [
Def
] Переключатели: [/A:Groupname
] [/F:Filepath.ext
] [/D
] [/O:Index#Arg
] [/E:Element Sub value
]"element0"
~"element#"
- [
Sort-int
] Сортирует массив по наименьшему или наибольшему значению, используя/L
или/H
переключатели - [
Sort-int
] Переключатели:
[/A:Groupname
] [/N:New Groupname
] [/L
|/H
] [/D
] - [
Sort-str
]
Сортировка строковых значений массива или текстовых файлов в алфавитно-цифровом порядке сортировки: [0-9][az] - [
Sort-str
] Переключатели:
[/A:Groupname
] [/F:Filepath.ext
] [/D
] - [
Find-searchstring
]
Ищет в массиве указанное строковое значение. - [
Find-searchstring
] Переключатели: [/A:Groupname
]
Один из подходов, который я использовал раньше, - это использование файлов как массивов и папок как словарей массивов.
А теперь выслушайте меня - сначала это может показаться вам глупым, но в этом есть свои достоинства.
Идея состоит в том, что файл можно рассматривать как массив и даже поддерживать встроенную, простую в использовании итерацию массива с помощью
array.txt
these are items in an array
Для 2-мерного массива вы можете использовать папку с файлами, подобную указанной выше. (с такими именами, как
В качестве альтернативы, я уверен, что разобрать csv будет не так уж сложно (помните о запятых и табуляциях внутри строковых значений!;))
Для смешанного массива (где некоторые элементы являются другими массивами, а некоторые - строками) вы можете отформатировать файл примерно так:
complex-array.txt
"value "1 "2 \path.txt \path2.txt
и такую папку:
complex-array\path.txt
complex-array\path2.txt
где, если строка начинается с одного символа, это значение, а другого - путь (возможно, относительно этого файла). Конечно, это может быть рекурсивно.
Однако есть одна большая проблема. Сценарий оставляет файлы, которые (вероятно) необходимо очищать перед каждым запуском. (Я говорю это раньше, потому что небезопасно предполагать, что компьютер не будет отключен от сети во время выполнения этого сценария).
Я не уверен в недостатках производительности, и пакетная обработка в любом случае довольно медленная, поэтому, возможно, это не имеет значения. (Я почти уверен, что тактика изменения имени переменной работает быстрее, поскольку значения будут оставаться в памяти дольше)
В пакете Windows нет массивов, связанных списков, ассоциативных массивов. Однако существует самый загадочный, причудливый, нелогичный синтаксис, который я когда-либо встречал. Серьезно, вам нужно использовать настоящий язык сценариев для этого. Все, кроме партии.
@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%]!