2012-04-15 10 views
45

Stavo giocando con cmd.exe, ma nel suo aiuto non ho trovato alcuna informazione, come definire gli array.Matrici, liste concatenate e altre strutture di dati nello script cmd.exe (batch)

ho trovato, come definire variabili semplici:

set a = 10 
echo %a% 

Ma, voglio creare array, lista collegata ecc ...

affermativo, in grado di cmd.exe (I significa: fa in cmd.exe esistono eventuali parole chiave matrice)

voglio realizzare alcuni algoritmi come:?

  • bubble sort
  • rapido sorta
  • Gnome sort

ecc ...

Così, anche io voglio sapere, non ha riferimenti cmd.exe o istanze, le strutture, ecc?

Causa il suo aiuto non è completo: /?

Potrebbe Cmd.exe essere definito come completo dalla definizione di Turing-Machine? (Turing-Complete)

+1

@MatteoItalia linux shell ce l'ha, powershell (basato su .net) ce l'ha, non so su Windows CScript.exe, ce l'ha o no? –

+8

Questo dovrebbe essere divertente, ma invece è molto triste: la maggior parte delle risposte ha ottenuto 13 upvotes nonostante non abbiano risposto alla domanda. A mio avviso, una risposta che critica l'argomento solo con parole come "idiota", "terribilmente hackerata", "bizzarra" e termini simili non ha alcun valore. Nota che ** NON sto criticando le risposte né difendo Batch **. Non riesco a vedere qual è il presunto merito di quelle risposte per meritare una tale quantità di voti positivi! Mi chiedo se simili critiche alle risposte a Power Shell, script VBS, argomenti Phyton, Ruby, Pearl, PHP ecc. Ricevano quantità simili di upvotes ... :( – Aacini

+2

Ben detto, Aacini. È importante ricordare che domande come questa coprono molti casi d'uso più semplici a cui è difficile trovare risposte, la risposta ha risposto a molte delle mie domande nello spirito originale di SO. – Shane

risposta

6

Seriamente: non ho mai sentito che il gruppo abbia array, forse puoi emularli con qualche trucco strano, ma non lo definirei una buona idea.

Riferimenti/istanze/strutture sono roba per un linguaggio reale, lo scripting cmd è solo un insieme di estensioni che crescono nell'interprete primitivo che era command.com, puoi fare alcuni script di base, ma qualcosa di più complicato di un un sacco di chiamate ad altri comandi è destinato a diventare brutto e incomprensibile.

L'unico costrutto "avanzata" è il fai-da-tutto weirdo for anello, che, mescolato con le strane "regole" di sostituzione variabile (%var%, %%var, !var!, sono cose diverse a causa del parser idiota), rende la scrittura di algoritmi anche banali una raccolta di strani hack (vedi ad es. per an implementation of quicksort).

Il mio consiglio è, se si vuole fare il vostro script in modo sano, di utilizzare un linguaggio di scripting vera, e lasciare batch per semplici, rapide e hack per la compatibilità all'indietro.

+0

http://www.msfn.org/board/topic/47265-making-arrays-in-batch/ Ecco gli esempi –

+0

Questo non è un array, è una variabile a stringa singola che contiene valori delimitati da punti, divisi con un 'per' loop. Una raccolta di hack 'set' /' for', esattamente come ho detto. Faresti qualcosa di serio in queste condizioni? –

+0

Sì, il comando 'for' è il più vicino possibile. E che dolore è lavorare con. –

7

Lo script di shell di Windows non è progettato per funzionare con array, per non parlare di strutture di dati complesse. Per la maggior parte, tutto è una stringa nella shell di Windows, ma ci sono alcune cose che puoi fare per "lavorare con" gli array, come dichiarare n variabili VAR_1, VAR_2, VAR_3... usando un loop e filtrare sul prefisso VAR_, o creare una stringa delimitata e quindi usando il costrutto FOR che itera su una stringa delimitata.

Analogamente, è possibile utilizzare la stessa idea di base per creare un insieme di variabili come struct come ITEM_NAME, ITEM_DATA o w/e. Ho persino trovato this link che parla della simulazione di un array associativo in CMD.

E 'terribilmente fastidioso e scomodo quando si tratta di farlo.La shell della riga di comando non era progettata per la programmazione pesante. Sono d'accordo con @MatteoItalia - se hai bisogno di uno script serio, usa un vero linguaggio di scripting.

+0

Che cosa intendi per serio? Potrebbe essere cmd.exe definito come completo dalla definizione di Turing Machine? –

+2

@magesi CMD ha UNA cosa da fare per esso - il comando 'FOR'. Se vuoi veramente imparare CMD, masterizzalo e vai avanti. – trutheality

+0

@trutheality oppure esiste un modo per scrivere proprio cmd.exe basato su sorgenti NT4, che sono in grado :) E includere alcune nuove funzionalità it :) –

-1

Non ci sono matrici, elenchi concatenati, array associativi nel batch di Windows. C'è, tuttavia, la sintassi più arcana, bizzarra e controintuitiva che io abbia mai incontrato. Seriamente, devi usare un vero linguaggio di scripting per questo. Qualsiasi cosa tranne partita.

125

Ok. Cercherò di essere il più chiaro possibile per non essere frainteso ...

In Windows file batch un nome di variabile dovrebbe iniziare con una lettera e può includere qualsiasi carattere valido, dove caratteri validi sono: # $ '() * +, -.? @ [] _ ​​`{} ~ oltre a lettere e cifre.

Ciò significa che dal punto di vista di cmd.exe, SET NORMAL_NAME=123 corrisponde esattamente a SET A#$'()*+,[email protected][\]_{}~=123 e corrisponde a SET VECTOR[1]=123; tutti e tre sono variabili normali. In questo modo, spetta a voi a scrivere i nomi delle variabili in forma di elementi di un array:

In questo modo, echo %elem[2]% mostrerà Second one.

Se si desidera utilizzare un'altra variabile come indice, è necessario sapere che la sostituzione di variabili racchiusi tra i simboli per cento dai loro valori viene analizzato da sinistra a destra; ciò significa che:

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

non dà il risultato desiderato perché significa: mostra il valore della variabile elem[, seguita da i, seguito dal valore della variabile ].

Per risolvere questo problema è necessario utilizzare ritardata di espansione, cioè, inserire setlocal EnableDelayedExpansion comando all'inizio, racchiudere variabili indice di simboli per cento, e racchiudono gli elementi dell'array in punti esclamativi:

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

È può anche utilizzare i parametri dei comandi FOR come indici: for /L %%i in (1,1,3) do echo !elem[%%i]!. Devi usare! Indice! per memorizzare i valori negli elementi dell'array quando l'indice viene modificato all'interno di FOR o IF: set elem[!index!]=New value. Per ottenere il valore di un elemento quando l'indice cambia all'interno di FOR/IF, racchiudere l'elemento in simboli doppia percentuale e precedere il comando con call. Ad esempio, per spostare una serie di elementi array quattro posizioni verso sinistra:

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

Un altro modo per realizzare il processo precedente è di usare un ulteriore PER comando per cambiare l'espansione ritardata dell'indice da un parametro sostituibile equivalente e quindi utilizzare l'espansione ritardata per l'elemento dell'array. Questo metodo corre più veloce di chiamata precedente:

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

In questo modo, il file batch si comporta come gestisce gli array. Penso che il punto importante qui non sia quello di discutere se Batch gestisce gli array o meno, ma il fatto che si possano gestire array in file Batch in modo equivalente ad altri linguaggi di programmazione.

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

Si noti che i valori di indice non sono limitati ai numeri, ma possono essere qualsiasi stringa che contiene caratteri validi; questo punto consente di definire ciò che in altri linguaggi di programmazione è chiamato associative arrays. A this answer c'è una spiegazione dettagliata del metodo utilizzato per risolvere un problema utilizzando un array associativo. Si noti inoltre che lo spazio è un carattere valido nei nomi delle variabili, quindi è necessario prestare attenzione a non inserire spazi nei nomi delle variabili che potrebbero passare inosservati.

Ho elaborato i motivi per cui devo utilizzare la notazione di array nei file batch allo this post.

In this post c'è un file batch che legge un file di testo e memorizza gli indici delle linee in un vettore, quindi esegue una ordinamento Buble di elementi vettoriali in base al contenuto della riga; il risultato equivalente è un ordinamento sui contenuti del file.

In this post è presente un'applicazione base di base dati relazionale in batch basata su indici memorizzati nei file.

In this post esiste in Batch un'applicazione completa con più elenchi concatenati che assembla una grande struttura dati presa da una sottodirectory e la visualizza sotto forma di comando TREE.

+0

Autopromozione spudorata: [questa risposta ] (http://stackoverflow.com/a/35632781/1683264) dimostra un'implementazione in batch di 'Array.splice()' (che si basa anche sulla convenzione di denominazione 'array [n]' raccomandata). – rojo

+0

@Cornbeetle no, il mio commento precedente era l'auto promozione. La risposta di Aacini è stata grandiosa! Non stavo criticando. Stavo promuovendo un post che ho collegato ... che ho scritto ... senza vergogna ... e roba. – rojo

+0

@rojo mio male, ho frainteso! – Cornbeetle

6

Ho realizzato un'implementazione bubble sort in batch utilizzando pseudo-array qualche tempo fa. Non sono sicuro del motivo per cui lo useresti (anche se ammetterò di farlo in un altro file batch) dato che diventa piuttosto lento man mano che la dimensione dell'elenco aumenta. Era più per mettermi una piccola sfida. Qualcuno potrebbe trovare questo utile.

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

Scusami. Non mi piace il tuo riferimento "pseudo-array". Un array è principalmente un _concept_: un insieme di elementi con lo stesso nome che sono selezionati tramite un indice. Il tuo programma può gestire un array o no; non c'è una cosa del tipo "pseudo-array". Vedi il mio [link precedente] (http://stackoverflow.com/questions/10544646/dir-output-into-bat-array/10569981#10569981) per ulteriori dettagli ... – Aacini

+1

@Aacini: Esiste assolutamente una cosa del genere. Quando si simulano gli array in un linguaggio che non fornisce costrutti di array nella sintassi o nella semantica, questi possono essere definiti chiaramente e inequivocabilmente "pseudoarray". –

+0

@PreferenceBean: Mi scusi. Conosci il comando 'set/A'? In questo esempio: 'set/A resul = 9 + 6', come chiameresti il' 15' _string_ memorizzato nella variabile 'resul'? "Pseudo-numero"? "intero simulato"? Ricorda che i file batch non _non_ forniscono variabili numeriche! – Aacini

2

Il seguente programma simula vettori operazioni (array) in cmd. Le subroutine presentate in esso erano inizialmente progettate per alcuni casi speciali come la memorizzazione dei parametri del programma in un array o il looping dei nomi di file in un ciclo "for" e la loro memorizzazione in un array. In questi casi, in un blocco enabled delayed expansion, i caratteri "!", se presenti nei valori dei parametri o nel valore della variabile di ciclo "for", vengono interpretati. Ecco perché, in questi casi, i sottoprogrammi devono essere utilizzate all'interno di un disabled delayed expansion blocco:

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

È anche possibile memorizzare i parametri del programma in una "matrice" o passante attraverso i nomi di file in una directory utilizzando un "for "loop e memorizzarli in una "matrice"(senza interpretare" ! "nei loro valori) senza utilizzare i sottoprogrammi presentati nel programma precedente:

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

Per quanto riguarda questa affermazione:

ho trovato, come definire variabili semplici:

set a = 10 
echo %a% 

Questo è semplicemente sbagliato!Variabile a rimarrà vuota (supponendo era vuoto inizialmente) e echo %a% tornerà ECHO is on. Una variabile denominata aSPACE effettivamente essere impostata sul valore SPACE10.

Quindi per far funzionare il codice, è necessario sbarazzarsi dei spazi tutto il uguale-a firmare:

set a=10 
echo %a% 

per rendere l'assegnazione sicuro contro tutti i personaggi, utilizzare la sintassi citato (si supponendo hanno il command extensions abilitato, che è l'impostazione predefinita per il Windows prompt dei comandi in ogni caso):

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

per tutto il resto della tua domanda vi consiglio di leggere Aacini 's grande e comp Rehensive answer.

Problemi correlati