To dotyczy pracy domowej, ale nie będę zadawać konkretnego pytania dotyczącego pracy domowej.
Muszę użyć głowy i ogona, aby pobrać różne zestawy linii z jednego pliku. Podobnie jak linie 6-11 i linie 19-24 i zapisz je oba do innego pliku. Wiem, że mogę to zrobić za pomocą append, takiego jak
head -11 file|tail -6 > file1; head -24 file| tail -6 >> file1.
Ale nie sądzę, że powinniśmy.
Czy istnieje konkretny sposób na połączenie poleceń głowy i ogona, a następnie zapisanie do pliku?
head
itail
? Jeśli tak, Twoje rozwiązanie jest prawie najlepsze, co możesz zrobić. Jeśli możesz korzystać z innych programówsed
lub możeszawk
pozwolić na lepsze rozwiązania (tj. Z mniejszą liczbą wywołań procesów).>>
) załączając dwa polecenia w nawiasach, aby przekierować ich wyjście łączone:(head -11 file | tail -6; head -24 file | tail -6) > file1
. To naprawdę sprowadza się do osobistych preferencji, co jest przyjemniejsze.Odpowiedzi:
Możesz to zrobić za pomocą
head
pojedynczej i podstawowej arytmetyki, jeśli zgrupujesz polecenia za{ ... ; }
pomocą konstrukcji podobnej dogdzie wszystkie polecenia mają takie same dane wejściowe (dzięki @mikeserv ).
Uzyskiwanie linii 6-11 i linii 19-24 jest równoważne z:
Zasadniczo uruchomiłbyś:
źródło
Za pomocą
{ … }
konstrukcji grupującej można zastosować operator przekierowania do polecenia złożonego.Zamiast powielać pierwsze linie M + N i zachowywać tylko ostatnie N, możesz pominąć pierwsze M linii i powielić następne N. Jest to zauważalnie szybsze w przypadku dużych plików . Uważaj, że
+N
argumentemtail
nie jest liczba linii do pominięcia, ale jeden plus, że - to liczba pierwszej linii do wydrukowania z liniami o numerze od 1.Tak czy inaczej, plik wyjściowy jest otwierany tylko raz, ale plik wejściowy jest przesuwany jeden raz dla każdego fragmentu do wyodrębnienia. Co powiesz na grupowanie danych wejściowych?
Ogólnie to nie działa. (Może działać na niektórych systemach, przynajmniej gdy dane wejściowe są zwykłymi plikami.) Dlaczego? Z powodu buforowania danych wejściowych . Większość programów, w tym
tail
także nie odczytuje bajtów bajt po bajcie, ale kilka kilobajtów na raz, ponieważ jest to szybsze.tail
Czyta więc kilka kilobajtów, przeskakuje nieco na początku, przekazuje nieco więcejhead
i zatrzymuje - ale to, co jest czytane, jest czytane i nie jest dostępne dla następnego polecenia.Innym podejściem jest użycie
head
pipowania/dev/null
do omijania linii.Ponownie nie gwarantuje się, że zadziała z powodu buforowania. Zdarza się, że działa z
head
poleceniem z GNU coreutils (tym, które można znaleźć w niewbudowanych systemach Linux), gdy dane wejściowe pochodzą ze zwykłego pliku. Dzieje się tak, ponieważ gdy ta implementacjahead
przeczytała, co chce, ustawia pozycję pliku na pierwszy bajt, którego nie wyprowadził. To nie działa, jeśli wejście jest potokiem.Prostszym sposobem wydrukowania kilku sekwencji wierszy z pliku jest wywołanie bardziej ogólnego narzędzia, takiego jak sed lub awk . (Może to być wolniejsze, ale ma to znaczenie tylko w przypadku bardzo dużych plików).
źródło
Wiem, że powiedziałeś, że musisz używać głowy i ogona, ale sed jest zdecydowanie prostszym narzędziem do tego zadania.
Możesz nawet budować bloki w ciągu z innym procesem i przeprowadzać je przez sed.
-n neguje dane wyjściowe, a następnie określasz zakresy do wydrukowania za pomocą p, przy czym pierwszą i ostatnią liczbę zakresu oddzielamy przecinkiem.
To powiedziawszy, możesz albo grupować polecenia, które sugerował @don_crissti, albo kilkakrotnie zapętlać plik z głową / ogonem, chwytając kawałek linii za każdym razem, gdy przejdziesz.
Im więcej wierszy w pliku i im więcej bloków masz, tym bardziej wydajny będzie sed.
źródło
Z
sed
tobą możesz zrobić:... Być może można by uzyskać bardziej wydajne rozwiązanie
head
. Don już pokazał, jak to może działać bardzo dobrze, ale bawiłem się również tym. Coś, co możesz zrobić, aby rozwiązać ten konkretny przypadek:... który wywołałby
head
4 razy pisząc naoutfile
lub w/dev/null
zależności od tego, czy wartość tej iteracji$n
jest liczbą parzystą czy nieparzystą.W bardziej ogólnych przypadkach, zebrałem to razem z kilku innych rzeczy, które już miałem:
Może to zrobić coś takiego:
... które drukuje ...
Oczekuje, że jego pierwszy argument będzie liczbą powtórzeń poprzedzoną znakiem
-
, lub, w przypadku braku takiej liczby , tylko-
. Jeśli podano liczbę, powtórzy wzór linii podany w poniższych argumentach tyle razy, ile określono, i zatrzyma się, gdy tylko to zrobi.Dla każdego następującego argumentu zinterpretuje ujemną liczbę całkowitą wskazującą liczbę wierszy, w której należy zapisać,
/dev/null
oraz dodatnią liczbę całkowitą wskazującą liczbę wierszy, w której należy zapisaćstdout
.Tak więc w powyższym przykładzie wypisuje pierwsze 5 wierszy do
/dev/null
, następne 6 dostdout
, następne 7 do/dev/null
ponownie i kolejne 6 ponownie dostdout
. Po osiągnięciu ostatniego z argumentów i pełnym przejściu-1
liczby powtórzeń, następnie kończy pracę. Gdyby był pierwszy argument,-2
powtórzyłby ten proces jeszcze raz lub-
tak długo, jak to możliwe.Dla każdego cyklu argowania
while
pętla jest przetwarzana raz. Na górze każdej pętli pierwszy wiersz zstdin
jest wczytywany do zmiennej powłoki$l
. Jest to konieczne, ponieważwhile head </dev/null; do :; done
będzie się powtarzało w nieskończoność -head
wskazuje w swoim zwrocie, kiedy osiągnie koniec pliku. Zatem kontrola EOF jest dedykowanaread
iprintf
zapisze$l
plus nowy wierszstdout
tylko wtedy, gdy drugi argument jest dodatnią liczbą całkowitą.read
Check komplikuje pętli trochę, bo zaraz po drugiej pętli nazywa -for
pętla która iteruje argumenty2-$#
reprezentowane w$n
dla każdej iteracji jego macierzystejwhile
pętli. Oznacza to, że dla każdej iteracji pierwszy argument musi być pomniejszony o jeden z wartości określonej w wierszu poleceń, ale wszystkie pozostałe powinny zachować swoje oryginalne wartości, więc wartość zmiennej$_n
var jest odejmowana od każdej, ale zawsze zawiera wartość wartość większa niż 0 dla pierwszego argumentu.To stanowi główną pętlę funkcji, ale większość kodu znajduje się na górze i ma na celu umożliwić tej funkcji czyste buforowanie nawet potoku jako danych wejściowych. Działa to najpierw poprzez wywołanie tła w
dd
celu skopiowania go do pliku tmp na wyjściu przy rozmiarach bloków 4k na kawałek. Następnie funkcja ustanawia pętlę wstrzymania - która prawie nigdy nie powinna ukończyć nawet jednego pełnego cyklu - tylko po to, aby upewnić się, żedd
dokonała przynajmniej jednego zapisu do pliku, zanim funkcja zastąpi swoje standardowe wejście deskryptorem pliku powiązanym z plikiem tmp i potem natychmiast rozłącza plik za pomocąrm
. Dzięki temu funkcja niezawodnie przetwarza strumień, nie wymagając pułapek ani w inny sposób do oczyszczenia - jak tylko funkcja zwolni swoje roszczenie na fd, plik tmp przestanie istnieć, ponieważ jego jedyne nazwane łącze do systemu plików zostało już usunięte.źródło
Użyj funkcji bash w następujący sposób:
W tym przypadku jest to trochę przesada, ale jeśli twoje filtry powiększą się, może stać się dobrodziejstwem.
źródło