如何让Windows cmd在拖放时“玩得很好”

时间:2021-03-26 16:22:50

I had a very straight forward little batch script. The design outline is simple enough ... I want to touch every file 'dropped'.

我有一个非常直接的小批量脚本。设计大纲很简单……我想要触摸每一个被删除的文件。

  1. There is a Windows short-cut on the desktop that calls a batch (.cmd) script.
  2. 桌面上有一个Windows快捷键,它调用一个批处理(.cmd)脚本。
  3. Select some files with Windows Explorer
  4. 使用Windows资源管理器选择一些文件
  5. Drag-n-Drop the selection onto either the short-cut icon or the command file directly.
  6. 拖-n-拖放选择直接到捷径图标或命令文件。
  7. The script loops over the list of files dropped onto the icon.
  8. 脚本循环遍历掉到图标上的文件列表。
  9. The script uses the FOR command to call the command one file at a time.
  10. 脚本使用FOR命令每次调用命令一个文件。

Anyway there are several circumstances where the result is strange (to say the least). The worst example is an infinite loop that crashes cmd.exe. Here is the command file. There are extra prints and pauses here so I can watch the behaviour.

无论如何,有几种情况的结果是奇怪的(至少可以说)。最糟糕的例子是无限循环崩溃cmd.exe。这是命令文件。这里有额外的印痕和停顿,这样我就可以观察它们的行为。

  @echo off
  @title  touch
  @echo.
  cd > z:\$$$\base.tmp
  set /p base= < z:\$$$\base.tmp
  @rem
  @echo  ^  folder: %base%
  @echo  ^  INPUT:  [%*]
  @echo.
  @pause
  @echo.
  for /d  %%x  in  (%*)  DO (
      @echo ^    RAW:      [%%x]
      @echo ^    each --^>  [%%~x]
      @echo ^    each --^>  [%%~fx]
      attrib "%%~fx"
      touch "%%~fx"
    echo.
    @pause
    echo.
)
@echo.
@echo ^  [done]
@echo.
@pause

The most problematic output is a source of confusion to me, see: example 1. I put the attrib command in because it is a 'tame' system .exe.

最成问题的输出是我困惑的来源:示例1。我输入attrib命令是因为它是一个“驯服”的系统。

When you comment-out the "touch" command, the look works with just attrib most of the time. The only thing I can say for sure is that file names with spaces cause problems. For example in the echo-ed "each" lines, I've seen examples like:

当你开始使用“触摸”命令时,大多数情况下,你的外观都能正常工作。我唯一能肯定的是,带空格的文件名会引起问题。例如,在回声“每”行中,我看到过这样的例子:

  • each --> [Z:\tmp\"Z:\tmp\C++ Recipes.pdf"]
  • 每个- - >[Z:\ tmp \ " Z:\ tmp \ c++ Recipes.pdf”)
  • "G:\_xfer_\-m"
  • “G:\ _xfer_ \ - m”

The first seems to be a problem with the FOR loop expansion and how it deals with quotes("). While the second appears to be a out of bounds error with the MSYS touch.exe command.

第一个似乎是FOR循环扩展的问题,以及它如何处理引号(")。而第二个似乎是一个超出界限的错误与MSYS的接触。exe命令。

There's also something fish that goes on when you pass a link (short-cut) file to the script.

当您将一个链接(短切)文件传递给脚本时,还会发生一些事情。

I've tried many different versions of both the FOR syntax and the loop-variable escaping to side-step the issue. After all my primary purpose is to have a GUI touch command. My first question for the brains trust is:

我尝试过很多不同版本的FOR语法和循环变量转义来回避这个问题。毕竟我的主要目的是有一个GUI触摸命令。我对大脑信任的第一个问题是:

  1. What is the appropriate FOR and loop-variable expansion to use for this?
  2. 对于这个,什么是合适的和循环变量的扩展?

As well, since it has become a mystery ....

,因为这已经成为一个谜....

  1. How does the infinite loop occur and is there a way to prevent it?
  2. 无限循环是如何发生的,是否有办法阻止它?
  3. Does someone have a link for documentation and/or examples of FOR with a drag-n-drop with Window?
  4. 是否有人有文档的链接和/或带有窗口的拖放的例子?
  5. How can the script just show: Working folder ... G:\_xfer_ When the string: "Working folder", which is NOT in the .cmd script. And, there was once a version that had the command: echo Working folder:
  6. 脚本如何显示:工作文件夹…G:\_xfer_当字符串“工作文件夹”不在.cmd脚本中时。曾经有一个版本有命令:echo工作文件夹:

It it helps, I put a small script called, "drop.cmd" below as well. As a final point, I suspect there's a bug in the MSYS /GNU command-line handler for Windows:

它很有用,我放了一个名为“drop”的小脚本。cmd”下面。最后,我怀疑Windows的MSYS /GNU命令行处理程序有一个bug:

  • It seem that, When the *args that was passed contains a string with unbalanced quotes -- It can go off the reservation.
  • 看起来,当传递的*args包含一个带有不平衡引号的字符串时,它可以取消预订。

I get similar output to that in example 1, below from the MSYS touch and from tail commands.

我从MSYS touch和tail命令中得到与示例1相似的输出。

I was about to sign-off. There is one more thing, to consider. Does anyone have a script or diagnostic I might use to 'unravel' this ball-of-wool?

我正要落款。还有一件事需要考虑。有人有我可能用来“解开”这个毛线团的脚本或诊断吗?

example 1

   folder: G:\_xfer_
   INPUT:  ["G:\_xfer_\00__zip (on D).lnk" G:\_xfer_\#trash.lnk]

Press any key to continue . . .

    RAW:      ["G:\_xfer_\00__zip (on D).lnk"]
    each -->  [G:\_xfer_\00__zip (on D).lnk]
    each -->  [G:\_xfer_\00__zip (on D).lnk]
A            G:\_xfer_\00__zip (on D).lnk
  all ... G:\_xfer_\00__zip (on D).lnk
  one ... G:\_xfer_\00__zip
  two ... (on


   Working folder ...
G:\_xfer_

touch -m -c  "G:\_xfer_\-m"

   Working folder ...
G:\_xfer_

touch -m -c  "G:\_xfer_\-m"
    :
    :

And eventually crashed with a *!

并最终崩溃与*!

drop.cmd

@echo off
@title  %~1
@echo.
cd > z:\$$$\base.tmp
set /p base= < z:\$$$\base.tmp
@rem
@echo  ^  drop folder: %base%
@echo  ^  INPUT:       [%*]
@echo.
@pause
rem
@echo.
for   %%x  in  (%*)  DO ( 
    @echo ^    RAW:      [%%x]
    @echo ^    each --^>  [%%~x]
    @echo.
    @pause
)
@echo.
@echo ^  [done]
@echo.
@pause

1 个解决方案

#1


2  

Getting a batch file handle dropped files can be sometimes difficult. Any dragged file with spaces in its name will be quoted, but a file with special characters (&) but without spaces will not be quoted. This will lead to problems with the parser handling the arguments to the file using %*.

获取批处理文件处理掉的文件有时是困难的。任何名称中带有空格的拖放文件都将被引用,但是带有特殊字符(&)但没有空格的文件将不会被引用。这将导致解析器使用%*处理文件的参数出现问题。

But taking the code from jeb' answer and polished by @dbenham to solve the problem of "Pretty print windows %path% variable" (thank you both), and retrieving the arguments from %* only when called from command line (where arguments "should" be well formed) or from %cmdcmdline% (that hold the command line used to start the current cmd instance) when dropped, we can do something like

但是把代码从杰布的回答和抛光@dbenham解决问题的“相当打印窗口% %变量”(谢谢),和检索的参数% *只有在从命令行调用(参数“应该”是形成良好的)或从% cmdcmdline %(持有当前cmd命令行用于启动实例)下降时,我们可以做一些类似

@echo off
    setlocal enableextensions disabledelayedexpansion
    set "var="

    rem Determine call origin
    setlocal enabledelayedexpansion
    call :detectDrop !cmdcmdline!
    endlocal
    if not errorlevel 1 goto :dropped

:commandLine
    rem Invoked from command line
    set "dropped="
    if "%~1"=="" goto done
    set var=%*
    set "var=%var:"=""%"
    goto :process

:dropped
    rem Invoked from explorer
    set "dropped=1"
    set "var=%cmdcmdline:"=""%"
    set "var=%var:*/c """"=%"
    set "var=%var:*"" =%"
    set "var=%var:~0,-2%"

:process
    if not defined var goto :done

    rem Adapted from dbenham's answer at [https://*.com/a/7940444/2861476]

    set "var=%var:^=^^%"
    set "var=%var:&=^&%"
    set "var=%var:|=^|%"
    set "var=%var:<=^<%"
    set "var=%var:>=^>%"
    set "var=%var: =^ ^ %"
    set var=%var:""="%
    set "var=%var:"=""Q%"
    set "var=%var:  ="S"S%"
    set "var=%var:^ ^ = %"
    set "var=%var:""="%"
    setlocal enabledelayedexpansion
    set "var=!var:"Q=!"
    for %%a in ("!var:"S"S=" "!") do ( 
        if "!!"=="" endlocal

        rem Here we have a reference to the passed/dropped element
        if %%a neq "" echo "%%~fa"

    )
    goto :done

:detectDrop cmdcmdline
    if /i "%~1"=="%comspec%" if /i "%~2"=="/c" exit /b 0
    exit /b 1

:done    
    if defined dropped ( pause & exit )

note: sorry, not thoroughly tested. Maybe there is some case that will make it fail.

注意:对不起,没有完全测试。也许有什么情况会让它失败。

#1


2  

Getting a batch file handle dropped files can be sometimes difficult. Any dragged file with spaces in its name will be quoted, but a file with special characters (&) but without spaces will not be quoted. This will lead to problems with the parser handling the arguments to the file using %*.

获取批处理文件处理掉的文件有时是困难的。任何名称中带有空格的拖放文件都将被引用,但是带有特殊字符(&)但没有空格的文件将不会被引用。这将导致解析器使用%*处理文件的参数出现问题。

But taking the code from jeb' answer and polished by @dbenham to solve the problem of "Pretty print windows %path% variable" (thank you both), and retrieving the arguments from %* only when called from command line (where arguments "should" be well formed) or from %cmdcmdline% (that hold the command line used to start the current cmd instance) when dropped, we can do something like

但是把代码从杰布的回答和抛光@dbenham解决问题的“相当打印窗口% %变量”(谢谢),和检索的参数% *只有在从命令行调用(参数“应该”是形成良好的)或从% cmdcmdline %(持有当前cmd命令行用于启动实例)下降时,我们可以做一些类似

@echo off
    setlocal enableextensions disabledelayedexpansion
    set "var="

    rem Determine call origin
    setlocal enabledelayedexpansion
    call :detectDrop !cmdcmdline!
    endlocal
    if not errorlevel 1 goto :dropped

:commandLine
    rem Invoked from command line
    set "dropped="
    if "%~1"=="" goto done
    set var=%*
    set "var=%var:"=""%"
    goto :process

:dropped
    rem Invoked from explorer
    set "dropped=1"
    set "var=%cmdcmdline:"=""%"
    set "var=%var:*/c """"=%"
    set "var=%var:*"" =%"
    set "var=%var:~0,-2%"

:process
    if not defined var goto :done

    rem Adapted from dbenham's answer at [https://*.com/a/7940444/2861476]

    set "var=%var:^=^^%"
    set "var=%var:&=^&%"
    set "var=%var:|=^|%"
    set "var=%var:<=^<%"
    set "var=%var:>=^>%"
    set "var=%var: =^ ^ %"
    set var=%var:""="%
    set "var=%var:"=""Q%"
    set "var=%var:  ="S"S%"
    set "var=%var:^ ^ = %"
    set "var=%var:""="%"
    setlocal enabledelayedexpansion
    set "var=!var:"Q=!"
    for %%a in ("!var:"S"S=" "!") do ( 
        if "!!"=="" endlocal

        rem Here we have a reference to the passed/dropped element
        if %%a neq "" echo "%%~fa"

    )
    goto :done

:detectDrop cmdcmdline
    if /i "%~1"=="%comspec%" if /i "%~2"=="/c" exit /b 0
    exit /b 1

:done    
    if defined dropped ( pause & exit )

note: sorry, not thoroughly tested. Maybe there is some case that will make it fail.

注意:对不起,没有完全测试。也许有什么情况会让它失败。