如何像Windows资源管理器一样按文件名排序?

问题描述 投票:0回答:4

这是应用到 powershell 时著名的“ASCIIbetical”顺序与“Natural”顺序的问题。为了能够像资源管理器一样在 powershell 中进行排序,您可以在 StrCmpLogicalW API 上使用这个包装器,它实际上对 Windows 资源管理器执行自然排序。但这需要一些管道。

然而,这篇文章表明Python中有一个三行排序的实现。人们希望 Get-ChildItem cmdlet 或至少文件系统提供程序可以具有内置的自然排序选项。不幸的是,他们没有。

那么问题来了,在 Powershell 中最简单的实现是什么?简单地说,我的意思是要编写的代码量最少,并且可能不需要第三方/外部脚本/组件。理想情况下,我想要一个 short Powershell 函数来为我进行排序。

powershell sorting natural-sort
4个回答
116
投票

TL;博士

Get-ChildItem | Sort-Object { [regex]::Replace($_.Name, '\d+', { $args[0].Value.PadLeft(20) }) }

这里有一些非常短的代码(只是

$ToNatural
脚本块),它使用正则表达式和匹配评估器来完成这个技巧,以便用空格填充数字。然后我们像往常一样用填充数字对输入进行排序,并最终得到自然顺序。

$ToNatural = { [regex]::Replace($_, '\d+', { $args[0].Value.PadLeft(20) }) }

'----- test 1 ASCIIbetical order'
Get-Content list.txt | Sort-Object

'----- test 2 input with padded numbers'
Get-Content list.txt | %{ . $ToNatural }

'----- test 3 Natural order: sorted with padded numbers'
Get-Content list.txt | Sort-Object $ToNatural

输出:

----- test 1 ASCIIbetical order
1.txt
10.txt
3.txt
a10b1.txt
a1b1.txt
a2b1.txt
a2b11.txt
a2b2.txt
b1.txt
b10.txt
b2.txt
----- test 2 input with padded numbers
                   1.txt
                  10.txt
                   3.txt
a                  10b                   1.txt
a                   1b                   1.txt
a                   2b                   1.txt
a                   2b                  11.txt
a                   2b                   2.txt
b                   1.txt
b                  10.txt
b                   2.txt
----- test 3 Natural order: sorted with padded numbers
1.txt
3.txt
10.txt
a1b1.txt
a2b1.txt
a2b2.txt
a2b11.txt
a10b1.txt
b1.txt
b2.txt
b10.txt

最后我们使用这个单行代码按名称按自然顺序对文件进行排序:

Get-ChildItem | Sort-Object { [regex]::Replace($_.Name, '\d+', { $args[0].Value.PadLeft(20) }) }

输出:

    Directory: C:\TEMP\_110325_063356

Mode                LastWriteTime     Length Name                                                                                                                  
----                -------------     ------ ----                                                                                                                  
-a---        2011-03-25     06:34          8 1.txt                                                                                                                 
-a---        2011-03-25     06:34          8 3.txt                                                                                                                 
-a---        2011-03-25     06:34          8 10.txt                                                                                                                
-a---        2011-03-25     06:34          8 a1b1.txt                                                                                                              
-a---        2011-03-25     06:34          8 a2b1.txt                                                                                                              
-a---        2011-03-25     06:34          8 a2b2.txt                                                                                                              
-a---        2011-03-25     06:34          8 a2b11.txt                                                                                                             
-a---        2011-03-25     06:34          8 a10b1.txt                                                                                                             
-a---        2011-03-25     06:34          8 b1.txt                                                                                                                
-a---        2011-03-25     06:34          8 b2.txt                                                                                                                
-a---        2011-03-25     06:34          8 b10.txt                                                                                                               
-a---        2011-03-25     04:54         99 list.txt                                                                                                              
-a---        2011-03-25     06:05        346 sort-natural.ps1                                                                                                      
-a---        2011-03-25     06:35         96 test.ps1                                                                                                              

10
投票

请允许我复制并粘贴另一个问题的答案。

Powershell 排序-带有数字的对象名称不正确

Windows 资源管理器使用 shlwapi.dll 中的旧版 API,称为

StrCmpLogicalW
,这就是看到不同排序结果的原因。

我不想补零,所以写了一个脚本。

https://github.com/LarrysGIT/Powershell-Natural-sort

由于我不是 C# 专家,如果不整洁,请提出拉取请求。

找到以下PowerShell脚本,它使用相同的API。

function Sort-Naturally
{
    PARAM(
        [System.Collections.ArrayList]$Array,
        [switch]$Descending
    )

    Add-Type -TypeDefinition @'
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace NaturalSort {
    public static class NaturalSort
    {
        [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
        public static extern int StrCmpLogicalW(string psz1, string psz2);
        public static System.Collections.ArrayList Sort(System.Collections.ArrayList foo)
        {
            foo.Sort(new NaturalStringComparer());
            return foo;
        }
    }
    public class NaturalStringComparer : IComparer
    {
        public int Compare(object x, object y)
        {
            return NaturalSort.StrCmpLogicalW(x.ToString(), y.ToString());
        }
    }
}
'@
    $Array.Sort((New-Object NaturalSort.NaturalStringComparer))
    if($Descending)
    {
        $Array.Reverse()
    }
    return $Array
}

在下面查找测试结果。

PS> # Natural sort
PS> . .\NaturalSort.ps1
PS> Sort-Naturally -Array @('2', '1', '11')
1
2
11
PS> # If regular sort is being used
PS> @('2', '1', '11') | Sort-Object
1
11
2

PS> # Not good
PS> $t = ls .\testfiles\*.txt
PS> $t | Sort-Object
1.txt
10.txt
2.txt

PS> # Good
PS> Sort-Naturally -Array $t
1.txt
2.txt
10.txt

8
投票

我更喜欢 @Larry Song 的答案,因为它的排序方式与 Windows 资源管理器完全一样。我尝试稍微简化一下以减少干扰。

Add-Type -TypeDefinition @"
using System.Runtime.InteropServices;
public static class NaturalSort
{
    [DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
    private static extern int StrCmpLogicalW(string psz1, string psz2);
    public static string[] Sort(string[] array)
    {
        System.Array.Sort(array, (psz1, psz2) => StrCmpLogicalW(psz1, psz2));
        return array;
    }
}
"@

然后你可以像这样使用它:

$array = ('1.jpg', '10.jpg', '2.jpg')
[NaturalSort]::Sort($array)

输出:

1.jpg
2.jpg
10.jpg

3
投票

从 python 到 PowerShell 的翻译效果非常好:

function sort-dir {
    param($dir)
    $toarray = {
        @($_.BaseName -split '(\d+)' | ?{$_} |
        % { if ([int]::TryParse($_,[ref]$null)) { [int]$_ } else { $_ } })
    }
    gci $dir | sort -Property $toarray
}

#try it
mkdir $env:TEMP\mytestsodir
1..10 + 100..105 | % { '' | Set-Content $env:TEMP\mytestsodir\$_.txt }
sort-dir $env:TEMP\mytestsodir
Remove-Item $env:TEMP\mytestsodir -Recurse

当您使用代理函数方法时,您可以做得更好。您将

-natur
参数添加到
Sort-Object
,您就得到了非常漂亮的解决方案。

更新:首先,我对 PowerShell 以这种方式处理比较数组感到非常惊讶。当我尝试创建测试文件

("a0", "a100", "a2") + 1..10 + 100..105 | % { '' | Set-Content $env:TEMP\mytestsodir\$_.txt }
后,结果发现它不起作用。所以,我认为没有像这样优雅的解决方案,因为 PowerShell 在幕后是静态的,而 python 是动态的。

© www.soinside.com 2019 - 2024. All rights reserved.