Matrizes, listas vinculadas e outras estruturas de dados no script cmd.exe (lote)

Eu estava jogando com cmd.exe, mas em sua ajuda eu não encontrei nenhuma informação, como definir matrizes.

Eu encontrei, como definir variables ​​simples:

set a = 10 echo %a% 

Mas, eu quero criar matrizes, lista ligada, etc …

Então, é capaz em cmd.exe (quero dizer: existe em cmd.exe existem palavras-chave da matriz?)

Eu quero realizar alguns algoritmos como:

  • Tipo de bolha
  • ordenação rápida
  • gnome sort

etc …

Então, eu também quero saber, o Cmd.exe tem referências ou instâncias, estruturas etc?

Porque sua ajuda não está cheia em: /?

Poderia o Cmd.exe ser definido como completo pela definição de Turing-Machine? (Turing-Completo)

   

Está bem. Vou tentar ser o mais claro possível para não ser mal interpretado …

Nos arquivos de lote do Windows, um nome de variável deve começar com uma letra e pode include qualquer caractere válido, onde os caracteres válidos são: # $ ‘() * +, -.? @ [] _ ​​`{} ~ Além de letras e dígitos.

Isso significa que, do ponto de vista do cmd.exe, SET NORMAL_NAME=123 é exatamente o mesmo que SET A#$'()*+,-.?@[\]_{}~=123 e também o mesmo que SET VECTOR[1]=123 ; todas as três são variables ​​normais. Dessa forma, cabe a você escrever nomes de variables ​​na forma de elementos de matriz:

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

Dessa forma, echo %elem[2]% mostrará o Second one .

Se você quiser usar outra variável como índice, deve saber que a substituição de variables ​​contidas em símbolos de porcentagem por seus valores é analisada da esquerda para a direita ; Isso significa que:

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

não dá o resultado desejado porque significa: mostre o valor do elem[ variável, seguido por i , seguido pelo valor da variável ] .

Para resolver esse problema, você deve usar a Expansão com atraso , ou seja, inserir o comando setlocal EnableDelayedExpansion no início, include variables ​​de índice em símbolos de porcentagem e colocar os elementos da matriz em pontos de exclamação:

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

Você também pode usar parâmetros de comandos FOR como índices: for /L %%i in (1,1,3) do echo !elem[%%i]! . Você deve usar! Index! para armazenar valores em elementos de matriz quando o índice é alterado dentro de um FOR ou IF: set elem[!index!]=New value . Para obter o valor de um elemento quando o índice é alterado dentro de FOR / IF, coloque o elemento em símbolos de porcentagem dupla e preceda o comando com a call . Por exemplo, para mover um intervalo de elementos de matriz para quatro lugares à esquerda:

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

Outra maneira de alcançar o processo anterior é usar um comando FOR adicional para alterar a expansão atrasada do índice por um parâmetro substituível equivalente e, em seguida, usar a expansão atrasada para o elemento de matriz. Este método é executado mais rapidamente do que a chamada anterior:

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

Desta forma, o arquivo em lote se comporta como ele gerencia matrizes. Acho que o ponto importante aqui não é discutir se o Batch gerencia matrizes ou não, mas o fato de você poder gerenciar matrizes em arquivos Batch de maneira equivalente a outras linguagens de programação.

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

Observe que os valores de índice não estão limitados a números, mas podem ser qualquer string que contenha caracteres válidos; Este ponto permite definir o que em outras linguagens de programação são chamadas matrizes associativas . Nesta resposta, há uma explicação detalhada do método usado para resolver um problema usando uma matriz associativa. Observe também que o espaço é um caractere válido em nomes de variables, portanto, você deve prestar atenção para não inserir espaços em nomes de variables ​​que possam passar despercebidos.

Eu elaborei as razões pelas quais tenho que usar a notação de array em arquivos em lote neste post .

Neste post há um arquivo em lote que lê um arquivo de texto e armazena os índices das linhas em um vetor, em seguida, faz um tipo Buble de elementos vetoriais com base no conteúdo da linha; o resultado equivalente é uma sorting sobre o conteúdo do arquivo.

Neste post há um aplicativo básico de Base de Dados Relacional no Lote baseado em índices armazenados em arquivos.

Neste post, há um aplicativo completo de linked list múltipla no Batch que monta uma estrutura de dados grande tirada de um subdiretório e a exibe na forma de comando TREE.

O shell script do Windows realmente não foi projetado para trabalhar com matrizes, quanto mais estruturas de dados complexas. Na maior parte, tudo é uma string no shell do Windows, mas existem algumas coisas que você pode fazer para “trabalhar com” arrays, como declarar n variables VAR_1, VAR_2, VAR_3... usando um loop e filtrando o prefixo VAR_ ou criar uma cadeia delimitada e, em seguida, utilizar a construção FOR que itera sobre uma cadeia delimitada.

Da mesma forma, você pode usar a mesma ideia básica para criar um conjunto de variables ​​como ITEM_NAME, ITEM_DATA ou w / e. Eu até encontrei este link que fala sobre a simulação de um array associativo no CMD.

É tudo terrivelmente agressivo e inconveniente quando se trata disso. O shell de linha de comando simplesmente não foi projetado para programação pesada. Eu concordo com @MatteoItalia – se você precisar de scripts sérios, use uma linguagem de script real.

Falando sério: nunca ouvi dizer que o lote tenha arrays, talvez você possa imitá-los com algum truque estranho, mas não seria uma boa ideia.

Referências / instâncias / structs são coisas para um idioma real, cmd scripting é apenas um monte de extensões que cresceram sobre o intérprete muito primitivo que era command.com, você pode fazer alguns scripts básicos, mas qualquer coisa mais complicada do que um monte de chamadas para outros comandos estão condenados a se tornarem feios e incompreensíveis.

O único construtor “avançado” é o esquisito do-it-all for loop, que, misturado com as estranhas “regras” de substituição de variables ​​( %var% , %%var !var! São coisas diferentes por causa do parser idiota) ), torna a escrita de algoritmos ainda triviais uma coleção de hacks estranhos (veja, por exemplo, aqui para uma implementação do quicksort ).

Minha dica é, se você quiser fazer o seu script de uma maneira sã, usar uma linguagem de script real e deixar o lote para hacks simples e rápidos e para compatibilidade com versões anteriores.

Fiz uma implementação de sorting de bolhas em lote usando pseudoarray há um tempo atrás. Não tenho certeza por que você o usaria (embora eu admita fazer isso em outro arquivo de lote), já que fica muito lento conforme o tamanho da lista aumenta. Foi mais para me colocar um pequeno desafio. Alguém pode achar isso útil.

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

Sobre esta afirmação:

Eu encontrei, como definir variables ​​simples:

 set a = 10 echo %a% 

Isso é simplesmente errado! A variável a permanecerá vazia (supondo que estivesse vazia inicialmente) e o echo %a% retornará o ECHO is on. Uma variável chamada SPACE será realmente definida como o valor SPACE 10 .

Portanto, para que o código funcione, você deve se livrar dos espaços ao redor do sinal de igual:

 set a=10 echo %a% 

Para tornar a tarefa segura contra todos os caracteres, use a syntax entre aspas (supondo que você tenha as extensões de comando ativadas, que é o padrão para o prompt de comando do Windows):

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

Para todo o resto da sua pergunta, eu recomendo ler a grande e abrangente resposta de Aacini .

O programa a seguir simula operações de vetores (arrays) em cmd . As sub-rotinas apresentadas foram inicialmente projetadas para alguns casos especiais, como armazenar os parâmetros do programa em uma matriz ou fazer um loop através de nomes de arquivos em um loop ” for ” e armazená-los em uma matriz. Nesses casos, em um bloco de enabled delayed expansion , os caracteres ” ! ” – se presentes nos valores dos parâmetros ou no valor da variável de loop ” for ” – seriam interpretados. É por isso que, nesses casos, as sub-rotinas devem ser usadas dentro de um bloco de disabled delayed expansion :

 @echo off rem The subroutines presented bellow implement vectors (arrays) operations in CMD rem Definition of a vector : 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 ) 

Também é possível armazenar os parâmetros do programa em uma “matriz” ou percorrer os nomes de arquivos em um diretório usando um loop ” for ” e armazená-los em uma “matriz” (sem interpretar ” ! ” Em seus valores) sem usar a apresentação sub-rotinas no programa acima:

 @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 

Não há matrizes, listas vinculadas, matrizes associativas no lote do Windows. Há, no entanto, a syntax mais misteriosa, bizarra e contra-intuitiva que já encontrei. Sério, você precisa usar uma linguagem de script real para isso. Qualquer coisa menos lote.

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