@Echo Off SetLocal EnableDelayedExpansion :: Небольшое демо Call :Array new Test "'random string','123','34',567,abcd,'TEST1',test2,'',qWerty" Echo View array: Call :Array echo Test Echo ============== Call :Array shuffle Test Echo View after shuffle: Call :Array echo Test Echo ============== Call :Array get Test 4 out Echo Element no.4: Echo [4] !out! Echo ============== Echo Search: Call :Array find Test "s" f Call :Array find Test "s" f out Echo Elements = [!out!] Echo ============== pause :Array (action,name,data) :: Обработчик команды For %%A In (new,wipe,copy,add,delete,set,sort,shuffle,get,find,view,echo) Do ( If "%~1"=="%%A" ( Call :[]%%A %2 %3 %4 %5 &:: Передаём аргументы далее, исключая саму команду Exit /B 0 ) ) :: Вывод справки если команда не указана или не существует :: Ага, мой английский ужасен. Echo. Arrays for CMD Echo. Inquisitor, 2011 Echo. USAGE: Call :Array ^ ^ ^ Echo. Actions: Echo. new - Create array Echo. Call :Array new A "'one','two','three'" Echo. wipe - Delete array Echo. Call :Array wipe A Echo. copy - Create copy of array Echo. Call :Array copy A B Echo. add - Add element to existing array Echo. Call :Array add A "four" Echo. Call :Array add A "'some more','elements'" Echo. delete - Delete item from array Echo. Call :Array delete A 2 Echo. set - Change the value of the item by its number in the index Echo. Call :Array set A 1 "test" Echo. sort - Sort array Echo. Call :Array sort A Echo. shuffle - Shuffle array in random order Echo. Call :Array shuffle A Echo. get - Get the value of element to specified variable Echo. use 'index' instead of number to obtain array element count Echo. Call :Array get A 3 ^ Echo. find - Find all elements with this value Echo. if defined var name, returns array with element numbers Echo. mode: s - regular, i - case insensitive, f - fuzzy Echo. Call :Array find A "string" ^ ^ Echo. view - Print the array elements Echo. Call :Array view A Echo. echo - Print the array elements w/ index Echo. Call :Array echo A Exit /B 2 :[]new (name,data) :: Проверки If "%~1"=="" (Echo Name not specified&&Exit /B 1) If "%~2"=="" (Echo Array data is empty&&Exit /B 1) If Defined $%~1[index] (Echo Array already exist&&Exit /B 1) Set "ArrayData=%~2" Set /A i=0 :: Разбираем входящие данные и создаём элементы. Пустое значение всегда указывается как ''. :: Значение элемента всегда начинается с "=", этот трюк позволяет использовать пустые значения без проблем с неопределённой переменной For %%A In (%ArrayData:'=^"%) Do ( Set /A i+=1 Set "$%~1[!i!]==%%~A" ) Set "$%~1[index]=!i!" Exit /B :[]echo (name) :: Проверки If "%~1"=="" (Echo Name not specified&&Exit /B 1) If Not Defined $%~1[index] (Echo This array not existed&&Exit /B 1) :: Выводим массив в понятном человеку виде Echo. %~1[] For /L %%A In (1,1,!$%~1[index]!) Do ( Echo [%%A]: "!$%~1[%%A]:~1!" ) Exit /B :[]view (name) :: Проверки If "%~1"=="" (Echo Name not specified&&Exit /B 1) If Not Defined $%~1[index] (Echo This array not existed&&Exit /B 1) :: Выводим массив без лишних свистоперделок, можно перенаправить в файл, например. For /L %%A In (1,1,!$%~1[index]!) Do ( Echo.!$%~1[%%A]:~1! ) Exit /B :[]wipe (name) :: Проверки If "%~1"=="" (Echo Name not specified&&Exit /B 1) If Not Defined $%~1[index] (Echo This array not existed&&Exit /B 1) :: Очищаем все элементы и индекс For /L %%A In (1,1,!$%~1[index]!) Do ( Set "$%~1[%%A]=" ) Set "$%~1[index]=" Exit /B :[]add (name,data) :: Проверки If "%~1"=="" (Echo Name not specified&&Exit /B 1) If Not Defined $%~1[index] (Echo This array not existed&&Exit /B 1) :: Добавляем значения в массив. Можно бы было слить с :[]new, там почти тот же код, но лень Set "ArrayData=%~2" Set i=!$%~1[index]! For %%A In (,%ArrayData:'=^"%) Do ( Set /A i+=1 Set "$%~1[!i!]==%%~A" ) Set "$%~1[index]=!i!" Exit /B :[]copy (name1,name2) :: Проверки If "%~1"=="" (Echo First name not specified&&Exit /B 1) If "%~2"=="" (Echo Second name not specified&&Exit /B 1) If Not Defined $%~1[index] (Echo Can't copy nonexistent array&&Exit /B 1) If Defined $%~2[index] (Echo Array already exist&&Exit /B 1) :: Полностью переносим содержимое массива в новый For /L %%A In (1,1,!$%~1[index]!) Do ( Set "$%~2[%%A]=!$%~1[%%A]!" ) Set "$%~2[index]=!$%~1[index]!" Exit /B :[]delete (name,element) :: Проверки If "%~1"=="" (Echo Name not specified&&Exit /B 1) If "%~2"=="" (Echo Element not specified&&Exit /B 1) If Not Defined $%~1[index] (Echo This array not existed&&Exit /B 1) If Not Defined $%~1[%~2] (Echo Element not found&&Exit /B 1) :: Делаем копию и удаляем оригинал Call :[]copy "%~1" "$copy_%~1" Call :[]wipe "%~1"&&Set "$%~1[index]=0" :: Добавляем в исходный массив все элементы, кроме указанного. :: Медленно, чёрт побери. Фактически это полная пересборка. Но по другому тяжело организовать удаление со сдвигом нумерации. :: Сокращение числа вызовов :[]add, как это реализовано в :[]shuffle, не приносит заметного прироста производительности For /L %%A In (1,1,!$$copy_%~1[index]!) Do ( If Not "%%A"=="%~2" Call :[]add "%~1" '!$$copy_%~1[%%A]:~1!' ) :: Удаляем массив если в нём ничего не осталось и чистим за собой If "!$%~1[index]!"=="0" Set "$%~1[index]=" Call:[]wipe "$copy_%~1" Exit /B :[]set (name,element,data) :: Проверки If "%~1"=="" (Echo Name not specified&&Exit /B 1) If "%~2"=="" (Echo Element not specified&&Exit /B 1) If Not Defined $%~1[index] (Echo This array not existed&&Exit /B 1) If Not Defined $%~1[%~2] (Echo Element not found&&Exit /B 1) :: Устанавливаем новое значение. Set "ElementValue=%~3" :: Перестраховка Set "$%~1[%~2]==!ElementValue:'=!" Exit /B :[]sort (name) :: Проверки If "%~1"=="" (Echo Name not specified&&Exit /B 1) If Not Defined $%~1[index] (Echo This array not existed&&Exit /B 1) :: Сбрасываем массив во временный файл. Без него никак, выхлоп функции нельзя передать через пайп Set tmpDump=%Temp%\a_%random%%random%.tmp Call :[]view "%~1">"%tmpDump%" :: Сортировка. Find служит для вывода не печатаемых в обычной ситуации пустых строк. Call :[]wipe "%~1"&&Set "$%~1[index]=0" For /F "eol= delims=] tokens=1,*" %%A In ('Sort "%tmpDump%"^|Find /V /N ""') Do ( Call :[]add "%~1" "'%%B'" ) Del "%tmpDump%" Exit /B :[]shuffle (name) :: Проверки If "%~1"=="" (Echo Name not specified&&Exit /B 1) If Not Defined $%~1[index] (Echo This array not existed&&Exit /B 1) :: Делаем копию, которая служит источником данных во время рандомной выборки Call :[]copy "%~1" "$shuffle_%~1" :: Удаляем исходный массив и заново наполняем его Call :[]wipe "%~1"&&Set "$%~1[index]=0" Set "rndSequence=,"&&Set i=0 :rndseq :: Почему не цикл? Чтобы не играться с экранированием вложенных скобок, да и более читаемо Echo !random!!random!>nul Set /A rnd=1+!$$shuffle_%~1[index]!*%random%/32768 If "!rndSequence!"=="!rndSequence:,%rnd%,=!" ( Set rndSequence=!rndSequence!!rnd!, Set /A i+=1 ) If !i! GEQ !$$shuffle_%~1[index]! ( Set "ArrayData=" :: Формируем строку для добавления For %%A In (%rndSequence%) Do ( Call :StrLen ArrayData &:: Если данные превысили половину макс. размера переменной, добавляем в массив и чистим Set ArrayData=!ArrayData!,'!$$shuffle_%~1[%%~A]:~1!' If !ArrayData.strlen! GEQ 4096 ( Call :[]add "%~1" "!ArrayData!" Set "ArrayData=" ) ) Call :[]add "%~1" "!ArrayData!" &:: Добавляем остаток в случае большого массива или все данные для маленького Call :[]wipe "$shuffle_%~1" Exit /B ) GoTo :rndseq :[]get (name,element,out:variable) :: Проверки If "%~1"=="" (Echo Name not specified&&Exit /B 1) If "%~2"=="" (Echo Element not specified&&Exit /B 1) If "%~3"=="" (Echo Output name not specified&&Exit /B 1) If Not Defined $%~1[index] (Echo This array not existed&&Exit /B 1) If Not Defined $%~1[%~2] (Echo Element not found&&Exit /B 1) Set "%~3=!$%~1[%~2]:~1!" Exit /B :[]find (name,search_string,mode,out:variable) :: Проверки If "%~1"=="" (Echo Name not specified&&Exit /B 1) If Not Defined $%~1[index] (Echo This array not existed&&Exit /B 1) If "%~2"=="" (Echo Search string is empty&&Exit /B 1) If "%~3"=="" (Echo Search mode not specified&&Exit /B 1) Set SearchMode=/i/s/f/ If "!SearchMode!"=="!SearchMode:/%~3/=!" (Echo Invalid search mode, see help&&Exit /B 1) :: Сбрасываем массив во временный файл. При нескольких тысячах элементов есть разница, :: вызывать find на каждую строку или лишь единожды Set tmpDump=%Temp%\a_%random%%random%.tmp Call :[]view "%~1">>"%tmpDump%" Set "SearchResult=" :: Обычный поиск, нужно точное совпадение, вплоть до регистра символов. If "%~3"=="s" ( For /F "tokens=1 delims=:" %%A In ('Findstr /XNC:"%~2" "%tmpDump%"') Do (Set SearchResult=!SearchResult! %%A) ) :: Обычный, но независимо от регистра If "%~3"=="i" ( For /F "tokens=1 delims=:" %%A In ('Findstr /XINC:"%~2" "%tmpDump%"') Do (Set SearchResult=!SearchResult! %%A) ) :: Нечёткий поиск, регистронезависим и находит все включения строки If "%~3"=="f" ( For /F "tokens=1 delims=:" %%A In ('Findstr /INC:"%~2" "%tmpDump%"') Do (Set SearchResult=!SearchResult! %%A) ) :: Если указано имя переменной для вывода, выдаём последовательность номеров туда, иначе - просто показываем строки и номера. If Not "%~4"=="" If Defined SearchResult (Set "%~4=!SearchResult:~1!") Else (Set "%~4=") If "%~4"=="" If Defined SearchResult ( Echo. %~1[], searched for "%~2" For %%A In (!SearchResult:~1!) Do (Echo [%%A]: "!$%~1[%%A]:~1!") ) Else ( Echo Not found ) Del "%tmpDump%" Exit /B :StrLen (name,out:name.strlen] :: Получение размера строки по имени переменной Set StrLen.S=A!%~1! Set StrLen=0 For /L %%P In (12,-1,0) Do ( Set /A "StrLen|=1<<%%P" For %%I In (!StrLen!) Do If "!StrLen.S:~%%I,1!"=="" Set /A "StrLen&=~1<<%%P" ) Set "%~1.strlen=!StrLen!" Exit /B