Brian Utterback
Brian Utterback

Reputation: 61

How to sort the argument list in a a Windows 10 batch file

I need to call a program with a list of file names, but I need to find and extract the first file on the list taken as sorted order and pass the rest to the program.

Specifically, I want to pass a list of files selected with the QTTabbar application launcher and execute exiftool so that the first file on the list is used for the "-TagsFromFile" option, and then process all of the rest of the files so that they get the "-AllDates" option applied. So my first attempt was:

exiftool -TagsFromFile %1 -AllDates %*

This would put the first file on the list, but since exiftool would be setting it to the same value as it already has, that would be acceptable.

However, I discovered that QTTabbar did not pass the arguments to the batch file in the lexigraphically sorted order by name as I was expecting. So I figured I needed to sort the list.

I found the way to sort the arguments in How to sort the arguments dropped to a batch file? but in that solution there is a loop and a program is invoked once for each argument, rather than building a new argument list.

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

Instead of "echo use "%%a", I need to build a new argument list that I can pass to exiftool. Ideally I could build a list and then replace the original argument list with the new one, like the "set" command in Bash. Failing that, I could build a new list and use that, but I don't know how to build a list and I don't know how to reference the first element if I had one.

How do I do this?

EDIT: The files are selected in the File Explorer GUI. The order they are presented to the batch file is determined by Windows. Here is the output of "echo %*" from the batch file:

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

As you can see, the last file appears first. I don't know why. Sometimes they are in reverse order.

So, the batch file gets invoked as:

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"

And I want exiftool to run as:

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"

Upvotes: 0

Views: 244

Answers (2)

Compo
Compo

Reputation: 38623

Very similar to the method already provided by Squashman, but suitable for filepaths containing ! characters.

@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%

Upvotes: 1

Squashman
Squashman

Reputation: 14290

So your code was really close. All you needed to add were a few basic concepts to get the first argument into a variable which can be done by checking if a variable is defined or not. Then use the SET command to rebuild all the arguments back into another variable. This requires delayed expansion as described in this 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

Upvotes: 3

Related Questions