2009-05-25 10 views
30

長期実行バッチファイルを実行しています。バッチファイルの最後にいくつかのコマンドを追加する必要があることに気付きました(既存のコンテンツに変更を加えず、余分なコマンドを追加するだけです)。たいていのバッチファイルがインクリメンタルに読み込まれ、1つずつ実行されるので、これを行うことは可能ですか?または、システムがファイルの内容全体を読み取り、ジョブを実行しますか?実行中のバッチファイルの変更

+3

あなた奨めの愛SO迅速な応答。すでにバッチを実行し始めました。>質問を投稿しました>答えを得ました>実行完了前にファイルを編集しました! –

+0

また、バッチファイルを削除または名前を変更すると、現在の命令がエラーを終了した瞬間がスローされます。「バッチファイルが見つかりません」。 –

答えて

28

私はちょうどそれを試みたが、私の直感に対して、それは(Windows XP上で)最後に新しいコマンドを拾っ

私は、ファイルを実行した

echo Hello 
pause 
echo world 

を含むバッチファイルを作成し、それは一時停止しながら、

echo Salute 

を追加ポーズcontineする入力し、それを保存し、押下、すべての3つのプロンプトがコンソールにエコーしました。

だから、それに行ってください!

15

コマンドインタープリタは、バッチファイル内の行位置を記憶しています。現在実行中の行位置の後でバッチファイルを変更する限り、大丈夫です。

これを変更すると、それは変なこと(コマンドの繰り返しなど)を開始します。

+1

それはどこに書かれていますか、これはあなた自身の経験からですか? – Benoit

+2

これは私の経験では真実です。 – UnhandledExcepSean

+0

実際には、パーサーポインタはファイル内の同じインデックスにとどまるので、インデックスの前にテキストを追加/削除すると、命令ポインタの下にあるものが移動します。確かに奇妙なことが起こります。 – cat

3

ほとんどの場合、cmd.exeは現在のファイル位置(行位置だけでなく)を覚えており、呼び出しごとにファイル位置を非表示スタックにプッシュします。それは背後に実行していると、実際のファイル位置の前に、あなただけが何を必要とする一方で、あなたはあなたのファイルを編集することができることを意味し

...

自己修正バッチ
の小さなサンプルそれはラインを変更set value=1000連続

@echo off 
setlocal DisableDelayedExpansion 
:loop 
REM **** the next line will be changed 
set value=1000 
rem *** 
echo ---------------------- 
echo The current value=%value% 
<nul set /p ".=Press a key" 
pause > nul 
echo(
(
call :changeBatch 
rem This should be here and it should be long 
) 
rem ** It is neccessary, that this is also here! 
goto :loop 
rem ... 
:changeBatch 
set /a n=0 
set /a newValue=value+1 
set /a toggle=value %% 2 
set "theNewLine=set value=%newValue%" 
if %toggle%==0 (
    set "theNewLine=%theNewLine% & rem This adds 50 byte to the filesize.........." 
) 
del "%~f0.tmp" 2> nul 
for /F "usebackq delims=" %%a in ("%~f0") DO (
    set /a n+=1 
    set "line=%%a" 
    setlocal EnableDelayedExpansion 
    if !n!==5 (
     (echo !theNewLine!) 
    ) ELSE (
     (echo !line!) 
    ) 
    endlocal 
) >> "%~f0.tmp" 
(
    rem the copy should be done in a parenthesis block 
    copy "%~f0.tmp" "%~f0" > nul 
    if Armageddon==TheEndOfDays (
    echo This can't never be true, or is it? 
) 
) 
echo The first line after the replace action.... 
echo The second line comes always after the first line? 
echo The current filesize is now %~z0 
goto :eof 
+0

+1それは可能なことの楽しい例です。 – dbenham

7

ジェブの例では、楽しみがたくさんあるが、それは追加または削除されたテキストの長さに大きく依存します。私は反直観的な結果は、 "あなたがそれを変更すると、それは奇妙なことをやり直す(コマンドを繰り返すなど)"と言いました。

jebのコードを変更して、実行中のバッチファイルの先頭でさまざまな長さの動的コードを自由に変更する方法を示しました。動的セクション全体が完全に各繰り返しに置き換えられます。それぞれのダイナミックラインの先頭には、非干渉の;が付いています。これにより、暗黙的なEOL=;オプションのため、FOR /Fが動的コードを削除することができます。

特定の行番号を検索する代わりに、動的コードの開始位置を特定するための特定のコメントを探します。これは維持しやすくなります。

私は等号の行を使用してコードを無害に埋め込み、伸縮を許しています。コンマ、セミコロン、等号、スペース、タブ、改行のいずれかの組み合わせを使用できます。 (もちろん、パディングはセミコロンで始めることはできません。)括弧内の等号はコード展開を可能にします。括弧の後の等号はコードの収縮を可能にします。

FOR /Fは空白行を削除します。この制限は、FINDSTRを使用して各行の先頭に行番号を付け、ループ内の接頭辞を取り除くことで解決できます。しかし、余分なコードは物事を遅くするので、コードが空白行に依存しない限り、それは価値がありません。

@echo off 
setlocal DisableDelayedExpansion 
echo The starting filesize is %~z0 
:loop 
echo ---------------------- 
::*** Start of dynamic code *** 
;set value=1 
::*** End of dynamic code *** 
echo The current value=%value% 
:: 
::The 2 lines of equal signs amount to 164 bytes, including end of line chars. 
::Putting the lines both within and after the parentheses allows for expansion 
::or contraction by up to 164 bytes within the dynamic section of code. 
(
    call :changeBatch 
    ============================================================================== 
    ============================================================================== 
) 
================================================================================ 
================================================================================ 
set /p "quit=Enter Q to quit, anything else to continue: " 
if /i "%quit%"=="Q" exit /b 
goto :loop 
:changeBatch 
(
    for /f "usebackq delims=" %%a in ("%~f0") do (
    echo %%a 
    if "%%a"=="::*** Start of dynamic code ***" (
     setlocal enableDelayedExpansion 
     set /a newValue=value+1, extra=!random!%%9 
     echo ;set value=!newValue! 
     for /l %%n in (1 1 !extra!) do echo ;echo extra line %%n 
     endlocal 
    ) 
) 
) >"%~f0.tmp" 
:: 
::The 2 lines of equal signs amount to 164 bytes, including end of line chars. 
::Putting the lines both within and after the parentheses allows for expansion 
::or contraction by up to 164 bytes within the dynamic section of code. 
(
    move /y "%~f0.tmp" "%~f0" > nul 
    ============================================================================== 
    ============================================================================== 
) 
================================================================================ 
================================================================================ 
echo The new filesize is %~z0 
exit /b 

上記はうまくいきますが、動的コードをファイルの最後にあるサブルーチンに移動するとはるかに簡単です。このコードは、制限なしに、またパディングの必要なく、拡張および縮小できます。 FINDSTRは、動的部分を削除する際のFOR/Fよりもはるかに高速です。動的な行には、セミコロン(ラベルも含む)を接頭辞として安全に付けることができます。次に、FINDSTR/Vオプションを使用して、セミコロンで始まる行を除外し、新しい動的コードを単に追加することができます。

@echo off 
setlocal DisableDelayedExpansion 
echo The starting filesize is %~z0 

:loop 
echo ---------------------- 
call :dynamicCode1 
call :dynamicCode2 
echo The current value=%value% 
call :changeBatch 
set /p "quit=Enter Q to quit, anything else to continue: " 
if /i "%quit%"=="Q" exit /b 
goto :loop 

:changeBatch 
(
    findstr /v "^;" "%~f0" 
    setlocal enableDelayedExpansion 
    set /a newValue=value+1, extra=!random!%%9 
    echo ;:dynamicCode1 
    echo ;set value=!newValue! 
    echo ;exit /b 
    echo ; 
    echo ;:dynamicCode2 
    for /l %%n in (1 1 !extra!) do echo ;echo extra line %%n 
    echo ;exit /b 
    endlocal 
) >"%~f0.tmp" 
move /y "%~f0.tmp" "%~f0" > nul 
echo The new filesize is %~z0 
exit /b 

;:dynamicCode1 
;set value=33 
;exit /b 
; 
;:dynamicCode2 
;echo extra line 1 
;exit /b 
+1

EDIT - 偶数のラベルに接頭辞を付けることができることを示すために、最後のコードを変更して、簡単に動的コードに含めることができます。 – dbenham

2

短い回答:はい、バッチファイルは実行中に自分自身を変更することができます。すでに他の人が確認しているので。

私が働いていた場所は、Windows 3以前はMS-DOSの社内メニューシステムでした。実行方法は非常にエレガントでした。実際には、バッチファイルから実行して、スクリプトを実行するためにメインプログラム(C言語で書かれたもの)を修正しました。このトリックは、選択が実行されている間、メニュープログラム自体がメモリスペースを占有していないことを意味していました。これには、LAN Mailプログラムや3270端末プログラムなどが含まれていました。

しかし、自己修正バッチファイルから実行すると、TSRプログラムのロードなどのスクリプトも実行でき、実際にはバッチファイルに入れることのできるものはほとんどできます。それはそれを非常に強力にしました。作者が最終的にバッチファイルをコマンドごとに再起動させる方法を見つけだすまで、GOTOコマンドだけが機能しませんでした。

2

コマンドインタープリタは、読み込んでいる各コマンドファイル内のバイトオフセットを記憶しているように見えますが、ファイル自体はロックされていないため、実行中はテキストエディタなどで変更することができます。

この記憶された場所の後にファイルが変更された場合、インタープリタは今変更されたスクリプトをうまく実行し続ける必要があります。しかし、そのポイントより前に変更が加えられ、その変更がそのポイントのテキストの長さを変更した場合(たとえば、テキストを挿入または削除した場合など)、その記憶されたロケーションはもはやその次のコマンドの開始を参照しなくなります。インタプリタが次の '行'を読み込もうとすると、代わりに、挿入または削除されたテキストの量に応じて、別の行または行の一部を選択します。あなたが運が良ければ、おそらくそれが起こった言葉を処理したり、エラーを出したり、次の行から実行し続けることはできませんが、あなたが望むものではないでしょう。

しかし、何が起こっているのかを理解することで、スクリプトを構築してリスクを軽減することができます。メニューを表示し、choiceコマンドを使用してユーザーからの入力を受け入れてから、選択を処理することによって、単純なメニューシステムを実装するスクリプトを用意しました。そのトリックは、スクリプトが入力を待っているポイントがファイルの最上部近くにあることを確認して、編集したいと思うような編集がそのポイントの後に起こり、厄介な影響を与えないようにすることです。

例:

:top 
call :displayMenu 
:prompt 
REM The script will spend most of its time waiting here. 
choice /C:1234 /N "Enter selection: " 
if ERRORLEVEL == 4 goto DoOption4 
if ERRORLEVEL == 3 goto DoOption3 
if ERRORLEVEL == 2 goto DoOption2 
goto DoOption1 
:displayMenu 
(many lines to display menu) 
goto prompt 
:DoOption1 
(many lines to do Option 1) 
goto top 
:DoOption2 
(many lines to do Option 2) 
goto top 
(etc) 
関連する問題