Mam plik CSV, który wygląda tak
AS2345, ASDF1232, Mr. Plain Example, 110 Binary ave., Atlantis, RI, 12345, (999) 123-5555,1,56 AS2345, ASDF1232, Mrs. Plain Example, 1121110 Ternary st. 110 Binary ave .., Atlantis, RI, 12345, (999) 123-5555,1.56 AS2345, ASDF1232, Mr. Plain Example, 110 Binary ave., Liberty City, RI, 12345, (999) 123-5555,1,56 AS2345, ASDF1232, Mr.Plain Example, 110 Ternary ave., Some City, RI, 12345, (999) 123-5555,1.56
Muszę to posortować według długości linii, w tym spacji. Poniższe polecenie nie zawiera spacji, czy istnieje sposób na zmodyfikowanie go, aby działał dla mnie?
cat $@ | awk '{ print length, $0 }' | sort -n | awk '{$1=""; print $0}'
Odpowiedzi:
Odpowiedź
Lub, aby wykonać oryginalne (być może niezamierzone) sortowanie dowolnych równych długości wierszy:
W obu przypadkach rozwiązaliśmy zgłoszony problem, odsuwając się od awk do ostatecznego cięcia.
Linie o dopasowanej długości - co zrobić w przypadku krawata:
Pytanie nie określało, czy dalsze sortowanie było potrzebne dla wierszy o pasującej długości. Założyłem, że jest to niepożądane i zasugerowałem użycie
-s
(--stable
), aby zapobiec sortowaniu takich wierszy względem siebie i utrzymywać je we względnej kolejności, w jakiej występują na wejściu.(Ci, którzy chcą mieć większą kontrolę nad sortowaniem tych powiązań, mogą spojrzeć na
--key
opcję sortowania ).Dlaczego próba rozwiązania tego pytania zawodzi (przebudowa wiersza awk):
Warto zauważyć różnicę między:
Dają odpowiednio
Odpowiedniej sekcji (gawk'S) instrukcja wspomina tylko na marginesie, że awk będzie odbudować całe $ 0 (na podstawie separatora, etc) w przypadku zmiany jednego pola. Myślę, że to nie jest szalone zachowanie. Ma to:
„Wreszcie są chwile, kiedy wygodnie jest zmusić awk do odbudowania całego rekordu przy użyciu bieżącej wartości pól i OFS. Aby to zrobić, użyj pozornie nieszkodliwego przypisania:”
„To zmusza awk do odbudowania rekordu”.
Wejście testowe zawierające kilka wierszy o równej długości:
źródło
cat $@
jest zepsuty. Zdecydowanie chcesz to zacytować, na przykładcat "$@"
Rozwiązanie AWK od Neillb jest świetne, jeśli naprawdę chcesz go używać
awk
i wyjaśnia, dlaczego jest to kłopotliwe, ale jeśli chcesz, aby praca została wykonana szybko i nie obchodzi Cię, w czym to robisz, jednym z rozwiązań jest użyciesort()
Funkcja Perla z niestandardową procedurą Caparison do iteracji po liniach wejściowych. Oto jedna linijka:Możesz umieścić to w swoim potoku gdziekolwiek tego potrzebujesz, otrzymując STDIN (od
cat
lub przekierowanie powłoki) lub po prostu podaj nazwę pliku perlowi jako kolejny argument i pozwól mu otworzyć plik.W moim przypadku musiałem najdłuższe linie pierwszy, więc zamieniłem się
$a
i$b
w porównaniu.źródło
cat testfile.txt | perl -e 'print sort { length($a) <=> length($b) } <>' > out.txt
type testfile.txt | perl -e "print sort { length($a) <=> length($b) } <>" > out.txt
Zamiast tego spróbuj tego polecenia:
źródło
Wyniki testów porównawczych
Poniżej znajdują się wyniki testu porównawczego rozwiązań z innych odpowiedzi na to pytanie.
Metoda badania
Wyniki
perl
Rozwiązanie Caleba zajęło 11,2 sekundyperl
rozwiązanie zajęło 11,6 sekundyawk
Rozwiązanie nr 1 Neillba zajęło 20 sekundawk
Rozwiązanie nr 2 Neillba zajęło 23 sekundyawk
Rozwiązanie anubhavy zajęło 24 sekundyawk
Rozwiązanie Jonathana zajęło 25 sekundbash
rozwiązanie trwa 400 razy dłuższy odawk
rozwiązania (stosując skróconą sprawdzian 100000 linii). Działa dobrze, po prostu trwa wieczność.Dodatkowa
perl
opcjaDodałem również kolejne rozwiązanie Perla:
źródło
Czysty bas:
źródło
length()
Funkcja ma zawierać spacji. Wprowadziłbym tylko drobne poprawki do twojego rurociągu (w tym unikanie UUOC ).sed
Polecenie bezpośrednio usuwa cyfr i okrężnicy dodane przezawk
polecenia. Możesz też nie dopuścić do formatowaniaawk
:źródło
Zauważyłem, że te rozwiązania nie będą działać, jeśli plik zawiera wiersze zaczynające się od liczby, ponieważ zostaną one posortowane numerycznie wraz ze wszystkimi policzonymi wierszami. Rozwiązanie to daje
sort
się-g
(ogólnie numeryczne-rodzaju) flagi zamiast-n
(numeryczna sortowania)źródło
-n
sugerowanej zmiany metody sortowania z na twoją,-g
aby uzyskać jakąkolwiek poprawę, więc nie spodziewam się. W mojej odpowiedzi odniosłem się teraz do tego, jak zabronić sortowania podrzędnego wierszy o równej długości (przy użyciu--stable
). Czy to miałeś na myśli, czy nie, dziękuję za zwrócenie mi na to uwagi! Dodałem również przemyślane wejście do przetestowania.awk
część wygeneruje listę linii z prefiksem długości linii i spacją. Rurowaniesort -n
będzie działać zgodnie z oczekiwaniami. Ale jeśli któryś z tych wierszy ma już numer na początku, te wiersze będą rozpoczynać się od długości + spacja + liczba.sort -n
pomija tę przestrzeń i potraktuje ją jako jedną liczbę połączoną z długości + liczba. Użycie-g
flagi spowoduje zamiast tego zatrzymanie się na pierwszym miejscu, dając poprawne sortowanie. Spróbuj sam, tworząc plik z kilkoma wierszami z prefiksami liczbowymi i uruchamiaj polecenie krok po kroku.sort -n
pomija przestrzeń i powoduje nieprawidłowe sortowanie.sort -g
wyświetla poprawną kolejność.-n
w formaciesort (GNU coreutils) 8.21
.info
Dokumentacja opisuje-g
jako potencjalnie mniej wydajne i mniej precyzyjny (konwertuje numery do pływaków), więc prawdopodobnie nie używaj go, jeśli nie trzeba.-n
: „Sortuj numerycznie. Numer zaczyna się w każdym wierszu i składa się z opcjonalnych odstępów, opcjonalnego znaku„ - ”oraz zera lub większej liczby cyfr, ewentualnie oddzielonych separatorami tysięcy, po których opcjonalnie następuje znak przecinka dziesiętnego i zero lub więcej cyfr . Pusta liczba jest traktowana jako „0”. Ustawienia regionalne „LC_NUMERIC” określają znak przecinka dziesiętnego i separator tysięcy. Domyślnie spacja to spacja lub tabulator, ale ustawienie regionalne „LC_CTYPE” może to zmienić. "Z POSIX Awk:
Przykład
źródło
1) czyste rozwiązanie awk. Załóżmy, że długość linii nie może być wtedy większa niż 1024
nazwa pliku kota | awk 'BEGIN {min = 1024; s = "";} {l = length ($ 0); if (l <min) {min = l; s = 0 $;}} END {print s} '
2) jedno rozwiązanie liniowe bash zakładające, że wszystkie linie mają tylko 1 słowo, ale można je przerobić dla każdego przypadku, w którym wszystkie linie mają taką samą liczbę słów:
LINES = $ (nazwa pliku cat); dla k w $ LINES; do printf "$ k"; echo $ k | wc -L; gotowe | sort -k2 | głowa -n 1 | wytnij -d "" -f1
źródło
Oto wielobajtowa metoda sortowania wierszy według długości. To wymaga:
wc -m
jest dostępny dla Ciebie (ma go macOS).LC_ALL=UTF-8
. Możesz to ustawić albo w swoim .bash_profile, albo po prostu dodając go przed następującym poleceniem.testfile
ma kodowanie znaków zgodne z Twoim ustawieniem regionalnym (np. UTF-8).Oto pełne polecenie:
Wyjaśniając część po części:
l=$0; gsub(/\047/, "\047\"\047\"\047", l);
← tworzy kopię każdej linii w zmiennej awkl
i zawiera podwójne znaki specjalne,'
tak aby można ją było bezpiecznie powtórzyć jako polecenie powłoki (\047
jest to pojedynczy cudzysłów w notacji ósemkowej).cmd=sprintf("echo \047%s\047 | wc -m", l);
← to jest polecenie, które wykonamy, które jest echem linii uciekającejwc -m
.cmd | getline c;
← wykonuje polecenie i kopiuje liczbę znaków, która jest zwracana do zmiennej awkc
.close(cmd);
← zamknij potok do polecenia powłoki, aby uniknąć przekroczenia przez system ograniczenia liczby otwartych plików w jednym procesie.sub(/ */, "", c);
← przycina białe znaki z wartości liczby znaków zwróconej przezwc
.{ print c, $0 }
← wyświetla liczbę znaków w linii, spację i oryginalny wiersz.| sort -ns
← sortuje wiersze (według liczby poprzedzonych znaków) numerycznie (-n
), zachowując stabilną kolejność sortowania (-s
).| cut -d" " -f2-
← usuwa wartości liczby znaków dołączonych na początku.Jest wolny (tylko 160 linii na sekundę na szybkim Macbooku Pro), ponieważ musi wykonać polecenie podrzędne dla każdej linii.
Alternatywnie, zrób to tylko z
gawk
(od wersji 3.1.5, gawk obsługuje wiele bajtów), co byłoby znacznie szybsze. Wykonywanie wszystkich znaków ucieczki i podwójnych cudzysłowów w celu bezpiecznego przepuszczenia wierszy przez polecenie powłoki z awk jest bardzo kłopotliwe, ale jest to jedyna metoda, jaką udało mi się znaleźć, która nie wymaga instalowania dodatkowego oprogramowania (gawk nie jest domyślnie dostępny w System operacyjny Mac).źródło