Sort logically like in Windows Explorer (Powershell hybrid)
Posted: 11 Aug 2019 06:11
If an Explorer list view is sorted by name then digits are taken as numeric values rather than text. Furthermore the sorting ignores the case of characters.
The API function used to compare the file names is StrCmpLogicalW which is not accessible in Batch. However, we can use Batch to call Powershell that wraps a C# class that imports Windows API functions, and use it.
References:
https://docs.microsoft.com/en-us/window ... mplogicalw
https://docs.microsoft.com/en-us/dotnet ... comparer-1
Fully commented macro code:
Example (comments at the end of lines removed for readability reasons):
Output:
Steffen
The API function used to compare the file names is StrCmpLogicalW which is not accessible in Batch. However, we can use Batch to call Powershell that wraps a C# class that imports Windows API functions, and use it.
References:
https://docs.microsoft.com/en-us/window ... mplogicalw
https://docs.microsoft.com/en-us/dotnet ... comparer-1
Fully commented macro code:
Code: Select all
:: %logicalsort_asc% and %logicalsort_desc% macros
:: sort a list of lines in an Explorer-like manner
:: strings are read from standard input and written to standard output
set logicalsort_asc=powershell -nop -ep Bypass -c ^"try{ %======================== invoke Powershell with the code specified after -c =%^
%=% Add-Type ' %================================================================== add a .NET class to the Powershell session =%^
%=====% using System.Runtime.InteropServices; %=================================== namespace used for platform invokation =%^
%=====% public class SCmpLgcl : System.Collections.Generic.IComparer^<string^>{ %= custom comparer class for a List of string =%^
%=========% [DllImport(\"shlwapi.dll\",CharSet=CharSet.Unicode)] %================ import StrCmpLogicalW which is the comparison function used =%^
%=========% static extern int StrCmpLogicalW(string a,string b); %=============== to sort Windows Explorer list views =%^
%=========% public SCmpLgcl(){} %================================================= public constructor of the comparer class =%^
%=========% public int Compare(string a,string b){return StrCmpLogicalW(a,b);} %== public Compare method of the comparer that calls StrCmpLogicalW =%^
%=====% }'; %===================================================================== end of the .NET class =%^
%=% $list=[System.Collections.Generic.List[string]]@($Input); %=================== cast the standard input to a List of string =%^
%=% $list.Sort([SCmpLgcl]::new()); %============================================== invoke the Sort method of the List using an instance of the comparer class =%^
%=% $list; %====================================================================== write the sorted List to the standard output =%^
%=% exit 0;}catch{exit 1;}^" %==================================================== return a suitable errorlevel =%
(set logicalsort_desc=%logicalsort_asc:(a,b)=(b,a)%) %============================ reversed parameters passed to StrCmpLogicalW lead to descending sorting =%
Code: Select all
@echo off &setlocal
:: %logicalsort_asc% and %logicalsort_desc% macros
:: sort a list of lines in an Explorer-like manner
:: strings are read from standard input and written to standard output
set logicalsort_asc=powershell -nop -ep Bypass -c ^"try{^
%=% Add-Type '^
%=====% using System.Runtime.InteropServices;^
%=====% public class SCmpLgcl : System.Collections.Generic.IComparer^<string^>{^
%=========% [DllImport(\"shlwapi.dll\",CharSet=CharSet.Unicode)]^
%=========% static extern int StrCmpLogicalW(string a,string b);^
%=========% public SCmpLgcl(){}^
%=========% public int Compare(string a,string b){return StrCmpLogicalW(a,b);}^
%=====% }';^
%=% $list=[System.Collections.Generic.List[string]]@($Input);^
%=% $list.Sort([SCmpLgcl]::new());^
%=% $list;^
%=% exit 0;}catch{exit 1;}^"
(set logicalsort_desc=%logicalsort_asc:(a,b)=(b,a)%)
:: write an example file
>"_to_sort.txt" (
echo a3b4
echo a11b15
echo a5b
echo a100b
echo a3b18
echo x1b
echo a11b6
echo a3b
echo a2b
echo a9b
)
echo just print to screen (ascending sorted)
<"_to_sort.txt" %logicalsort_asc%
echo(
echo or process the output in a FOR /F loop (descending sorted)
for /f "delims=" %%i in ('^<"_to_sort.txt" %logicalsort_desc%') do echo %%i
echo(
echo or redirect to a file (ascending once again)
<"_to_sort.txt" >"_sorted.txt" %logicalsort_asc%
type "_sorted.txt"
echo(
:: clean up
del "_to_sort.txt" "_sorted.txt"
pause
Output:
Code: Select all
just print to screen (ascending sorted)
a2b
a3b
a3b4
a3b18
a5b
a9b
a11b6
a11b15
a100b
x1b
or process the output in a FOR /F loop (descending sorted)
x1b
a100b
a11b15
a11b6
a9b
a5b
a3b18
a3b4
a3b
a2b
or redirect to a file (ascending once again)
a2b
a3b
a3b4
a3b18
a5b
a9b
a11b6
a11b15
a100b
x1b
Drücken Sie eine beliebige Taste . . .