web-dev-qa-db-ja.com

複数のファイルのバッチファイルをドラッグアンドドロップしますか?

バッチファイルにドラッグアンドドロップしたときに、PngCrushを使用して.png画像を最適化するバッチファイルを作成しました。

次のセクションでは、バッチファイルのアップグレードに適していると思うことについて書きました。

私の質問は:投稿で行ったようにバッチファイルを作成することは可能ですが、一度に複数の画像を最適化することはできますか?複数の.pngファイルをドラッグアンドドロップしますか? (そして、出力はnew.png、new(1).png、new(2).pngなどのようになります。

28
Chaddeus

はい、もちろんこれは可能です。バッチファイルで複数のファイルをドラッグすると、ドロップされたファイルのリストがスペースで区切られたリストとして表示されます。これは、次の簡単なバッチで確認できます。

@echo %*
@pause

今、2つのオプションがあります:

  1. PngCrushは、コマンドラインで指定された複数のファイル名をすでに処理できます。この場合、必要なのは%*をPngCrushに渡すことだけです。 %1だけでなく(おそらく今やっているように):

    @pngcrush %*
    

    %*にはバッチファイルへのすべての引数が含まれているため、これはall引数を別のプログラムに渡すための便利な方法です。ただし、PngCrushオプションのような名前のファイルには注意してください。 UNIXオタクはその問題を知っているでしょう:-)

    ただし、テクニックを説明している投稿を読んだ後は、圧縮ファイルをnew.pngに書き込んでいるため、これは正しく機能しません。 new.pngは1つしか存在できないため、一度に複数のファイルを処理する場合はお勧めできません:-)。しかし、私はPngCrushが複数のファイルをうまく処理することを試したので、ファイルのインプレース更新を気にしない場合は、

    @pngcrush -reduce -brute %*
    

    あなたのバッチに(あなたの元の記事に続いて)仕事をします。

  2. PngCrushは複数のファイルを処理しませんまたは圧縮後に各画像を新しいファイルに書き込みます。この場合、「一度に1つのファイル」ルーチンを使用しますが、入力引数をループします。この場合、1つを処理するたびに、小さなループとshift引数を作成するのが最も簡単です。

    @echo off
    if [%1]==[] goto :eof
    :loop
    pngcrush -reduce -brute %1 "%~dpn1_new%~x1"
    shift
    if not [%1]==[] goto loop
    

    ここで行っていることは単純です。引数なしで実行された場合は最初にバッチ全体をスキップし、次にジャンプするラベルを定義します:loop。内部では、最初の引数でPngCrushを実行し、圧縮ファイルに新しい名前を付けます。ここで使用したパス分析構文をhelp callで確認することをお勧めします。基本的に、ここで行っているのは、以前とまったく同じようにファイルに名前を付けることです。ファイル名の末尾(拡張子の前)に「_new」を付けるだけです。 %~dpn1はドライブ、パス、ファイル名(拡張子なし)に展開され、%~x1はドットを含む拡張子に展開されます。

    ETA:うーん、new.png、new(1).pngなどで目的の出力を読み取っただけです。この場合、派手なパスの分析は必要ありませんが、他に注意すべき問題があります約。

    最も簡単な方法は、最初のファイルを処理する前に0からカウンターを開始し、別のファイルを処理するたびにそれをインクリメントすることです。

    @echo off
    if [%1]==[] goto :eof
    set n=0
    :loop
    if %n%==0 (
        pngcrush -reduce -brute %1 new.png
    ) else (
        pngcrush -reduce -brute %1 new^(%n%^).png
    )
    shift
    set /a n+=1
    if not [%1]==[] goto loop
    

    ここでは%n%がカウンターであり、new(0).pngではなくnew.pngに結果を書き込むことでnが0の場合を処理します。

    ただし、このアプローチには問題があります。 new.pngまたはnew(x).pngという名前のファイルがすでに存在する場合は、おそらくそれらを壊してしまいます。よくない。したがって、別のことをして、実際にファイル名を使用できるかどうかを確認する必要があります。

    rem check for new.png
    if exist new.png (set n=1) else (set n=0 & goto loop)
    rem check for numbered new(x).png
    :checkloop
    if not exist new^(%n%^).png goto loop
    set /a n+=1
    goto checkloop
    

    プログラムの残りの部分は、通常のループを含めて同じままです。しかし今、私たちは最初の未使用のファイル名から始めて、すでにそこにあるファイルを上書きすることを避けます。

必要に応じて自由に適応してください。

43
Joey

安全な方法でドラッグアンドドロップを行うには、バッチを使用するのはそれほど簡単ではありません。

Explorerはあまりスマートではないため、%1shift、または%*の処理は失敗する可能性があります。ファイル名を引用するときは、スペースを含むファイル名のみが引用されます。
ただし、Cool&stuff.pngのようなファイルはエクスプローラーによって引用されないため、次のようなコマンドラインが表示されます。

pngCr.bat Cool&stuff.png

したがって、%1ではCoolのみです。%*でもCoolのみですが、バッチが終了した後、cmd.exeはstuff.png(失敗します)。

これを処理するには、!cmdcmdline! .. %1の代わりに%nを使用してパラメーターにアクセスし、実行の最後に発生する可能性のあるエラーをバイパスするために、単純なexitを使用できます。助けて。

@echo off
setlocal ENABLEDELAYEDEXPANSION
rem Take the cmd-line, remove all until the first parameter
set "params=!cmdcmdline:~0,-1!"
set "params=!params:*" =!"
set count=0

rem Split the parameters on spaces but respect the quotes
for %%G IN (!params!) do (
  set /a count+=1
  set "item_!count!=%%~G"
  rem echo !count! %%~G
)

rem list the parameters
for /L %%n in (1,1,!count!) DO (
  echo %%n #!item_%%n!#
)
pause

REM ** The exit is important, so the cmd.ex doesn't try to execute commands after ampersands
exit

ところで。 「標準」のバッチ行制限は最大8192文字ですが、ドラッグアンドドロップ操作には最大2048文字の行制限があります。
各ファイルについて完全なパスが渡されますが、この制限に達する可能性があるのは少数のファイルです。

10
jeb
FOR %%A IN (%*) DO (
    REM Now your batch file handles %%A instead of %1
    REM No need to use SHIFT anymore.
    ECHO %%A
)

また、ドロップされたファイルとフォルダーを区別するために、これを使用できます。

FOR %%I IN (%*) DO (
    ECHO.%%~aI | FIND "d" >NUL
    IF ERRORLEVEL 1 (
        REM Processing Dropped Files
        CALL :_jobF "%%~fI"
    ) ELSE (
        REM Processing Dropped Folders
        CALL :_jobD "%%~fI"
    )
)
2
Amr Ali

これは非常に遅い回答です。実際、私はこの古い質問に気づかず、 this の回答を用意しました。エクスプローラーはファイル名のみを引用するため、特殊文字を含むファイル名の処理についての議論がありました。スペースが含まれています。それから、その質問へのコメントで、私はこのスレッドへの参照を見ました、その後、私の確信ではなく、 jeb はすでにこの問題をカバーし、説明していることに気付きました。これは彼に期待されています。

したがって、これ以上の説明なしに、この,;!^文字を使用したファイル名のより特殊なケースをカバーし、guessバッチファイルがエクスプローラーによって直接起動されるかどうかに関係なく、バッチファイルの引数を処理するための古い方法のロジックをすべての場合に使用できます。

@echo off
setlocal DisableDelayedExpansion

if "%~1" EQU "/DontCheckDrapDrop" (
    shift
) else (
    call :IsDragDrop && (
        call "%~f0" /DontCheckDrapDrop %%@*%%
        exit
    )    
)

:: Process batch file arguments as you normally do 
setlocal EnableDelayedExpansion
echo cmdcmdline=!cmdcmdline!
endlocal

echo,
echo %%*=%*
echo,
if defined @* echo @*=%@*%
echo,
echo %%1="%~1"
echo %%2="%~2"
echo %%3="%~3"
echo %%4="%~4"
echo %%5="%~5"
echo %%6="%~6"
echo %%7="%~7"
echo %%8="%~8"
echo %%9="%~9"
pause
exit /b

:: IsDragDrop routine
:: Checks if the batch file is directly lanched through Windows Explorer
:: then Processes batch file arguments which are passed by Drag'n'Drop,
:: rebuilds a safe variant of the arguments list suitable to be passed and processed
:: in a batch script and returns the processed args in the environment variable
:: that is specified by the caller or uses @* as default variable if non is specified.
:: ErrorLevel: 0 - If launched through Explorer. 1 - Otherwise (Will not parse arguments)
:IsDragDrop [retVar=@*]
setlocal
set "Esc="
set "ParentDelayIsOff=!"

setlocal DisableDelayedExpansion
if "%~1"=="" (set "ret=@*") else set "ret=%~1"
set "Args="
set "qsub=?"

:: Used for emphasis purposes
set "SPACE= "

setlocal EnableDelayedExpansion
set "cmdline=!cmdcmdline!"
set ^"ExplorerCheck=!cmdline:%SystemRoot%\system32\cmd.exe /c ^""%~f0"=!^"
if "!cmdline!"=="!ExplorerCheck!" (
    set ^"ExplorerCheck=!cmdline:"%SystemRoot%\system32\cmd.exe" /c ^""%~f0"=!^"
    if "!cmdline!"=="!ExplorerCheck!" exit /b 1
)
set "ExplorerCheck="
set ^"cmdline=!cmdline:*"%~f0"=!^"
set "cmdline=!cmdline:~0,-1!"
if defined cmdline (
    if not defined ParentDelayIsOff (
        if "!cmdline!" NEQ "!cmdline:*!=!" set "Esc=1"
    )
    set ^"cmdline=!cmdline:"=%qsub%!"
)
(
    endlocal & set "Esc=%Esc%"
    for /F "tokens=*" %%A in ("%SPACE% %cmdline%") do (
        set "cmdline=%%A"
    )
)
if not defined cmdline endlocal & endlocal & set "%ret%=" & exit /b 0

:IsDragDrop.ParseArgs
if "%cmdline:~0,1%"=="%qsub%" (set "dlm=%qsub%") else set "dlm= "
:: Using '%%?' as FOR /F variable to not mess with the file names that contain '%'
for /F "delims=%dlm%" %%? in ("%cmdline%") do (
    set ^"Args=%Args% "%%?"^"
    setlocal EnableDelayedExpansion
    set "cmdline=!cmdline:*%dlm: =%%%?%dlm: =%=!"
)
(
    endlocal
    for /F "tokens=*" %%A in ("%SPACE% %cmdline%") do (
        set "cmdline=%%A"
    )
)
if defined cmdline goto :IsDragDrop.ParseArgs

if defined Esc (
    set ^"Args=%Args:^=^^%^"
)
if defined Esc (
    set ^"Args=%Args:!=^!%^"
)
(
    endlocal & endlocal
    set ^"%ret%=%Args%^"
    exit /b 0
)

サンプルファイルをバッチファイルにドラッグアンドドロップした出力:

cmdcmdline=C:\Windows\system32\cmd.exe /c ""Q:\DragDrop\DragDrop.cmd" Q:\DragDrop\ab.txt "Q:\DragDrop\c d.txt" Q:\DragDrop\!ab!c.txt "Q:\DragDrop\a b.txt" Q:\DragDrop\a!b.txt Q:\DragDrop\a&b.txt Q:\DragDrop\a(b&^)).txt Q:\DragDrop\a,b;c!d&e^f!!.txt Q:\DragDrop\a;b.txt"

%*=/DontCheckDrapDrop  "Q:\DragDrop\ab.txt" "Q:\DragDrop\c d.txt" "Q:\DragDrop\!ab!c.txt" "Q:\DragDrop\a b.txt" "Q:\DragDrop\a!b.txt" "Q:\DragDrop\a&b.txt" "Q:\DragDrop\a(b&^)).txt" "Q:\DragDrop\a,b;c!d&e^f!!.txt" "Q:\DragDrop\a;b.txt"

@*= "Q:\DragDrop\ab.txt" "Q:\DragDrop\c d.txt" "Q:\DragDrop\!ab!c.txt" "Q:\DragDrop\a b.txt" "Q:\DragDrop\a!b.txt" "Q:\DragDrop\a&b.txt" "Q:\DragDrop\a(b&^)).txt" "Q:\DragDrop\a,b;c!d&e^f!!.txt" "Q:\DragDrop\a;b.txt"

%1="Q:\DragDrop\ab.txt"
%2="Q:\DragDrop\c d.txt"
%3="Q:\DragDrop\!ab!c.txt"
%4="Q:\DragDrop\a b.txt"
%5="Q:\DragDrop\a!b.txt"
%6="Q:\DragDrop\a&b.txt"
%7="Q:\DragDrop\a(b&^)).txt"
%8="Q:\DragDrop\a,b;c!d&e^f!!.txt"
%9="Q:\DragDrop\a;b.txt"

:IsDragDropルーチンでは、コマンドライン形式と引数間の間隔に関する仮定を最小限に抑えるように特別に試みました。 Explorerの起動の検出(推測)は、このコマンドラインシグネチャ%SystemRoot%\system32\cmd.exe /c ""FullPathToBatchFile" Arguments"に基づいています。

したがって、エクスプローラーからダブルクリックするかドラッグアンドドロップすることでコードが起動したと思わせる可能性が非常に高く、それは問題ではなく、バッチファイルは正常に機能します。

ただし、この特定のシグニチャでは、%SystemRoot%\system32\cmd.exe /c ""FullPathToBatchFile" Arguments & SomeOtherCommand"のように意図的にバッチファイルを起動することはできず、SomeOtherCommandが実行されることを期待します。代わりに、バッチファイルの引数にマージされます。

1
sst

複数のPNGを最適化するためにバッチスクリプトは必要ありません。必要なのはワイルドカードだけです。

pngcrush -d "crushed" *.png

これにより、現在のディレクトリ内のすべてのPNGがpngcrushされ、「crushed」という名前のサブディレクトリに移動されます。 -bruteフラグを追加して、さらに数バイトを削減する可能性があります。

pngcrush -d "crushed" -brute *.png

これを投稿するのは、十分に文書化されていないか、広く知られていないようであり、ドラッグアンドドロップバッチファイルを作成して維持するよりも長期的には簡単な場合があるためです。

0
dmitrik