2013-06-21 1 views
11

Quando provo a utilizzare SHIFT all'interno di un blocco IF, sto vedendo alcuni risultati imprevisti. L'utilizzo di questo:Windows batch shift non funziona se il blocco

@echo off 

if "%1"=="/p" (
    echo %1 
    shift 
    echo "shifted" 
    echo %1 
) 

ottengo il seguente:

C:\>ex.bat /p HAI 
/p 
"shifted" 
/p 

Tuttavia, quando uso questo codice:

@echo off 

echo %1 
shift 
echo "shifted" 
echo %1 

ottengo questo:

C:\>ex.bat /p HAI 
/p 
"shifted" 
HAI 

ho bisogno la seconda uscita, ma in un blocco logico in modo da poter loopover r it. Sto cercando di implementare qualcosa di simile alla risposta di Jon qui: Using parameters in batch files at DOS command line, ma ho qualche problema. Perché sta succedendo?

+0

Ho finito per trovare la risposta qui: [Maiusc non funziona nello script batch] (http://social.technet.microsoft.com/Forums/scriptcenter/en-US/7bbbe8df-e3c0-46ab-aede-396c2b6f6184/shift-doesnt-work-in-batch-script) – piebie

risposta

10

Questo è il comportamento previsto perché %1 viene espanso quando viene analizzata la riga e l'intero costrutto IF parentesi viene analizzato tutto in un unico passaggio. Quindi non è possibile visualizzare il risultato dello SHIFT fino a quando non viene analizzata una nuova riga, che non si verificherà fino a quando non si è lasciato il blocco tra parentesi.

Lo stesso problema si verifica quando si espande una variabile di ambiente utilizzando %var%. Con le variabili di ambiente è possibile aggirare il problema abilitando l'espansione ritardata utilizzando SETLOCAL EnableDelayedExpansion e quindi utilizzando !var!. Ma non esiste un modo analogo per espandere i parametri usando l'espansione ritardata.

EDIT 2013-06-21 - Non c'è un modo simile, ma esiste un modo semplice relativamente lento.

È possibile forzare la ripetizione della linea utilizzando CALL e il raddoppio dello %.

@echo off 

if "%1"=="/p" (
    echo %1 
    shift 
    echo "shifted" 
    call echo %%1 
) 



EDIT 2016/07/15 Il code in Vladislav's answer è una cattiva pratica, in quanto implica che si può tornare indietro all'interno di un blocco di codice dopo che hai utilizzato GOTO di lasciarlo. Questo semplicemente non funziona. GOTO uccide immediatamente tutti i blocchi di codice analizzati. Si potrebbe anche avere scritto il codice di Vladislav come:

@echo off 
if "%1"=="/p" (
    goto :true 
    :true 
    echo "%1" 
    shift 
    echo shifted 
    echo "%1" 
) 

Se la condizione è falsa, allora l'intero blocco viene saltato. Se la condizione è TRUE, GOTO uccide immediatamente il blocco, quindi l'esecuzione viene eseguita normalmente su: true label (nessun contesto di blocco). L'extra ) alla fine viene semplicemente ignorato.

Nota che non è possibile aggiungere un ELSE con questo costrutto. Quanto segue non dà il risultato desiderato:

@echo off 
if "%1"=="/p" (
    goto :true 
    :true 
    echo "%1" 
    shift 
    echo shifted 
    echo "%1" 
) else (
    echo This will execute regardless whether arg1 is /p or not 
) 

Se FALSE, viene eseguito solo il blocco ELSE. se TRUE, quindi il blocco superiore viene eseguito e immediatamente ucciso. Il resto del codice viene eseguito e la riga ) else ( viene ignorata perché le funzioni ) come REM se il parser si aspetta un comando e non ci sono blocchi parentesi attivi nello stack.

Consiglio vivamente di non gettare mai un'etichetta all'interno di un blocco di codice in parentesi come mostrato sopra (o come ha fatto Vladislav).

Qui di seguito è un modo migliore (più semplice e non fuorvianti) per fare la stessa cosa:

@echo off 
if not "%1"=="/p" goto :skip 
echo "%1" 
shift 
echo shifted 
echo "%1" 

:skip 

Si potrebbe sostenere un/THEN/concetto ELSE IF con alcune etichette extra e GOTO

@echo off 
if not "%1"=="/p" goto :notP 
echo "%1" 
shift 
echo shifted 
echo "%1" 
goto :endIf 

:notP 
echo not /p 

:endIf 
2

in realtà è possibile eseguire solo il turno esterno se blocco:

@echo off 
if "%1"=="/p" (
    echo "%1" 
    goto:perform_shift; 
:aftersift 
    echo "%1" 
) 
goto:end; 

:perform_shift 
shift 
echo "shifted" 
goto:aftersift; 


:end 
+1

Quel "tipo di lavoro", ma è una forma pessima. Implica che stai saltando all'interno di un blocco tra parentesi, che non funziona. Nel momento in cui usi GOTO all'interno di un blocco, uccidi il blocco; le parentesi ci sono, ma non servono a nulla. Questo non è importante in questo caso, ma è molto importante se il blocco fa parte di un ciclo FOR. Se si esegue il GOTO all'interno di un ciclo FOR, il ciclo viene ucciso. – dbenham

+0

vedere [MODIFICA 2016-07-15 nella mia risposta] (http://stackoverflow.com/a/17241649/1012053) per ulteriori informazioni sul motivo per cui questa risposta non è una buona idea, nonché un suggerimento per una migliore modo per fare efficacemente la stessa cosa – dbenham