Exe (批处理)脚本中的数组、链表和其他数据结构

我在玩 cmd.exe,但在它的帮助下,我没有找到任何信息,如何定义数组。

我发现了如何定义简单变量:

set a=10
echo %a%

但是,我想创建数组,链表等..。

那么,它是否能够在 cmd.exe 中运行(我的意思是: 在 cmd.exe 中是否存在任何数组关键字?)

我想实现一些算法如下:

  • 气泡排序
  • 快速排序
  • 侏儒那种

等等。

因此,我还想知道,Cmd.exe 是否有引用或实例、结构等?

因为它的帮助不充分:/?

Exe 可以被图灵机定义为 full 吗? (图灵完成)

109424 次浏览

严肃地说: 我从来没有听说过批处理有数组,也许你可以用一些奇怪的技巧来模拟它们,但是我不认为这是一个好主意。

引用/实例/结构是真正语言的东西,cmd 脚本只是一堆扩展,它们是在 command.com 这个非常原始的解释器上发展起来的,你可以做一些基本的脚本,但是任何比一堆对其他命令的调用更复杂的东西注定会变得丑陋和难以理解。

唯一的“高级”结构是全能怪异的 for循环,它混合了变量替换的奇怪“规则”(%var%%%var!var!,因为愚蠢的解析器而是不同的东西) ,使得编写即使是微不足道的算法成为一个奇怪黑客的集合(参见这里的 快速排序的实现)。

我的建议是,如果你想以一种理智的方式编写脚本,那么使用一个 真的脚本语言,并且为了简单、快速的编程和向后兼容性而保留批处理。

Windows shell 脚本实际上并不适用于数组,更不用说复杂的数据结构了。大多数情况下,所有东西都是 windows shell 中的一个字符串,但是,你可以做一些事情来“处理”数组,比如使用循环声明 n变量 VAR_1, VAR_2, VAR_3...并在前缀 VAR_上过滤,或者创建一个带分隔符的字符串,然后使用在带分隔符的字符串上迭代的 FOR结构。

类似地,你可以使用相同的基本思想来创建一组类似 struct 的变量,比如 ITEM_NAME, ITEM_DATA或者 w/e。我甚至发现了 这个链接,它讨论在 CMD 中模拟一个关联数组。

归根结底,这一切都是可怕的骇客和不方便的。命令行 shell 不是为繁重的编程而设计的。我同意@matteoItalia 的观点——如果你需要严肃的脚本,那就使用真正的脚本语言。

好的,我会尽量说清楚,以免被误解。

在 Windows 批处理文件中,变量名应该以字母开头,并且可以包含任何有效字符,其中 有效字符是: # $’() * + ,-。?@ [] _‘{} ~ 除了字母和数字。

这意味着从 cmd.exe 的角度来看,SET NORMAL_NAME=123SET 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,后面跟着 ]变量的值。

要解决这个问题,你必须使用 延迟扩张,也就是说,在开头插入 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 中更改索引时,在数组元素中存储值。若要在 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 文件 举止就像它一样管理数组。我认为这里的重点不在于讨论 Batch 是否管理数组,而在于您可以使用与其他编程语言相同的方式来管理 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%

注意,索引值不限于数字,但它们可以是包含有效字符的任何字符串; 这一点允许定义在其他编程语言中称为 联合数组联合数组的内容。在 这个答案,有一个用关联数组解决问题的方法的详细说明。还要注意,空格在变量名中是有效的字符,因此必须注意不要在变量名中插入可能被忽视的空格。

我详细阐述了在 这篇文章的 Batch 文件中必须使用数组表示法的原因。

这篇文章中,有一个批处理文件,它读取一个文本文件并将行的索引存储在一个向量中,然后根据行内容对向量元素进行 Buble 排序; 等效的结果是对文件内容进行排序。

这篇文章中,基于存储在文件中的索引,Batch 中有一个基本的关系数据库应用程序。

这篇文章中,在 Batch 中有一个完整的多链表应用程序,它从一个子目录中组装一个大型数据结构,并以 TREE 命令的形式显示它。

不久前,我使用伪数组批处理了一个冒泡排序实现。 不确定您为什么要使用它(尽管我承认在另一个批处理文件中使用它) ,因为随着列表大小的增加,它变得非常慢。更多的是给自己一个小小的挑战。 有些人可能会觉得这很有用。

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

下面的程序模拟 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
@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%]!

关于这项声明:

我发现了如何定义简单变量:

set a = 10
echo %a%

这是完全错误的!变量 a将保持为空(假设它最初是空的) ,并且 echo %a%将返回 ECHO is on.一个名为 aSPACE的变量实际上将被设置为值 SPACE10

因此,为了让代码正常工作,你必须去掉 SPACEs的等号:

set a=10
echo %a%

为了让赋值对所有字符都安全,使用引号语法(假设启用了 命令扩展,这是命令提示符的默认设置) :

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

对于所有其余的问题,我建议阅读 Aacini的伟大和全面的 回答


*)此声明为 被剪掉了

TLDR:

我偶然想到了使用“ For”循环和“ set”命令来允许解析变量,允许我创建伪数组,有序和链表样式,更重要的是,伪对象类似于结构。

一个典型的批处理伪数组,以及如何解析:

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

下面我们创建了一些哑伪数组和一个手工排序的伪数组,另外还创建了一个有序的伪数组来捕获 DIR 命令的输出。

我们还使用哑伪数组并将其转换为有序数组(之后删除原始哑伪数组变量)。

然后,我们手动更新所有有序数组以包含更多元素。

最后,我们通过对值7到9进行预定义的 For L Loop,并生成一个 Random 值来打印数组的第4个示例值,从而动态地报告 Array 中的一些值。

注:

我创建了一个变量来保存添加成员的方法,从而使添加成员变得更简单。

我指出这一点,因为它应该使我们很容易看到我们如何从有序数组到伪对象的小跳转。

@(
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 的事情,一个简单的变量行与一个增量计数器,手动,或通过一个简单的循环分配它们从一个快速变量列表。

这对于小型2D 数组来说是很好的。

然而,我发现这对于长数据阵列来说是一种痛苦,特别是当我需要多值内容时。

更不用说我什么时候需要动态地匹配和填充那些多维数组中的内容了,在这些数组中,简单的用法不再适用。

我发现,当您最终需要多个信息数组来全面更新或添加特性时,就变得很困难了。

因为这样的数组本质上是一个子字符串列表,您需要将其作为变量导出,并且添加或更改它们的顺序意味着更改代码。

以一个场景为例,您需要登录到多个 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

公平地说,这个脚本示例可能没有非常明确地显示正在发生的事情,我不得不动态地修改一个新的对象样式,但本质上: 它创建连接对象,然后动态扩展它们以包括子文件夹,并维护每个子文件夹和网站的运行总数(KB,MB 和 GB) ,以及在动态汇总给定文件夹的所有目录之后报告的值。

虽然我不得不编辑它一点,因为这也是这些的早期版本,我认为这是一个实例,它可能最好地显示的好处之一。如果我在其他脚本中找到一个更好的例子,我可能也会在那里更新它。

通用数组处理脚本

@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][ a-z ]
  • [ Sort-str]开关:
    [ /A:Groupname][ /F:Filepath.ext][ /D]
  • [ Find-searchstring]
    在数组中搜索提供的字符串值。
  • 开关: [ /A:Groupname]

关于“批处理编程中的图灵完备性”的课题

是的,据我所知,Batch 是完整的图灵(如果你忽略了“无限的”特性,比如无限的内存和计算时间; 所以有人可能会说 Batch 只是“理论上的图灵等价物”)。

有所有基本的布尔运算符和算术运算符,以及循环(for)和分支(if)。还有一个 goto功能,它允许建模循环(while/do while/for)和 sub routines。块嵌套是可能的。Variables可以命名、存储、删除/清除、显示/写入文件。使用 exit(或 goto eof)可以达到 halt条件。
作为旁注: 可以从批处理程序内部编写批处理文件,将其写入磁盘并运行(允许自我修改/定制/子例程/状态保存和恢复)。

但是没有无限的内存存储空间。在计算中只能使用32位算法。显然,运行批处理文件的计算机也有硬件和物理限制(只有有限的时间、速度或空间)。

应该注意的是,您提到的所有“高级”概念都不是“批处理编程语言”的一部分。没有集成类、对象、记录/结构、数组、链表、堆栈、队列等的概念。也没有提供任何默认算法,比如排序等(除非考虑到 sortfindStrmore等带有管道的算法)。随机化也是非常基本的 %RANDOM%变量。
如果您需要这些概念,您需要使用上面提到的给定的基本语言元素对它们进行建模(或者使用一些库/第三方批处理文件)。
当然,call不仅可以执行批处理文件,还可以执行计算机上的任何补充程序,然后返回批处理执行(通过文件、标准 I/O 流或退出/错误级别代码进行通信)。这些程序可以用更高级的语言编写,以更方便的方式提供这些东西。

在我看来,Bash(Linux)和 Powershell(Windows/Linux)在这些领域要先进得多。

我以前使用过的一种方法是 使用文件作为数组,使用文件夹作为数组的字典。

现在听我说完——刚开始对你来说可能很愚蠢,但是它有一些优点。

其思想是,可以将文件视为数组,甚至可以使用 FOR命令支持本机的、易于使用的数组迭代。

array.txt

these
are
items
in
an
array

对于一个2d 数组,您可以使用一个文件夹的文件,如上所述。(名称如 0.txt100.txt)。请记住,您可能需要一个单独的文件来对它们进行索引,因为数组的目录不一定按照 for 循环中所期望的方式排序,而且实际上更像是一个散列映射,其中是 string -> [string]

或者,我确信解析 csv 不会太难(请记住字符串值中的逗号和制表符!)!;) )


对于混合数组(其中一些项是其他数组,另一些是字符串) ,可以将文件格式设置如下:

complex-array.txt

"value
"1
"2
\path.txt
\path2.txt

还有这样的文件夹:

complex-array\path.txt
complex-array\path2.txt

如果一行以一个字符开头,那么它就是一个值,而另一个是路径(可能是相对于这个文件)。当然,这可能是递归的。


不过有个大问题。该脚本在每次运行之前留下了(可能)需要清理的文件。(我之所以这么说是因为假设在这个脚本运行时计算机不会被拔掉是不安全的)。

我不确定性能下降,这一批是相当缓慢的无论如何,所以也许它不会有什么问题。(我相当肯定变量名混淆策略会更快,因为这些值会在内存中保留更长的时间)