代码之家  ›  专栏  ›  技术社区  ›  Chaddeus

是否为多个文件拖放批处理文件?

  •  27
  • Chaddeus  · 技术社区  · 15 年前

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

    在接下来的部分中,我写了一些我认为可以很好地升级到批处理文件的内容。

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

    5 回复  |  直到 6 年前
        1
  •  42
  •   Joey    15 年前

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

    @echo %*
    @pause
    

    现在您有两个选项:

    1. pngcrush已经可以在命令行上处理给它的多个文件名。 在这种情况下,你只需要通过 %* 去PNG冲锋而不是仅仅 %1 (就像你现在可能做的那样):

      @pngcrush %*
      

      %* 包含批处理文件的所有参数,因此这是传递 全部的 其他程序的参数。不过,要小心使用名为pngcrush options的文件。Unix极客将知道这个问题:—)

      但是,在阅读了描述您的技术的文章之后,当您将压缩文件写入到 new.png . 一次处理多个文件是个坏主意,因为只有一个文件 新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 扩展到扩展名,包括点。

      埃塔: 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 是0,方法是将结果写入 新PNG 而不是 new(0).png .

      不过,这种方法有问题。如果已经有名为 新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
      

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

    根据需要随时调整。

        2
  •  10
  •   jeb    13 年前

    以安全的方式执行拖放操作,对于批处理来说并不简单。

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

    pngCr.bat Cool&stuff.png
    

    所以在 % 1 只是 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个字符。
    对于每个文件,都会通过完整的路径,只有很少的文件才能达到此限制。

        3
  •  2
  •   Amr Ali    10 年前
    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"
        )
    )
    
        4
  •  1
  •   sst    6 年前

    这是一个很晚的答案,实际上我不知道这个老问题,并准备了一个答案 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 例行程序我特别尝试最小化关于命令行格式和参数之间间距的假设。Explorer启动的检测(猜测)基于此命令行签名 %SystemRoot%\system32\cmd.exe /c ""FullPathToBatchFile" Arguments"

    因此,很有可能欺骗代码,使其认为它是通过从资源管理器双击或通过拖放启动的,这不是问题,批处理文件将正常工作。

    但是有了这个特殊的签名,就不可能以这种方式有意启动批处理文件: %SystemRoot%\system32\cmd.exe /c ""FullPathToBatchFile" Arguments & SomeOtherCommand" 并期望 SomeOtherCommand 要执行,它将被合并到批处理文件参数中。

        5
  •  0
  •   dmitrik    8 年前

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

    pngcrush -d "crushed" *.png
    

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

    pngcrush -d "crushed" -brute *.png
    

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