Come ordinare per nome file nello stesso modo in cui si trova Windows Explorer?

Questo è il famoso problema dell’ordine “ASCIIbetic” rispetto all’ordine “Naturale” applicato a PowerShell. Per poter ordinare in PowerShell allo stesso modo di explorer, è ansible utilizzare questo wrapper su API StrCmpLogicalW, che esegue effettivamente l’ordinamento naturale per Esplora risorse. Ciò richiederà tuttavia alcuni impianti idraulici.

Tuttavia, questo articolo suggerisce che esiste un’implementazione a tre linee del genere in python. Si spera che il cmdlet Get-ChildItem o almeno il provider del file system possa avere un’opzione di ordinamento naturale integrata. Sfortunatamente no.

Quindi ecco la domanda, qual è la più semplice implementazione di questo in PowerShell? Con parole semplici intendo la minor quantità di codice da scrivere, e possibilmente senza script / componenti esterni di terze parti. Idealmente voglio una breve funzione PowerShell che faccia l’ordinamento per me.

Ecco un codice molto breve (solo il blocco di script $ToNatural ) che fa il trucco con un’espressione regolare e un valutatore di corrispondenza per riempire i numeri con spazi. Quindi ordiniamo l’input con i numeri riempiti come al solito e in effetti otteniamo un ordine naturale come risultato.

 $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 

Produzione:

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

E infine usiamo questo one-liner per ordinare i file in base ai nomi in ordine naturale:

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

Produzione:

  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 

La conversione da Python a PowerShell funziona abbastanza bene:

 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 

Puoi farlo ancora meglio quando usi l’ approccio della funzione Proxy . Aggiungi il parametro -natur a Sort-Object e hai una soluzione piuttosto bella.

Aggiornamento : per prima cosa sono rimasto piuttosto sorpreso dal fatto che PowerShell gestisca il confronto degli array in questo modo. Dopo aver provato a creare file di test ("a0", "a100", "a2") + 1..10 + 100..105 | % { '' | Set-Content $env:TEMP\mytestsodir\$_.txt } ("a0", "a100", "a2") + 1..10 + 100..105 | % { '' | Set-Content $env:TEMP\mytestsodir\$_.txt } ("a0", "a100", "a2") + 1..10 + 100..105 | % { '' | Set-Content $env:TEMP\mytestsodir\$_.txt } , si è scoperto che non funziona. Quindi, penso che non ci sia una soluzione elegante, perché PowerShell è statico sotto le copertine, mentre python è dinamico.

Consentitemi di copiare e incollare la mia risposta da un’altra domanda.

Il nome dell’object di ordinamento PowerShell con i numeri non funziona correttamente

Windows explorer utilizza un’API legacy in shlwapi.dll chiamata StrCmpLogicalW durante l’ordinamento delle stringhe, motivo per cui visualizziamo risultati di ordinamento diversi.

Non voglio usare il metodo di riempimento degli zeri, quindi scrivi uno script.

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

Poiché non sono un esperto di C #, le richieste di pull sono apprezzate.

Trova il seguente script di PowerShell, usando la stessa API.

 function Sort-Naturally { PARAM( [string[]]$strArray ) 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()); } } } '@ return [NaturalSort.NaturalSort]::Sort($strArray) } 

Trova i risultati dei test di seguito.

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

E,

 PS> # Not good PS> $t = (ls .\Scripts*.txt).name PS> $t | Sort-Object Scripts1.txt Scripts10.txt Scripts2.txt PS> # Good PS> Sort-Naturally -strArray $t Scripts1.txt Scripts2.txt Scripts10.txt