如何对 Windows 10 批处理文件中的参数列表进行排序

Posted

技术标签:

【中文标题】如何对 Windows 10 批处理文件中的参数列表进行排序【英文标题】:How to sort the argument list in a a Windows 10 batch file 【发布时间】:2021-07-07 13:37:23 【问题描述】:

我需要调用一个带有文件名列表的程序,但我需要找到并提取列表中的第一个文件,并按照排序顺序将其余文件传递给程序。

具体来说,我想传递使用 QTTabbar 应用程序启动器选择的文件列表并执行 exiftool,以便列表中的第一个文件用于“-TagsFromFile”选项,然后处理所有其余文件以便他们应用“-AllDates”选项。所以我的第一次尝试是:

exiftool -TagsFromFile %1 -AllDates %*

这会将第一个文件放在列表中,但由于 exiftool 会将其设置为与已有的相同的值,因此这是可以接受的。

但是,我发现 QTTabbar 并没有按照我的预期将参数按名称按字典顺序传递给批处理文件。所以我想我需要对列表进行排序。

我找到了对How to sort the arguments dropped to a batch file? 中的参数进行排序的方法,但在该解决方案中存在一个循环,并且为每个参数调用一次程序,而不是构建一个新的参数列表。

for /f "delims=" %%a in ('cmd /c ^"for %%i in ^(%*^) do @echo %%~i^"^|sort') do (
    echo use "%%a"
)

我需要构建一个可以传递给 exiftool 的新参数列表,而不是“echo use”%%a。理想情况下,我可以构建一个列表,然后用新的参数列表替换原始参数列表,例如“ set" 命令在 Bash 中。如果失败,我可以构建一个新列表并使用它,但我不知道如何构建一个列表,也不知道如何引用第一个元素(如果有的话)。

我该怎么做?

编辑: 在文件资源管理器 GUI 中选择文件。它们出现在批处理文件中的顺序由 Windows 确定。这是批处理文件中“echo %*”的输出:

"C:\Users\user1\Desktop\setAB.test\00000920.jpg" "C:\Users\user1\Desktop\setAB.test\00000913.jpg" "C:\Users\user1\Desktop\setAB.test\00000914.jpg" "C:\Users\user1\Desktop\setAB.test\00000915.jpg" "C:\Users\user1\Desktop\setAB.test\00000916.jpg" "C:\Users\user1\Desktop\setAB.test\00000917.jpg" "C:\Users\user1\Desktop\setAB.test\00000918.jpg" "C:\Users\user1\Desktop\setAB.test\00000919.jpg"

如您所见,最后一个文件首先出现。我不知道为什么。有时它们的顺序相反。

因此,批处理文件被调用为:

ex.bat "C:\Users\user1\Desktop\setAB.test\00000920.jpg" "C:\Users\user1\Desktop\setAB.test\00000913.jpg" "C:\Users\user1\Desktop\setAB.test\00000914.jpg" "C:\Users\user1\Desktop\setAB.test\00000915.jpg" "C:\Users\user1\Desktop\setAB.test\00000916.jpg" "C:\Users\user1\Desktop\setAB.test\00000917.jpg" "C:\Users\user1\Desktop\setAB.test\00000918.jpg" "C:\Users\user1\Desktop\setAB.test\00000919.jpg"

我希望 exiftool 运行为:

exiftool -TagsFromFile "C:\Users\user1\Desktop\setAB.test\00000913.jpg" -AllDates "C:\Users\user1\Desktop\setAB.test\00000913.jpg" "C:\Users\user1\Desktop\setAB.test\00000914.jpg" "C:\Users\user1\Desktop\setAB.test\00000915.jpg" "C:\Users\user1\Desktop\setAB.test\00000916.jpg" "C:\Users\user1\Desktop\setAB.test\00000917.jpg" "C:\Users\user1\Desktop\setAB.test\00000918.jpg" "C:\Users\user1\Desktop\setAB.test\00000919.jpg" "C:\Users\user1\Desktop\setAB.test\00000920.jpg"

【问题讨论】:

打开命令提示符窗口,输入shift /? 并按下[ENTER] 键,阅读可能对您有用的命令。如果您要向我们提供一些示例输入,准确解释该输入是如何传递给脚本的,并向我们准确显示 exiftool 命令以及按所需顺序完成这些输入等,它也会对我们有很大帮助。 这种转变没有帮助,因为原始顺序不正确。例如: ex.bat f4 f3 f8 f7 f6 f1 f2 f5 在 ex.bat 文件中我需要像这样调用 exiftool:exiftool -TagsFromFile f1 -AllDates f2 f3 f4 f5 f6 f7 f8 可选地 f1 也可以出现在第二个设置为 f1 f2 f3 f4 f5 f6 f7 f8. 您能否发布一个真实世界的示例,说明正在传递到批处理文件中的内容。您的伪示例存在缺陷,因为 F10 将使用 SORT 命令在 F1 之后和 F2 之前进行排序。 您被要求向我们展示您是如何将输入传递给批处理文件的。如果您在命令行中输入它,如您的示例所示,请以正确的顺序输入它。如果该参数列表是由另一个命令或程序提供的,那么请向我们展示产生它的命令。现在,与您之前的评论不同,您上面的评论将所有这些参数都提供为双引号,请具体说明,在获得强大的解决方案时确实很重要。 那么根据提供给批处理的参数,EXIFTOOL 命令应该是什么样子?再次使用您提供的真实示例和edit 您的问题。请您花点时间阅读How to Ask 一个好问题。 【参考方案1】:

所以您的代码非常接近。您只需要添加一些基本概念即可将第一个参数放入变量中,这可以通过检查变量是否已定义来完成。然后使用SET 命令将所有参数重建回另一个变量。这需要延迟扩展,如 question 中所述。

@ECHO OFF
setlocal enabledelayedexpansion
set "first="
set "all="
for /f "delims=" %%a in ('cmd /c ^"for %%i in ^(%*^) do @echo %%~i^"^|sort') do (
    if not defined first set "first=%%~a"
    set "all=!all!"%%~a" "
)
exiftool -TagsFromFile "%first%" -AllDates %all%
endlocal

【讨论】:

@BrianUtterback 如果您阅读What should I do when someone answers my question?,我相信这对每个人都会有所帮助,然后回到您的所有问题并采取适当的行动。【参考方案2】:

与 Squashman 已经提供的方法非常相似,但适用于包含! 字符的文件路径。

@For %%G In (First Rest) Do @Set "%%G="
@For /F "Delims=" %%H In (
    '"(For %%G In (%*) Do @Echo "%%~G") | %SystemRoot%\System32\sort.exe"'
) Do @If Not Defined First (Set "First=%%H") Else (SetLocal EnabledelayedExpansion
    For /F "Delims=" %%I In ("!Rest!%%H") Do @EndLocal & Set "Rest=%%I")
exiftool.exe -TagsFromFile %First% -AllDates %Rest%

【讨论】:

以上是关于如何对 Windows 10 批处理文件中的参数列表进行排序的主要内容,如果未能解决你的问题,请参考以下文章

Windows批处理文件:如何将命令行参数与整数进行比较

如何对 SVN 中的特定文件夹使用预提交挂钩(Windows 批处理文件)?

R命令行将文件名传递给参数中的脚本(Windows)

如何使用 tesseract 对文档中的多个列进行 OCR

连接数据框中的所有列

R语言使用caret包的preProcess函数进行数据预处理:对所有的数据列进行scale标准化(数据列中的数值除以标准差)设置method参数为scale