2013-06-21 8 views
11

IFブロック内でSHIFTを使用しようとすると、予期しない結果が表示されます。この使い方:ブロック内でWindowsのバッチシフトが機能しない場合

@echo off 

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

私は次の取得:しかし

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

、私はこのコードを使用する場合:私は必要

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

@echo off 

echo %1 
shift 
echo "shifted" 
echo %1 

を私はこれを取得2番目の出力が、私はループすることができますので、ロジックブロックでそれで。私はここでジョンの答えに似た何かを実装しようとしています:Using parameters in batch files at DOS command lineしかし、私はいくつかの問題を抱えています。なぜこうなった?

+0

[シフトはバッチスクリプトでは機能しません] http://social.technet.microsoft.com/Forums/scriptcenter/en-US/7bbbe8df-e3c0-46ab-aede-396c2b6f6184/shift-doesnt-work-in-batch-script) – piebie

答えて

10

これは、行が解析されたときに%1が展開され、括弧で囲まれたIF構文全体がすべて1回のパスで解析されるため、これは予想される動作です。したがって、改行が解析されるまではSHIFTの結果を見ることはできません。これは、括弧で囲まれたブロックを終了するまで発生しません。

%var%を使用して環境変数を展開しても同じ問題が発生します。環境変数を使用すると、SETLOCAL EnableDelayedExpansionを使用して遅延拡張を有効にしてから、!var!を使用して問題を回避できます。しかし、遅延拡張を使用してパラメータを拡張する方法はありません。

EDIT 2013-06-21 - これに匹敵する方法はありませんが、比較的遅い簡単な方法があります。

CALLを使用して%を倍増させると、回線を強制的に再解析することができます。それはあなたがそれを残すためにGOTOを使用した後、あなたは、コードのブロック内でバックジャンプすることができることを意味して

@echo off 

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



EDIT 2016年7月15日 code in Vladislav's answerは、悪い習慣です。それは単に機能しません。 GOTOは、解析されたコードブロックを直ちに殺します。条件はその後、ブロック全体がスキップされ、FALSEの場合

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

:あなたはだけでなく、ウラジスラフのコードを書かれているかもしれません。 条件がTRUEの場合、GOTOは直ちにブロックを強制終了し、実行は通常:trueラベル(ブロックコンテキストなし)で正常に実行されます。最後の余分な)は単に無視されます。

この構文でELSEを追加することはできません。次のような結果は得られません。

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

偽の場合、ELSEブロックだけが実行されます。 TRUEの場合、先頭のブロックが実行され、すぐに終了します。コードの残りの部分が実行され、) else (行は無視されます。これは、パーサーがコマンドを期待していて、アクティブなカッコブロックがスタックにない場合、がREMとして機能するためです。

上記のように(またはVladislavが行ったように)括弧で囲まれたコードブロック内にラベルを絶対入れないことを強くお勧めします。以下は

同じことを行うには良い(単純で誤解を招くない)方法です:

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

:skip 

あなたは

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

:notP 
echo not /p 

:endIf 
2
いくつかの余分なラベルや型GOTOとIF/THEN/ELSE概念をサポートすることができました

実際にブロックすると外にシフトすることができます:

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

:perform_shift 
shift 
echo "shifted" 
goto:aftersift; 


:end 
+1

「作品の種類」、それは非常に悪い形です。カッコで囲まれたブロック内をジャンプしていることを意味しますが、これはうまくいきません。ブロック内でGOTOを使用する瞬間、ブロックを終了します。かっこはそこにありますが、目的はありません。この場合は重要ではありませんが、ブロックがFORループの一部である場合は非常に重要です。 FORループ内でGOTOを実行すると、ループが強制終了されます。 – dbenham

+0

この回答が良いアイデアではない理由と、より良いことを提案する理由については、[編集2016-07-15 in my answer](http://stackoverflow.com/a/17241649/1012053)を参照してください。効果的に同じことをする方法。 – dbenham

関連する問題