cmd.exe(批处理)脚本中的数组,链接列表和其他数据结构


67

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

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

set a=10
echo %a%

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

因此,它在cmd.exe中是否可以(我的意思是:cmd.exe中是否存在任何数组关键字?)

我想实现一些算法为:

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

等等...

所以,我也想知道,Cmd.exe是否具有引用或实例,结构等?

导致其帮助未满:/?

Turing-Machine定义能否将Cmd.exe定义为完整?(图灵完成)


2
@MatteoItalia linux shell具有它,powershell(基于.net)具有它,不知道Windows CScript.exe,是否具有?

13
这应该很有趣,但是却实在令人难过:尽管大多数答案没有回答问题,但大多数答案都获得了13次支持。以我的观点,仅仅用诸如“白痴”,“骇人的hackish”,“ bizarre”和类似术语之类的词来批评这个话题根本没有任何价值。请注意,我既不批评该答案,也不捍卫Batch。我只是看不出应该得到如此大量投票的答案的所谓优点是什么!我想知道对Power Shell,VBS脚本,Phyton,Ruby,Pearl,PHP等主题的类似批评性答案是否会得到类似数量的支持... :(
Aacini 2012年

2
好吧,Aacini。重要的是要记住,这样的问题涵盖了许多较简单的用例,很难找到答案,您的回答本着SO的原始精神回答了我的几个问题。
Shane

3
@meagar:我不明白您的意思...我知道所有这些编程语言都非常不同,并且每种语言都有其自己的应用领域,关注者,批评等等,但是所有这些都应该得到一定的尊重来自科学家(程序员)社区。但是,与其他编程语言的批评相比,在此站点上对批处理文件的批评非常频繁。最糟糕的是,经常会遭到这种批评,所以我只是想知道:为什么?也许不喜欢批处理文件的人比其他人更多或更少受人尊敬...
Aacini

1
@Aacini:所以有人不同意你。多么令人震惊!您会发现这在生活中经常发生。特别是当您持有少数意见时。所以您认为Batch是一种可爱的语言。大!您有权获得该意见。其他人有权认为事实并非如此。您不必惊慌。
Lightness Races in Orbit

Answers:


182

好。我会尽量清楚一点,不要被误解...

在Windows Batch文件中,变量名应以字母开头,并且可以包含任何有效字符,其中有效字符为:#$'()* +,-。?@ [] _`{}〜除字母和数字外。

这意味着从cmd.exe的角度来看,SET NORMAL_NAME=123SET A#$'()*+,-.?@[\]_{}~=123SET VECTOR[1]=123;完全相同,也相同;这三个都是正常变量。这样,由您决定以数组元素的形式编写变量名称:

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

这样,echo %elem[2]%将显示Second one

如果要使用另一个变量作为索引,则必须知道,用百分号括起来的变量用其值替换是从左到右进行解析的;这意味着:

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

无法给出理想的结果,因为这意味着:显示elem[变量的值,然后显示i,然后显示变量的值]

要解决此问题,必须使用Delayed Expansion,即setlocal EnableDelayedExpansion在开始处插入命令,将索引变量括在百分号中,并将数组元素括在感叹号中:

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

您也可以将FOR命令的参数用作索引:for /L %%i in (1,1,3) do echo !elem[%%i]!。您必须使用!index!当在FOR或IF:中更改索引时,将值存储在数组元素中set elem[!index!]=New value。要在FOR / IF内的索引更改时获取元素的值,请用双百分号将元素括起来,并在命令前加上call。例如,将一系列数组元素向左移动四个位置:

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

实现先前过程的另一种方法是使用附加的FOR命令通过等效的可替换参数更改索引的延迟扩展,然后将延迟扩展用于数组元素。此方法比以前的CALL运行得更快:

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

这样,批处理文件的行为就像管理数组一样。我认为这里的重点不是讨论Batch是否管理数组,而是讨论您可以用其他编程语言的等效方法来管理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 Sort;等效的结果是对文件内容进行排序。

这篇文章有基于存储在文件索引在批处理基本关系数据库应用程序。

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


无耻的自我提升:此答案演示了的批处理实现Array.splice()(也依赖于您建议的array[n]命名约定)。
rojo

我认为除=<NUL>(0x00)以外的任何字符都是有效的变量名,甚至包括换行符
HackingAddict1337 '20

1
@ HackingAddict1337:看来你是对的。但是,某些字符(例如冒号)会阻止访问变量值。例如:set "var:=value"echo %var:%有效,但是如果您在冒号后插入任何字符,则无法访问变量值(因为%var%扩展扩展规则)
Aacini

8

Windows Shell脚本实际上并不是设计用于数组,更不用说复杂的数据结构了。在大多数情况下,一切都是Windows shell中的字符串,但是,您可以做一些事情来“处理”数组,例如使用循环声明n变量VAR_1, VAR_2, VAR_3...并过滤前缀VAR_,或者创建定界字符串,然后使用FOR在定界字符串上迭代的构造。

同样,您可以使用相同的基本思想来创建类似structITEM_NAME, ITEM_DATA或w / e的类似结构的变量集。我什至发现了这个链接,它讨论了如何在CMD中模拟关联数组。

归根结底,这都是非常骇人听闻的,而且很不方便。命令行shell并不是为繁重的编程而设计的。我同意@MatteoItalia-如果您需要认真的脚本编写,请使用真正的脚本编写语言。


认真下你是什么意思?可以通过图灵机定义将cmd.exe定义为完整吗?

2
@magesi CMD确实有一件事情要做-FOR命令。如果您真的想学习CMD,请掌握它并继续前进。
真实性2012年

@trutheality或即有一种方法,可以根据NT4来源编写自己的cmd.exe,它们是可以的:)并包括一些新功能:)

@trutheality谢谢!:)如果我把结果放在这里,我可以打电话给你看看吗?:)

1
@magesi:一项更有用但又足够疯狂的工作是对批处理语法中的每一个奇怪的地方进行反向工程(我认为即使在Microsoft,也没有正式的规范)并修复cmd.exeWine项目中的问题。:)
Matteo Italia

7

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

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

1
对不起。我不喜欢您的“伪数组”参考。数组主要是一个概念:通过索引选择的一组具有相同名称的元素。您的程序可以管理数组,也可以不管理;没有这样的“伪数组”的东西。有关更多详细信息,请参见我之前的链接...
Aacini 2014年

3
@Aacini:绝对有这样的事情。当您使用一种不以其语法或语义提供数组构造的语言来模拟数组时,可以将它们明确且明确地称为“伪数组”。
Lightness Races in Orbit

@PreferenceBean:对不起。你知道set /A命令吗?在此示例中:set /A resul=9+6,您将如何调用存储在变量中的15 字符串resul?“伪数”?“模拟整数”?请记住,批处理文件并没有提供数字变量!
阿萨尼

@Aacini:可以这么说,您一直雇用的“对不起”是粗鲁的。我确定这不是故意的。但是您不必对MS Batch保持防御。
Lightness Races in Orbit

1
@Aacini:我对的了解不多set /A,也没有要求批处理。我只是说,世界上肯定“伪数组”这样的概念。Dave似乎在描述什么,就像一组PHP变量一样$var1 = 0; $var2 = 1; $var3 = 2; $i = 2; echo ${var$i};。那是数组吗?否。是否尝试模拟数组?是。这是一个伪数组。这就是“伪”的意思
Lightness Races in Orbit

5

认真地说:我从来没有听说过批处理包含数组,也许您可​​以用一些奇怪的技巧来模仿它们,但是我不会把它称为一个好主意。

引用/实例/结构是一种真实语言的东西,cmd脚本只是一堆扩展,这些扩展是在command.com这个非常原始的解释器上扩展的,您可以执行一些基本的脚本编写,但是比一堆调用要复杂得多其他命令注定会变得丑陋和令人费解。

唯一的“高级”结构是do-it-all weirdofor循环,它与变量替换的奇怪“规则”(%var%%%var!var!,是因为愚蠢的解析器的不同的东西),使得编写甚至琐碎的算法集合奇怪的骇客(请参阅此处的quicksort实现)。

我的提示是,如果您想以一种明智的方式进行脚本编写,请使用一种真正的脚本语言,并保留批处理功能,以进行简单,快速的破解并向后兼容。



那不是一个数组,而是一个包含点分隔值的单个字符串变量,并用for循环分割。就像我说的那样,存在set/ forhack的集合。在这种情况下,您会做严肃的事情吗?
Matteo Italia 2012年

for是的,该命令与您将获得的命令差不多。和一起工作是多么痛苦。
Dominic P

@magesi:也许吧,但是使用一种甚至没有基本的工具就可以工作的收益是什么?
Matteo Italia 2012年

@MatteoItalia就像在brainf * ck中那样有趣地做爱,当然,我不会认真地开发它,只是为了疯狂的乐趣

5

关于此声明:

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

set a = 10
echo %a%

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

因此,要使代码正常工作,您必须摆脱SPACEs等号旁边的空格:

set a=10
echo %a%

为了使分配对所有字符都是安全的,请使用引用的语法(假设您已启用命令扩展名,这始终是Windows命令提示符的默认设置):

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

对于其余的所有问题,我建议您阅读Aacini的详尽综合解答


2

以下程序模拟中的向量(数组)操作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

2

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循环并生成一个随机值以打印数组的第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的事情,即手动地使用带有增量计数器的简单的变量行,或者通过从变量快速列表中进行的简单循环来分配它们。

这对于小型二维阵列很好。

但是,我发现它对于长数据数组很痛苦,尤其是当我需要多值内容时。

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

我发现当您最终需要全面更新或添加功能所需的多种信息时,这变得很困难。

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

例如,您需要登录多个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

公平地讲,此脚本示例可能无法非常清楚地显示正在发生的事情,因此我不得不即时进行更改以修复新的对象样式,但实际上:它创建连接对象,然后动态扩展它们以包括sub文件夹,并维护每个子文件夹和站点的运行总计(以KB,MB和GB为单位),并动态汇总给定文件夹的所有目录等后,报告要报告的值。

虽然我不得不对其进行一些编辑,因为这也是它们的较早版本,但我认为这是最能体现其优势的实例之一。如果我在其他脚本之一中找到了更好的示例,则也可以在那里进行更新。


1

通用数组处理脚本

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

0

关于“批量编程中的完整性”

是的,据我所知,Batch是Turing的完整版(如果您忽略了“无限”的功能,例如无限的内存和计算时间,则可能会说Batch在理论上只是“与Turing等效”)。

所有基本布尔和算术运算符以及循环(for)和分支(if)都有。也有一个goto其允许建模循环(功能while/ do while/ for)和sub routines。可以嵌套块。Variables可以命名,存储,删除/清除,显示/写入文件。甲halt条件可以与达到exit(或GOTO EOF)。
附带说明:可以从批处理程序内部写入批处理文件,将其写入磁盘并运行(允许自我修改/自定义/子例程/状态保存和恢复)。

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

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

以我的观点,Bash(Linux)和Powershell(Windows / Linux)在这些领域中要先进得多。


-4
@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%]!
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.