Sortuj nie sortując linii za pomocą potoku „|” w nim poprawnie

17

Próbuję posortować proste dane rozdzielane potokami. Jednak sortowanie nie jest tak naprawdę sortowaniem. Przenosi mój wiersz nagłówka na dół, ale moje dwa wiersze zaczynające się od 241 są dzielone przez wiersz zaczynający się od 24.

cat sort_fail.csv
column_a|column_b|column_c
241|212|20810378
24|121|2810172
241|213|20810376

sort sort_fail.csv
241|212|20810378
24|121|2810172
241|213|20810376
column_a|column_b|column_c

Nagłówki kolumn są przenoszone na dół pliku, więc sortowanie wyraźnie je przetwarza. Ale rzeczywiste wartości nie są sortowane, jak się spodziewałam.

W tym przypadku pracowałem nad tym

sort sort_fail.csv --field-separator='|' -k1,1

Ale wydaje mi się, że nie powinno to być konieczne. Dlaczego sortowanie nie sortuje?

użytkownik10777668
źródło
2
użyć LC_COLLATE=C sort. W zależności od tego, czego oczekujesz, możesz także potrzebowaćLC_COLLATE=C sort -t'|' -n
mosvy
3
Aby posortować dane w stylu „CSV” może chcesz korzystać csvsortz csvkit, które prawidłowo obsługuje notowane wartości.
Bakuriu,

Odpowiedzi:

32

sort rozpoznaje ustawienia regionalne, więc w zależności od ustawienia LC_COLLATE (dziedziczonego z LANG) możesz uzyskać różne wyniki:

$ LANG=C sort sort_fail.csv 
241|212|20810378
241|213|20810376
24|121|2810172
column_a|column_b|column_c

$ LANG=en_US sort sort_fail.csv
241|212|20810378
24|121|2810172
241|213|20810376
column_a|column_b|column_c

Może to powodować problemy w skryptach, ponieważ możesz nie być świadomy tego, na jakie ustawienia regionalne są wywoływane, a zatem możesz uzyskać różne wyniki.

Często zdarza się, że skrypty wymuszają wymagane ustawienie

na przykład

$ grep 'LC.*sort' /bin/precat
      LC_COLLATE=C sort -u | prezip-bin -z "$cmd: $2"

Ciekawe, że |postać wygląda dziwnie.

Ale to dlatego, że mówi domyślna reguła en_US, która wywodzi się z ISO

$ grep 007C /usr/share/i18n/locales/iso14651_t1_common
<U007C> IGNORE;IGNORE;IGNORE;<j> # 142 |

Co oznacza, że |znak jest ignorowany, a kolejność sortowania wyglądałaby tak, jakby znak nie istniał.

$ tr -d '|' < sort_fail.csv | LANG=C sort
24121220810378
241212810172
24121320810376
column_acolumn_bcolumn_c

I to pasuje do „nieoczekiwanego” sortowania, które widzisz.

Obejściem tego jest użycie -n(do wymuszenia sortowania numerycznego) lub użycie separatora pól (tak jak to zrobiłeś) lub użycie Custawień regionalnych.

Stephen Harris
źródło
Fascynujący. Widziałem kilka innych trafień dotyczących lokalizacji, ale pomyślałem, że wpłynie to na względną kolejność 24 na 241, a nie coś takiego.
user10777668,
7
coś bardzo przydatnego w sortowaniu GNU to --debugopcja, która wskazuje klawisz (podkreślony) użyty do porównania
Jeff Schaller
uruchamianie z --debug tylko podkreśla całą linię - sort zawiera znak potoku, po prostu nie ma wpływu z powodu lokalizacji. Jest to dobra funkcja, ale nie pomogła mi w tym przypadku (próbowałem :)
user10777668
Właśnie dlatego wspomniałem o tym, @ user10777668 - oznacza to, że sortużywa całej linii zamiast zatrzymywania się na znakach, które zakładamy.
Jeff Schaller
Nie spodziewałem się, że to się skończy. Spodziewałem się, że rozpozna znak potoku i uwzględni go w tym rodzaju, dlatego potraktowałem 24 | 1 i 241 inaczej. Nie jestem pewien, jak --debug by to zmienił, a biorąc pod uwagę, że podkreśla on | wygląda na to, że aktywnie oderwałoby to uwagę od prawdziwego problemu, w którym lokalizacja doprowadziła do zapisania znaku potoku
10777668,
1

Denerwuje mnie to, że 24nie porusza się ze swojego miejsca między nimi 241. Drugie pole zaczyna się od 1. Próbując tego rodzaju z prowadzeniem 4na drugim polu, 24przesuwa się w dół, więc podejrzewam, że sortpo prostu ignoruje, |chyba że podano inaczej. Spróbuj sort -n...

RudiC
źródło
1

-n, --numeric-sort porównaj według wartości liczbowej ciągu

210
23

Bez -n, 210 przez tekst wyprzedza 23, jeśli chodzi o znak mojej postaci.

michaelkrieger
źródło
Masz rację, ale to nie tłumaczy, że char wyróżnia się inaczej. Inne odpowiedzi pokazują, że z powodu ustawień regionalnych potok jest traktowany jako nieobecny, więc kolejna cyfra decyduje o kolejności.
Criggie,