73

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

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

set a=10
echo %a%

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

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

我想实现一些算法:

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

ETC...

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

导致它的帮助不完整:/?

图灵机定义可以将 Cmd.exe 定义为完整吗?(图灵完备)

4

11 回答 11

194

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

在 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,然后是]变量的值。

要解决这个问题,必须使用延迟扩展,即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 是否管理数组,而是您可以以与其他编程语言等效的方式管理 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 中有一个完整的多链表应用程序,它组装了一个从子目录中获取的大型数据结构,并以 TREE 命令的形式显示它。

于 2012-04-16T02:15:34.890 回答
8

Windows shell 脚本确实不是为处理数组而设计的,更不用说复杂的数据结构了。在大多数情况下,Windows shell 中的所有内容都是字符串,但是,您可以做一些事情来“处理”数组,例如使用循环声明n变量并过滤前缀,或者创建一个分隔字符串然后使用迭代分隔字符串的构造。VAR_1, VAR_2, VAR_3...VAR_FOR

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

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

于 2012-04-15T22:01:21.767 回答
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
于 2014-03-05T10:14:53.740 回答
5

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

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

唯一的“高级”结构是 do-it-all 怪人for循环,它与变量替换的奇怪“规则”(%var%, %%var, !var!, 是不同的东西,因为愚蠢的解析器)混合在一起,使得编写即使是微不足道的算法也成为一个集合奇怪的 hacks(参见例如这里的 quicksort 的实现)。

我的建议是,如果您想以一种理智的方式编写脚本,请使用真正的脚本语言,并为简单、快速的 hack 和向后兼容性保留批处理。

于 2012-04-15T21:54:58.850 回答
5

关于此声明*

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

set a = 10
echo %a%

这简直是​​错误的!变量a将保持为空(假设它最初为空)echo %a%并将返回ECHO is on.一个名为的变量aSPACE实际上将设置为 value SPACE10

因此,要使代码正常工作,您必须去掉SPACEs等号周围的:

set a=10
echo %a%

要使分配对所有字符安全,请使用带引号的语法(假设您启用了命令扩展名,无论如何这是 Windows 命令提示符的默认设置):

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

对于您的所有其他问题,我建议您阅读Aacini的出色而全面的答案


*) 此声明同时已被删除

于 2017-03-17T15:18:47.277 回答
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

下面我们制作了一些 Dumb Pseudo Arrays 和一个手动排序的 Pseudo Array,并创建了一个 Ordered Pseudo Array 来捕获 DIR 命令的输出。

我们还采用 Dumb Pseudo Arrays 并将它们转换为 Ordered 数组(之后删除原始 Dumb Pseudo Array 变量)。

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

最后,我们通过对值 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 为单位维护每个子文件夹和站点的运行总计,并在动态汇总给定文件夹的所有目录等后报告哪些值。

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

于 2018-12-14T20:10:43.177 回答
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
于 2016-01-21T10:29:42.583 回答
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]
于 2020-01-10T11:21:05.080 回答
1

论《批处理编程中的图灵完备性》

是的,据我所知,Batch 是图灵完备的(如果您忽略“无限”功能,如无限内存和计算时间;因此有人可能会争辩说,Batch 只是“理论上的图灵等效”)。

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

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

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

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

于 2020-05-14T16:54:23.597 回答
-1

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

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

这个想法是一个文件可以被视为一个数组,甚至可以通过FOR命令支持原生的、易于使用的数组迭代。

array.txt

these
are
items
in
an
array

对于二维数组,您可以使用上述文件的文件夹。(名称类似于0.txtthrough 100.txt)。请记住,您可能需要有一个单独的文件来索引它们,因为数组目录不一定按照您在 for 循环中所期望的方式排序,实际上更像是一个哈希映射,其中string -> [string].

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


对于混合数组(其中有些项目是其他数组,有些是字符串),您可以将文件格式化如下:

complex-array.txt

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

和这样的文件夹:

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

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


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

我不确定这对性能的不利影响,而且批处理无论如何都很慢,所以也许没关系。(我相当确定变量名修改策略更快,因为值会在内存中保留更长时间)

于 2021-08-31T21:39:29.350 回答
-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%]!
于 2016-11-02T10:21:55.227 回答