Mam linię (lub wiele linii) liczb, które są ograniczone dowolnym znakiem. Jakich narzędzi UNIX mogę użyć do sortowania elementów każdego wiersza numerycznie, zachowując separator?
Przykłady obejmują:
- lista liczb; wkład
10 50 23 42
:; posortowane:10 23 42 50
- Adres IP; wkład
10.1.200.42
:; posortowane:1.10.42.200
- CSV; wkład
1,100,330,42
:; posortowane:1,42,100,330
- rozdzielany potokami; wkład
400|500|404
:; posortowane:400|404|500
Ponieważ separator jest arbitralny, możesz podać (lub rozszerzyć) odpowiedź za pomocą jednoznakowego separatora wybranego przez siebie.
sort
numeric-data
Jeff Schaller
źródło
źródło
cut
obsługuje dowolne ograniczniki z tą-d
opcją.4,325 comma 55 comma 42,430
nie wystąpiłby, ani1.5 period 4.2
).Odpowiedzi:
Możesz to osiągnąć za pomocą:
zamień kropki
.
na separator.dodaj
-u
dosort
powyższego polecenia, aby usunąć duplikaty.lub za pomocą
gawk
( GNUawk
) możemy przetwarzać wiele linii, a powyższe można również rozszerzyć:zamień
*
jako separator pól naSEP='*'
swój ogranicznik .Uwagi:
Może być konieczne użycie
-g, --general-numeric-sort
opcjisort
zamiast-n, --numeric-sort
obsługi dowolnej klasy liczb (liczba całkowita, liczba zmiennoprzecinkowa, liczba naukowa, liczba szesnastkowa itp.).W
awk
braku zmian potrzeba, to nadal będzie obchodzenia tych.źródło
Używanie
perl
jest oczywistą wersją; podziel dane, posortuj je, dołącz ponownie.Separator musi być wymieniony dwukrotnie (raz w
split
i raz wjoin
)np. dla
,
Więc
Ponieważ
split
jest wyrażeniem regularnym, znak może wymagać cytowania:Korzystając z opcji
-a
i-F
, można usunąć podział. Za pomocą-p
pętli, jak poprzednio, ustaw wyniki na$_
, które automatycznie wydrukują:źródło
-l
opcji zamiast używaćchomp
. To także dodaje nową linię po wydrukowaniu. Zobacz także-a
(z-F
) część dzielącą.-l
i-F
jest jeszcze ładniej:perl -F'/\./' -le 'print join(".", sort {$a <=> $b} @F)'
-l
opcję; Tęskniłem za tym!-F
flagi początkowo, ponieważ nie działa ona poprawnie we wszystkich wersjach (np. twoja linia w CentOS 7 - perl 5.16.3 - zwraca puste wyjście, chociaż działa dobrze w Debianie 9). Ale w połączeniu z-p
tym daje nieco mniejszy wynik, więc dodałem to jako alternatywę dla odpowiedzi. pokazując, jak-F
można z niego korzystać. Dzięki!-a
i-n
opcje, kiedy-F
są używane i-n
kiedy-a
są używane ... więc po prostu zmień-le
na-lane
Używając Pythona i podobnego pomysłu jak w odpowiedzi Stephena Harrisa :
Więc coś takiego:
Niestety konieczność ręcznego wykonania operacji we / wy sprawia, że jest to o wiele mniej eleganckie niż wersja Perla.
źródło
Skrypt Bash:
Przykład:
Oparte na
Podziel ciąg na tablicę w Bash
Jak posortować tablicę w Bash
Dołącz elementy tablicy?
źródło
Muszla
Ładowanie języka wyższego poziomu wymaga czasu.
Dla kilku linii sama powłoka może być rozwiązaniem.
Możemy użyć polecenia zewnętrznego
sort
i poleceniatr
. Jedna jest dość wydajna w sortowaniu linii, a druga skutecznie konwertuje jeden separator na nowe linie:To wymaga basha z powodu użycia
<<<
tylko. Jeśli to zostanie zastąpione tutaj-doc, rozwiązanie jest ważne dla posix.Jest zdolny do sortowania pól z kartami, spacji lub znaków powłoki glob (
*
,?
,[
). Nie nowe wiersze, ponieważ każda linia jest sortowana.Zmień,
<<<"$2"
aby<"$2"
przetwarzać nazwy plików i nazwać to tak:Separator jest taki sam dla całego pliku. Jeśli jest to ograniczenie, można je ulepszyć.
Jednak przetworzenie pliku zawierającego zaledwie 6000 linii zajmuje 15 sekund. Naprawdę powłoka nie jest najlepszym narzędziem do przetwarzania plików.
Awk
W przypadku więcej niż kilku wierszy (więcej niż kilku dziesiątek) lepiej jest użyć prawdziwego języka programowania. Rozwiązaniem awk może być:
Co zajmuje tylko 0,2 sekundy dla tego samego pliku 6000 linii wspomnianego powyżej.
Zrozum, że
<"$2"
pliki for można zmienić z powrotem<<<"$2"
na wiersze wewnątrz zmiennych powłoki.Perl
Najszybszym rozwiązaniem jest perl.
Jeśli chcesz posortować zmianę pliku,
<<<"$a"
po prostu"$a"
i dodać-i
opcje perla, aby edycja pliku była „na miejscu”:źródło
Używanie
sed
do sortowania oktetów adresu IPsed
nie ma wbudowanejsort
funkcji, ale jeśli dane są wystarczająco ograniczone w zakresie (np. z adresami IP), możesz wygenerować skrypt sed, który ręcznie implementuje prosty sortowanie bąbelkowe . Podstawowym mechanizmem jest wyszukiwanie sąsiednich liczb, które są poza kolejnością. Jeśli liczby są nieprawidłowe, zamień je.Sam
sed
skrypt zawiera dwie komendy wyszukiwania i zamiany dla każdej pary liczb poza kolejnością: jedna dla pierwszych dwóch par oktetów (wymuszanie obecności ogranicznika końcowego w celu oznaczenia końca trzeciego oktetu) oraz drugi dla trzeciej pary oktetów (koniec EOL). Jeśli wystąpią zamiany, program rozgałęzia się na początek skryptu, szukając liczb, które nie są uporządkowane. W przeciwnym razie wychodzi.Wygenerowany skrypt jest częściowo:
Takie podejście sztywno koduje kropkę jako separator, który musi być poprzedzony znakiem ucieczki, ponieważ w przeciwnym razie byłby „specjalny” w składni wyrażeń regularnych (dopuszczając dowolny znak).
Aby wygenerować taki skrypt sed, ta pętla wykona:
Powiedzmy, że przekieruj wyjście tego skryptu do innego pliku
sort-ips.sed
.Przykładowy przebieg może wyglądać następująco:
Poniższa odmiana skryptu generującego używa znaczników granicy słowa
\<
i\>
pozbywa się potrzeby drugiego podstawienia. Zmniejsza to również rozmiar generowanego skryptu z 1,3 MB do nieco poniżej 900 KB, a także znacznie skraca czas działaniased
samego programu (do około 50% -75% oryginału, w zależności odsed
używanej implementacji):źródło
sed
jest śmieszne, dlatego jest ciekawym wyzwaniem.Oto kilka bashów, które same odgadują ogranicznik:
Może nie jest zbyt wydajny ani czysty, ale działa.
Użyj jak
bash my_script.sh "00/00/18/29838/2"
.Zwraca błąd, gdy ten sam ogranicznik nie jest używany konsekwentnie lub gdy dwa lub więcej ograniczników następuje jeden za drugim.
Jeśli użyty ogranicznik jest znakiem specjalnym, to jest on oznaczany znakiem ucieczki (w przeciwnym razie
sed
zwraca błąd).źródło
Ta odpowiedź jest oparta na nieporozumieniu Q., ale w niektórych przypadkach i tak jest poprawna. Jeśli dane wejściowe są liczbami całkowicie naturalnymi i mają tylko jeden separator na linię (jak w przykładowych danych w Q.), działa poprawnie. Będzie także obsługiwał pliki z liniami, z których każdy ma swój separator, co jest nieco więcej niż to, o co prosiliśmy.
Ta funkcja powłoki
read
s ze standardowego wejścia, używa podstawienia parametrów POSIX, aby znaleźć określony separator w każdym wierszu (przechowywany w$d
), i używatr
do zastąpienia$d
znakiem nowej linii\n
isort
danych tego wiersza, a następnie przywraca oryginalne ograniczniki każdego wiersza:Dotyczy danych podanych w PO :
Wynik:
źródło
W przypadku dowolnych ograniczników:
Na wejściu takim jak:
To daje:
źródło
To powinno obsłużyć dowolny niecyfrowy (0-9) ogranicznik. Przykład:
Wynik:
źródło
Z
perl
:Z
ruby
, co jest nieco podobne doperl
Komenda niestandardowa i przekazywanie tylko łańcucha separatora (nie regex). Działa, jeśli dane wejściowe zawierają również dane zmiennoprzecinkowe
Niestandardowe polecenie dla
perl
Dalsza lektura - Mam już tę poręczną listę jednoliniowych perli / rubinów
źródło
Poniżej znajduje się odmiana odpowiedzi Jeffa w tym sensie, że generuje
sed
skrypt, który wykona sortowanie bąbelkowe, ale jest wystarczająco różny, aby uzasadnić własną odpowiedź.Różnica polega na tym, że zamiast generować podstawowe wyrażenia regularne O (n ^ 2), generuje to rozszerzone wyrażenia regularne O (n). Wynikowy skrypt będzie miał około 15 KB. Czas działania
sed
skryptu to ułamki sekundy (wygenerowanie skryptu trwa nieco dłużej).Ogranicza się do sortowania dodatnich liczb całkowitych oddzielonych kropkami, ale nie ogranicza się do wielkości liczb całkowitych (po prostu zwiększenie
255
w głównej pętli) lub liczby liczb całkowitych. Separator można zmienić, zmieniającdelim='.'
kod.Zrobiłem wszystko, aby poprawnie wyobrazić sobie wyrażenia regularne, więc opowiem opisywanie szczegółów na kolejny dzień.
Skrypt będzie wyglądał mniej więcej tak:
Ideą wygenerowanych wyrażeń regularnych jest dopasowanie wzorca dla liczb, które są mniejsze niż każda liczba całkowita; te dwie liczby byłyby zepsute, a więc są zamieniane. Wyrażenia regularne są pogrupowane w kilka opcji OR. Zwróć szczególną uwagę na zakresy dołączane do każdego elementu, czasem tak jest
{0}
, co oznacza, że poprzedni element należy pominąć podczas wyszukiwania. Opcje wyrażenia regularnego od lewej do prawej odpowiadają liczbom mniejszym niż podana liczba o:Aby przeliterować przykład, weź
101
(z dodatkowymi spacjami dla czytelności):Tutaj pierwsza zmiana pozwala na liczby od 100 do 100; druga zmiana pozwala na 0 do 99.
Innym przykładem jest
154
:Tutaj pierwsza opcja pozwala na 150 do 153; drugi pozwala od 100 do 149, a ostatni pozwala od 0 do 99.
Testowanie cztery razy w pętli:
Wynik:
źródło
Podział danych wejściowych na wiele wierszy
Za pomocą
tr
można podzielić dane wejściowe za pomocą dowolnego ogranicznika na wiele wierszy.Wejście to można następnie uruchomić
sort
(używając,-n
jeśli dane wejściowe są numeryczne).Jeśli chcesz zachować separator na wyjściu, możesz użyć go
tr
ponownie, aby dodać separator z powrotem.np. używając spacji jako separatora
cat input.txt | tr " " "\n" | sort -n | tr "\n" " "
wejście:
1 2 4 1 4 32 18 3
wyjście:1 1 2 3 4 4 18 32
źródło