Piszę skrypt powłoki, używając dowolnych ogólnych poleceń UNIX. Muszę pobrać wiersz, który ma najmniej znaków (łącznie z białymi odstępami). Może być do około 20 linii.
Wiem, że mogę użyć head -$L | tail -1 | wc -m
liczby znaków w wierszu L. Problem polega na tym, że jedyną metodą, o której mogę pomyśleć, byłoby ręczne napisanie bałaganu instrukcji if, porównując wartości.
Przykładowe dane:
seven/7
4for
8 eight?
five!
Wróci, 4for
ponieważ linia ta ma najmniej znaków.
W moim przypadku, jeśli wiele linii ma najkrótszą długość, jedna powinna zostać zwrócona. Nie ma znaczenia, który z nich zostanie wybrany, o ile ma on minimalną długość. Ale nie widzę szkody w pokazywaniu obu sposobów innym użytkownikom w innych sytuacjach.
shell
text-processing
wc
Matthew D. Scholefield
źródło
źródło
Odpowiedzi:
Perlowy sposób. Zauważ, że jeśli jest wiele linii o tej samej, najkrótszej długości, to podejście wydrukuje tylko jeden z nich:
Wyjaśnienie
perl -lne
:-n
oznacza „odczytaj plik wejściowy wiersz po wierszu”,-l
powoduje usunięcie końcowych znaków nowej linii z każdej linii wejściowej i dodanie nowego wiersza do każdegoprint
wywołania; i-e
jest skryptem, który zostanie zastosowany do każdej linii.$m//=$_
: ustaw$m
na bieżącą linię ($_
), chyba że$m
zdefiniowano.//=
Operator jest dostępny od Perl 5.10.0.$m=$_ if length()<length($m)
: jeśli długość bieżącej wartości$m
jest większa niż długość bieżącej linii, zapisz bieżącą linię ($_
) jako$m
.END{print $m if $.}
: po przetworzeniu wszystkich linii wydrukuj bieżącą wartość$m
najkrótszej linii. Wif $.
gwarantuje, że dzieje się tak dopiero, gdy liczba linii ($.
) jest zdefiniowana, unikając drukowania pustą linię do pustego wkładu.Alternatywnie, ponieważ plik jest wystarczająco mały, aby zmieścić się w pamięci, możesz:
Wyjaśnienie
@K=sort{length($a) <=> length($b)}<>
:<>
tutaj jest tablica, której elementami są linie pliku.sort
Je sortować według ich długości i posortowane wiersze są zapisywane w tablicy@K
.print "$K[0]"
: wydrukuj pierwszy element tablicy@K
: najkrótszą linię.Jeśli chcesz wydrukować wszystkie najkrótsze linie, możesz użyć
źródło
-C
aby zmierzyć długość pod względem liczby znaków zamiast liczby bajtów. W ustawieniach regionalnych UTF-8$$
ma mniej bajtów niż€
(2 vs 3), ale więcej znaków (2 vs 1).Z
sqlite3
:źródło
strace
wskazuje). Jeśli potrzebujesz pracować z naprawdę dużymi plikami (a twój system nie zamienia się), możesz wymusić to, dodając nazwę pliku podobną do,sqlite3 $(mktemp)
a wszystkie dane zostaną zapisane na dysk.Oto wariant
awk
rozwiązania drukowania pierwszej znalezionej linii minimalnej:który można po prostu rozszerzyć o jeden warunek, aby wydrukować wszystkie minimalne wiersze:
źródło
Python wychodzi dość zwięźle, a kod robi to, co mówi na puszce:
python -c "import sys; print min(sys.stdin, key=len),"
Przyznaję, że ostatni przecinek jest niejasny. Zapobiega dodaniu instrukcji print do dodatkowego podziału wiersza. Dodatkowo możesz napisać to w Pythonie 3, obsługując 0 linii, takich jak:
python3 -c "import sys; print(min(sys.stdin, key=len, default='').strip('\n'))"
źródło
Zawsze uwielbiam rozwiązania z czystym skryptem powłoki (bez exec!).
Uwaga :
Wystąpił problem z bajtami NUL na wejściu. Więc
printf "ab\0\0\ncd\n" | bash this_script
drukujeab
zamiastcd
.źródło
bash
przekonałaby mnie do wprowadzenia pośredniego wynikusort
.var=$(get data)
ponieważ ogranicza przepływ danych do jednego kontekstu - ale gdy przenosisz dane przez potok - w strumieniu - każdy zastosowany exec jest ogólnie pomocny - ponieważ umożliwia wyspecjalizowanie stosowanie programów modułowych tylko w razie potrzeby.$IFS
nie jest dyskryminujący cyfrowo - nawet jeśli nie ma żadnej$IFS
wartości domyślnej , chociaż wiele powłok zaakceptuje wstępnie skonfigurowaną konfigurację środowiska$IFS
- i dlatego nie jest to szczególnie niezawodna wartość domyślna./bin/sh
jest dostępne nic innego jak statyczne powiązanie . Zdarzyło mi się to kilka razy z hostami SunOS4 z/usr
zagubionymi lub.so
uszkodzonymi, a teraz w epoce Linuksa wciąż czasami spotykam podobne sytuacje z systemami osadzonymi lub systemami z błędem rozruchu. BusyBox to jedna z wielkich rzeczy, które niedawno nabyliśmy.Oto czyste
zsh
rozwiązanie (drukuje wszystkie linie o minimalnej długości, odfile
):Przykładowe dane wejściowe:
Dane wyjściowe to:
Myślę, że potrzebuje krótkiego wyjaśnienia :-)
Najpierw ustawiamy wewnętrzny separator pól na nowy wiersz:
Jak dotąd tak dobrze, teraz najtrudniejsza część.
print
używa-l
flagi do wydrukowania wyniku oddzielonego znakiem nowej linii zamiast spacji.Teraz zaczynamy od środka:
Plik jest odczytywany wiersz po wierszu i traktowany jak tablica. Następnie:
o
Flaga mówi, że wynik powinien być uporządkowane w kolejności rosnącej, te@
środki do leczenia wynik jako tablicę też. Część za (//?/?
) jest podstawieniem i zastępuje wszystkie znaki znakiem?
. Teraz:Bierzemy pierwszy element tablicy
[1]
, który jest najkrótszy, w twoim przypadku jest teraz????
.Dopasowywanie jest wykonywane dla każdego elementu tablicy osobno, a niedopasowane elementy tablicy są usuwane (
M
). Każdy pasujący element????
(4 znaki) pozostaje w tablicy. Pozostałe elementy to te, które mają 4 znaki (najkrótsze).Edycja: Jeśli potrzebujesz tylko jednej z najkrótszych linii, ta zmodyfikowana wersja drukuje pierwszą:
źródło
... a zwycięzcą jest ... linia 2, wydaje się.
Problem polega jednak na tym, że każda linia musi mieć ponad dwukrotną długość, aby mogła działać - więc LINE_MAX jest skutecznie zmniejszony o połowę. Powodem jest to, że używa - co, podstawa 1? - do reprezentowania długości linii. Podobnym - i być może bardziej uporządkowanym - podejściem może być kompresowanie tych informacji w strumieniu. Pierwszym pomysłem, który przychodzi mi do głowy, jest to, że powinienem to
unexpand
zrobić:To drukuje ...
Kolejny, po prostu
sed
:Składnia jest zgodna ze standardami - ale to nie gwarantuje, że każdy stary
sed
poradzi sobie\(reference-group\)\{counts\}
poprawnie - wielu tego nie robi.Zasadniczo stosuje to samo wyrażenie regularne do danych wejściowych wielokrotnie - co może być bardzo korzystne, gdy nadszedł czas na ich kompilację. Ten wzór to:
Który pasuje do różnych ciągów znaków na różne sposoby. Na przykład:
... jest dopasowywane z
s
in\1
i''
łańcuchem zerowym w\2
.... jest dopasowane
1
w\1
i\nstring2\nstring3
w\2
... jest dopasowywane z
\n
in\1
i''
łańcuchem zerowym w\2
. Byłoby to problematyczne, gdyby istniała jakakolwiek szansa, że\n
ewline pojawi się na początku przestrzeni wzorów - ale polecenia/^\n/D
i//!g
służą do tego. Korzystałem z niego,[^\n]
ale inne potrzeby związane z tym małym skryptem sprawiły, że przenośność była problemem i nie byłem zadowolony z wielu sposobów, w jaki często jest on źle interpretowany. Plus.
jest szybszy.... dopasowuj
\n
is
ponownie do\1
i oba uzyskują''
ciąg zerowy\2
. Puste linie w ogóle się nie zgadzają.Kiedy wzór jest nakładany
g
lobalnie, dwa odchylenia - zarówno odchylenie standardowe najbardziej lewe, jak i mniejsze\n
odchylenie ewline po prawej stronie - są równoważone w celu pominięcia. Kilka przykładów:... jeśli wszystkie zastosowano (nie po kolei) do następującego ciągu ...
... przekształci go w ...
Zasadniczo używam wyrażenia regularnego, aby zawsze obsługiwać tylko pierwszą linię w dowolnej przestrzeni wzorców, do której ją stosuję. To pozwala mi żonglować dwiema różnymi wersjami zarówno zachowanej linii o najkrótszym jak dotąd dopasowaniu, jak i najnowszej linii bez uciekania się do pętli testowych - każda zastosowana zamiana obsługuje całą przestrzeń wzorców naraz.
Różne wersje są niezbędne do dosłownego porównywania ciągów / ciągów - dlatego musi istnieć wersja każdego wiersza, w której wszystkie znaki są równe. Ale oczywiście, jeśli jedno lub drugie powinno faktycznie zakończyć się najwcześniejszą pojawiającą się najkrótszą linią na wejściu, to linia drukowana na wyjściu powinna być prawdopodobnie oryginalną wersją linii - a nie tą, którą zdezynfekowałem / zhomogenizowałem dla porównania. Potrzebuję więc dwóch wersji każdego z nich.
To niefortunne, że kolejną koniecznością jest wiele przełączeń buforów, aby poradzić sobie z tym samym - ale przynajmniej żaden bufor nigdy nie przekracza więcej niż czterech linii potrzebnych do utrzymania aktualności - a więc może nie jest straszny.
W każdym razie dla każdego cyklu pierwszą rzeczą, która się dzieje, jest transformacja zapamiętanej linii - ponieważ jedyną faktycznie zapisaną kopią jest dosłowny oryginał - w ...
... a następnie
n
linia wejściowa ext zastępuje stary bufor. Jeśli nie zawiera co najmniej jednego znaku, jest skutecznie ignorowany. O wiele łatwiej byłoby po prostu skorzystaćq
z pierwszego pustego wiersza, ale cóż, moje dane testowe zawierały wiele takich i chciałem obsłużyć wiele akapitów.I tak, jeśli zawiera znak, jego dosłowna wersja jest dołączana do zapamiętanej linii, a jego wersja porównawcza z odstępami jest umieszczana na początku przestrzeni wzorów, jak poniżej:
Na koniec stosuje się podstawienie do tej przestrzeni wzorów:
Więc jeśli nowa linia zmieści się w przestrzeni potrzebnej do przechowywania zapamiętanej linii z co najmniej jednym znakiem do zaoszczędzenia, wówczas pierwsze dwie linie zostaną podstawione, w przeciwnym razie tylko pierwsza.
Niezależnie od wyniku, pierwsza linia w przestrzeni wzorów jest zawsze
D
usuwana na końcu cyklu przed ponownym uruchomieniem. Oznacza to, że jeśli nowy wiersz jest krótszy niż ostatni ciąg ...... jest odsyłany z powrotem do pierwszej substytucji w cyklu, która zawsze usuwa tylko pierwszy znak nowego wiersza - i dlatego pozostaje cała. Ale jeśli nie jest to ciąg ...
... zamiast tego rozpocznie się następny cykl, a pierwsze podstawienie usunie z niego ciąg ...
...każdego razu.
W ostatnim wierszu zapamiętana linia jest wypisywana na standardowe wyjście, więc dla podanych danych przykładowych wypisuje:
Ale poważnie, użyj
tr
.źródło
REINPUT | sort -t: -nk1,1 | cut -d: -f3-
. A druga to prosta kwestia włączenia kolejnegosed
--expression
skryptu do ogona.sort
„s zachowanie jako tie-breaker gdy występują linie tej samej długości na wejściu - tak najwcześniej występujących linia zawsze unosi się do góry w tej sprawie.Próbować:
Chodzi o to, aby
awk
najpierw wydrukować długość każdej linii. Będzie to wyglądać jak:Następnie użyj liczby znaków, aby posortować linie
sort
,cut
pozbyć się liczby ihead
zachować pierwszą linię (tę z najmniejszą liczbą znaków). W tym przypadku możesz oczywiście użyćtail
wiersza zawierającego najwięcej znaków.(Zostało przyjęte na podstawie tej odpowiedzi )
źródło
head -1
tail
(ponieważhead
może wyjść, gdy tylko zadanie zostanie wykonane, bez odczytywania reszty danych wejściowych).Z POSIX awk:
źródło
L
była najlepsza litera do wybrania nazwy zmiennej: D Coś takiegomin
wyjaśniłobyPożyczanie niektórych pomysłów @ mikeserv:
Pierwszy
sed
wykonuje następujące czynności:h
zapisuje oryginalną linię w buforze wstrzymania:
- ma to na celu usunięcie niebezpieczeństwa wstrzyknięcia koduexpr length "whole line"
- jest to wyrażenie powłoki, które można ocenićs
to GNU sed rozszerzenie do oceny przestrzeni wzorców i ponownego umieszczenia wyniku w przestrzeni wzorców.G
dołącza nową linię i zawartość przestrzeni wstrzymania (pierwotna linia) do przestrzeni wzorus
zastępuje nowy wiersz tabulatoremLiczba znaków jest teraz liczbą na początku każdej linii, więc
sort -n
sortuje się według długości linii.Końcowy
sed
następnie usuwa wszystkie oprócz pierwszej (najkrótszej) linii i długości linii i drukuje wynik.źródło
expr
jest tu ładniej. Tak,e
spawnuje powłokę dla każdej linii. Zredagowałem wyrażenie sed tak, aby zastępowało każdy znak w ciągu ciągiem:
przed eval, co moim zdaniem powinno usunąć wszelkie możliwości wstrzyknięcia kodu.xargs expr
osobiście - ale oprócz unikania pośredniej powłoki, to chyba bardziej stylistyczna rzecz. W każdym razie lubię to.Przyszło mi do głowy, że całość jest możliwa w jednym
sed
wyrażeniu. To nie jest ładne:Podział tego:
BSD sed w OS X jest nieco bardziej wybredny w przypadku nowych linii. Ta wersja działa zarówno dla wersji sed jak BSD i GNU:
Zauważ, że jest to bardziej odpowiedź „ponieważ jest to możliwe” niż poważna próba udzielenia odpowiedzi na najlepszą praktykę. Wydaje mi się, że to oznacza, że gram za dużo kodu-colf
źródło
man sed
w systemie OS X: „Sekwencja zmiany znaczenia \ n odpowiada znakowi nowej linii osadzonemu w obszarze wzorów” . Więc myślę, że GNU sed zezwala\n
na regex i na zamianę, podczas gdy BSD pozwala tylko\n
na regex, a nie na zamianę.\n
z obszaru wzorców jest dobrym pomysłem i działałoby w drugims///
wyrażeniu, ales/.*/&\n&/
wyrażenie to wstawia znak\n
do obszaru wzorców, w którym wcześniej go nie było. Wydaje się również, że BSD sed wymaga dosłownie nowych linii po definicjach etykiet i rozgałęzieniach.sed
skrypt powinien być plikiem tekstowym, ale nie musi kończyć się nowym wierszem . Możesz więc zwykle rozgraniczać je również jako osobne argumenty -sed -e :\ label -e :\ label2
i tak dalej. Ponieważ i tak robisz1h
, możesz po prostu przełączyć się na logikę opartą nax;H
uzyskiwaniu nowej linii - i możesz przyciąć wiodącą nową linię z przestrzeni wzorów na końcu cyklu bez wciągania nowej linii w /D
.G
pierwszy i zmieniającs///
wyrażenie. Dzielenie go za pomocą-e
pozwala, aby wszystko przebiegało w jednym (długim) wierszu bez dosłownych nowych linii.\n
Ucieczka jest spec''d dlased
„s LHS, zbyt, i myślę, że to stwierdzenie jest spec verbatim, oprócz tego, że POSIX wyrażenia wysięgniki są również spec''d w taki sposób że wszystkie postacie tracą - specjalne znaczenie (jawnie łącznie\\
) - w jednym z wyjątkiem nawiasów, myślnik jako separator zakresu, i kropka, równa się, karetka, dwukropek dla zestawienia, równoważności, negacji i klas.Inne rozwiązanie perla: przechowuj linie w haszowaniu tablic, przy czym kluczem skrótu jest długość linii. Następnie wydrukuj linie z minimalnym kluczem.
źródło
push @{$lines{+length}};
iprint @{$lines{+min keys %lines}};
mniej pisać :)perl -MList::Util=min -nE'push @{$l{+length}},$_}END{say@{$l{min keys%l}}' sample
perl
robi się trochę poważnie dla tych z nas, którzy nie są na równiperl
z tajemniczą naturą. BTW. golfedsay
drukuje fałszywą pustą linię na końcu.Aby uzyskać tylko pierwszą najkrótszą linię:
Aby uzyskać wszystkie najkrótsze kłaczki, po prostu zmień
{p;q}
nap
Inna metoda (nieco nietypowy) ma mieć
sort
zrobić rzeczywisty sortowanie wg długości . Jest stosunkowo wolny nawet przy krótkich liniach i staje się znacznie wolniejszy wraz ze wzrostem długości linii.Jednak pomysł sortowania według nakładających się klawiszy jest dość interesujący. Zamieszczam go na wypadek, gdyby inni również uznali to za interesujące / informacyjne.
Jak to działa:
Sortuj według wariantów długości tego samego klucza -
key 1
który obejmuje całą linięKażdy kolejny wariant klucza zwiększa długość klucza o jeden znak, aż do długości najdłuższej linii pliku (określonej przez
wc -L
)Aby uzyskać tylko pierwszą (posortowaną) najkrótszą linię:
który jest taki sam jak:
źródło
Zakładając, że puste linie nie są uważane za najkrótsze i że mogą istnieć puste linie, zadziała następujący czysty AWK:
źródło
Co z użyciem sortowania?
źródło
Z GNU awk
Wczytaj każdą linię do tablicy indeksowanej według długości linii.
Ustaw
PROCINFO["sorted_in"]
się@ind_num_asc
do siły skanowania tablica zamawiane przez indeks tablicy, sortowane numerycznieUstawienie
PROCINFO
w powyższy sposób wymusza, aby linia o najmniejszej długości była pobierana jako pierwsza podczas przechodzenia przez tablicę. Więc wydrukuj pierwszy element z tablicy i wyjdźWadą jest to
nlogn
, że niektóre inne podejścia sąn
na czasźródło
Mid-level metoda narzędzia powłoki, bez
sed
lubawk
:źródło
$f
zmiennej; Mam pojęcie, które może być możliwe wtee
jakiś sposób ...