拖放多个文件的批处理文件?

时间:2009-08-07 06:37:00

标签: image optimization batch-file png

当我将PngCrush拖放到批处理文件中时,我写了一个批处理文件来使用PngCrush来优化.png图像。

在接下来的部分中,我写了一篇关于我认为对批处理文件的良好升级的文章。

我的问题是:是否可以像在帖子中那样创建批处理文件,但能够一次优化多个图像?拖放多个.png文件? (并且输出类似于new.png,new(1).png,new(2).png等......

5 个答案:

答案 0 :(得分:43)

是的,当然这是可能的。在批处理文件上拖动多个文件时,您将获取已删除文件的列表作为以空格分隔的列表。您可以使用以下简单批处理来验证这一点:

@echo %*
@pause

现在您有两个选择:

  1. PngCrush已经可以处理在命令行上给它的多个文件名。在这种情况下,你所要做的只是将%*传递给PngCrush而不是只有%1(正如你现在所做的那样):

    @pngcrush %*
    

    %*包含批处理文件的所有参数,因此这是将所有参数传递给另一个程序的便捷方法。但要注意名为PngCrush选项的文件。 UNIX极客会知道这个问题: - )

    在阅读描述您的技术的帖子之后,当您将压缩文件写入new.png时,这将无法正常工作。如果你一次处理多个文件是一个坏主意,因为只有一个new.png :-)。但我刚试过PngCrush很好地处理了多个文件,所以如果你不介意文件的就地更新那么就放

    @pngcrush -reduce -brute %*
    

    进入您的批次将完成工作(跟随您的原始文章)。

  2. PngCrush将无法处理多个文件您希望在压缩后将每个图像写入新文件。在这种情况下,您坚持使用“一个文件”在一个时间“例程但你循环输入参数。在这种情况下,每次处理一个参数时,最简单的方法就是构建一个小循环和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: Eep,我刚刚用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%是我们的计数器,我们通过将结果写入n而不是new.png来处理new(0).png为0的情况。

    但这种方法存在问题。如果已经存在名为new.pngnew(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
    

    程序的其余部分保持不变,包括正常循环。但现在我们从第一个未使用的文件名开始,避免覆盖已存在的文件。

  3. 随意根据需要进行调整。

答案 1 :(得分:10)

做Drag&以安全的方式放弃,批处理不是那么简单。

处理%1shift%*可能会失败,因为资源管理器不是很聪明,在引用文件名时,只会引用带空格的文件名。
但是,Cool&stuff.png等文件未被资源管理器引用,因此您获得了像

这样的cmdline
pngCr.bat Cool&stuff.png

因此%1 Cool只有%* Cool只有stuff.png,但批次结束后,cmd.exe会尝试执行!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 可以帮助。

{{1}}

顺便说一下。尽管“标准”批次行限制为~8192个字符,但拖放操作的行数限制为~2048个字符。
对于每个文件传递完整路径,可以使用少量文件达到此限制。

答案 2 :(得分:2)

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

答案 3 :(得分:1)

这是一个很晚的答案,实际上我不知道这个老问题,而是为this准备了一个类似的答案,因为在讨论中有特殊字符处理文件名的问题,因为资源管理器仅引用那些包含空格。然后,在对该问题的评论中,我看到了对该线程的引用,此后而不是我的疑惑,我意识到jeb已经很好地涵盖并解释了此问题,这是他的期望。

因此,在不做进一步解释的情况下,我将主要致力于解决方案,以使用此,;!^字符覆盖文件名中的更多特殊情况,并提供一种机制来猜测 批处理文件是否由资源管理器直接启动,因此在所有情况下都可以使用处理批处理文件参数的老式逻辑。

@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例程中,我特别尝试最小化关于命令行格式和参数之间的间距的假设。资源管理器启动的检测(猜测)基于此命令行签名%SystemRoot%\system32\cmd.exe /c ""FullPathToBatchFile" Arguments"

因此,很有可能通过双击资源管理器或通过拖放操作使代码误以为代码已启动,这不是问题,并且批处理文件将正常运行。

但是使用此特定签名,不可能有意地以这种方式启动批处理文件:%SystemRoot%\system32\cmd.exe /c ""FullPathToBatchFile" Arguments & SomeOtherCommand"并期望执行SomeOtherCommand,而是将其合并到批处理文件参数中。 / p>

答案 4 :(得分:0)

您不需要批处理脚本来优化多个PNG,您只需要通配符:

pngcrush -d "crushed" *.png

这将会击败当前目录中的所有PNG并将它们移动到名为“crushed”的子目录。我会添加-brute标志以减少几个字节。

pngcrush -d "crushed" -brute *.png

我发布这个是因为它似乎没有很好的文档或广为人知,因为从长远来看,它可能比编写和维护拖放批处理文件更容易。