2016-12-12 15 views
0

ファイルのリストから読み込んで各項目を同じ場所に新しい場所にコピーするスクリプトを作成しようとしています。バッチファイル内のforループ内で2つのローカル変数を展開する方法

これはGitコミットの範囲内で変更されたファイルをコピーすることを意図していましたが、もっと簡単な方法(最初はC# "スクリプト"、次にthis)を見つけたので、学習の関心。

config.php 
repository\file picker.php 
user\edit.php 

これは、これまでのところ、私のの.batファイルです::コメント行set dest_file_dir=(!dest_file_full - !dest_file_filename)

@echo off 

setlocal enabledelayedexpansion 

set "source=input dir" 
set "target=output dir" 

for /f "tokens=* usebackq" %%A in ("file_list.txt") do (
    set "FILE=%%A" 
    set "dest_file_full=%target%\!FILE:%source%=!" 
    set "dest_file_filename=%%~nxA" 
    ::set dest_file_dir=(!dest_file_full - !dest_file_filename) 
    if not exist "!dest_file_dir!" (
     md "!dest_file_dir!" 
    ) 
    set "source_file_full=%source%\!FILE:%source%=!" 
    copy "!source_file_full!" "!dest_file_dir!" 
) 
pause 

お知らせ

これはfile_list.txtの一例です。これは擬似コードです。私はここで変数置換を使用する必要がありますが、cmd.exeは、1つのステップで2つのローカル変数を展開しません。

call set dest_file_dir=!!dest_file_full:!dest_file_filename!=!! 

しかし、どちらも動作します:

echo set dest_file_dir=^!dest_file_full:!dest_file_filename!=^! | cmd 

またはcallを使用して、私はすでにパイプを試してみました

...。

私はこれに何か間違っているのですか、これを達成するためにエレガントな方法(したがって一時ファイルを使用しないで)がありますか?

ありがとうございました。

答えて

1

この答えは、 "ネストされた変数" のみの拡大について詳しく説明:要するに

を、これを使用する:

call set "dest_file_dir=%%dest_file_full:!dest_file_filename!=%%" 

^!をエスケープするかに動作しませんcall!!を使用して遅れた展開で入れ子になった変数を展開する

パイプは、独自の別の環境を使用する両面の新しいcmdインスタンスを初期化するときのいずれの場合でも役に立ちません。


私たちが最初の即時展開を使用して、ネストされた変数を見てみましょう:

call set "dest_file_dir=%%dest_file_full:%dest_file_filename%=%%" 

callは、ネストを解決するために必要とされる第二の解析フェーズを、紹介します。 callコマンド(例えば、%dest_file_filename%config.phpに展開想定)のように、部分的に拡張コマンドラインを受信:

set "dest_file_dir=%dest_file_full:config.php=%" 

これは中に展開され、即時膨張と正常部分文字列置換構文が、何もしません前記第2の解析段階。

注:このアプローチは、dest_file_filenameの値は%で始まる場合に~、または*を失敗した(これはとにかくファイル名の禁断の文字である)、またはそれが=が含まれている場合は!さらに、もし存在すれば、"も問題を引き起こす可能性があります(ただし、ファイル名には許可されません)!


今、私たちは同様のアプローチをチェックしてみましょうが、遅延拡張を有効にして:

ポストHow does the Windows Command Interpreter (CMD.EXE) parse scripts?によると、callは、第二の解析フェーズを紹介しますが、これが唯一の遅れ拡張が、即時の拡張が含まれていません。遅延展開は最初の解析フェーズでのみ行われます。

直接展開を使用する上記のソリューションと同様に、サブストリング置換構文の検索と置換文字列が最初の解析フェーズで展開され、全体の式が2番目の解析フェーズで処理されることを確認する必要があります;そう、私たちはこれを使ってみましょう:

call set "dest_file_dir=%%dest_file_full:!dest_file_filename!=%%" 

だからcallコマンドは以下の部分拡大のコマンドラインを受け取る:

set "dest_file_dir=%dest_file_full:config.php=%" 

は、これもすぐに展開してサブ文字列置換構文を構成しています。

注:このアプローチは、dest_file_filenameの値は%で始まる場合に~、または*を失敗した(これはとにかくファイル名の禁断の文字である)、またはそれが=が含まれている場合は!さらに、もし存在すれば、"も問題を引き起こす可能性があります(ただし、ファイル名には許可されません)!ここ

for /F delims^=^ eol^= %%K in ("!dest_file_filename!") do set "dest_file_dir=!dest_file_full:%%K=!" 

!dest_file_filename!が展開されている全体for /Fループが解析される場合:


ネストされた変数を拡張するために必要な第二の解析相を導入する別の方法は、このように、forループを使用することです; 2番目の解析フェーズはループ本体に適用され、%%Kの代わりにdest_file_filename(たとえば、config.php)の値を受け取ります。

この方法の利点は、即時の拡張が完全に回避され、がdest_file_filenameという値で発生する可能性があります。ただし、これにより!^の問題が発生します(先に^でエスケープする必要があります)。
dest_file_filenameの値は、まだ~または*で始まらず、まだ=を含まなければなりません。また、!で始まらなければなりません。

1
FOR %%y IN ("!dest_file_full!") DO set "dest_file_dir=%%~dpy" 

dest_file_dirにあなたの宛先ドライブ+パス+ターミナル\を取得する必要があります。

言うまでもなく、::という形式のコメントはブロック内で使用しないでください。

0

(コードブロック)での文字列操作が混乱することがあります。私は個人的にこれらをサブに入れる方が好きです。このバッチが一時的に迅速かつあなたの変化のより良い出力を持つことにエコーを遮断します:

@echo off 
setlocal enabledelayedexpansion 
Set MyPrompt=%Prompt% 
Prompt $S 
Echo on 
set "source=input dir" 
set "target=output dir" 
for /f "tokens=* usebackq" %%A in ("file_list.txt") do call :sub "%%A" 
pause 
Prompt %MyPrompt% 
Goto :Eof 

:Sub 
set "FILE=%~1" 
set "dest_file_full=%target%\!FILE:%target%=!" 
set "dest_file_filename=%~nx1" 
set "dest_file_dir=!dest_file_full:%dest_file_filename%=!" 
Rem if not exist "%dest_file_dir%" md "%!dest_file_dir%" 
set "source_file_full=%source%\%FILE%" 
@Echo copy "%source_file_full%" "%dest_file_dir%" 

特に、第2のセットは私には不明である、より本当のVARSは役立つはずです。

関連する問題