Подтвердить что ты не робот

Как сортировать по имени файла так же, как Windows Explorer?

Это известная проблема "ASCIIbetical" порядка и "естественного" порядка применительно к powershell. Чтобы иметь возможность сортировать в powershell так же, как это делает проводник, вы можете использовать эту оболочку через интерфейс StrCmpLogicalW, который фактически выполняет естественную сортировку для Windows Проводник. Это потребует некоторой сантехники.

Однако эта статья предполагает, что в python существует три реализации liner такого типа. Можно было бы надеяться, что командлет Get-ChildItem или, по крайней мере, поставщик файловой системы может иметь встроенную функцию естественной сортировки. К сожалению, они этого не делают.

Итак, вот вопрос, что является простейшей реализацией этого в Powershell? Простым я имею в виду наименьшее количество кода для записи и, возможно, сторонних/внешних скриптов/компонентов. В идеале я хочу короткую функцию Powershell, которая будет сортировать для меня.

4b9b3361

Ответ 1

Вот очень короткий код (только блок $ToNatural script), который выполняет трюк с регулярным выражением и оценщиком совпадений для заполнения чисел пробелами. Затем мы сортируем вход с заполненными числами как обычно и фактически получаем естественный порядок в результате.

$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                                                                                                              

Ответ 2

Позвольте мне скопировать и вставить свой ответ на другой вопрос.

Имя объекта сортировки Powershell с номерами не правильно

Проводник Windows использует устаревший API из shlwapi.dll, который называется StrCmpLogicalW, и причина сортировки различна.

Я не хочу дополнять нулями, поэтому написал сценарий.

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

Поскольку я не являюсь экспертом в С#, приветствуются запросы на получение уведомлений, если они не аккуратны.

Найдите следующий скрипт 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

Ответ 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 является динамическим.