Muszę wielokrotnie usuwać pierwszą linię z dużego pliku tekstowego za pomocą skryptu bash.
Obecnie używam sed -i -e "1d" $FILE- ale usunięcie zajmuje około minuty.
Czy istnieje bardziej skuteczny sposób na osiągnięcie tego?
Muszę wielokrotnie usuwać pierwszą linię z dużego pliku tekstowego za pomocą skryptu bash.
Obecnie używam sed -i -e "1d" $FILE- ale usunięcie zajmuje około minuty.
Czy istnieje bardziej skuteczny sposób na osiągnięcie tego?
Odpowiedzi:
Spróbuj ogona :
tail -n +2 "$FILE"
-n x: Wystarczy wydrukować ostatnie xlinie. tail -n 5dałoby ci 5 ostatnich linii danych wejściowych. +Rodzaju znakiem odwraca argument i dokonać tailwydruku cokolwiek ale pierwsze x-1linie. tail -n +1wypisałby cały plik, tail -n +2wszystko oprócz pierwszej linii itp.
GNU tailjest znacznie szybszy niż sed. tailjest również dostępny w BSD, a -n +2flaga jest spójna dla obu narzędzi. Sprawdź strony podręcznika FreeBSD lub OS X, aby uzyskać więcej.
Wersja BSD może być jednak znacznie wolniejsza niż sed. Zastanawiam się, jak im się to udało; tailpowinien po prostu czytać plik linia po linii, podczas gdy sedwykonuje dość złożone operacje obejmujące interpretację skryptu, stosowanie wyrażeń regularnych i tym podobne.
Uwaga: możesz ulec pokusie użycia
# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"
ale to da ci pusty plik . Powodem jest to, że przekierowanie ( >) następuje zanim tailzostanie wywołane przez powłokę:
$FILEtailtailprocesu na$FILEtail czyta z teraz pustego $FILEJeśli chcesz usunąć pierwszy wiersz w pliku, powinieneś użyć:
tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
&&Będzie upewnić się, że plik nie nadpisywane gdy pojawia się problem.
-ropcją. Może gdzieś w systemie jest ustawiony bufor? A -nmoże 32-bitowy numer ze znakiem?
tailbędzie działać dla dowolnego rozmiaru pliku.
-n N means output the last N lines, instead of the last 10; or use +N to output lines starting with the Nth
Możesz użyć -i, aby zaktualizować plik bez użycia operatora „>”. Następujące polecenie usunie pierwszy wiersz z pliku i zapisze go w pliku.
sed -i '1d' filename
unterminated transform source string
sed -i '1,2d' filename
tail -n +2. Nie jestem pewien, dlaczego nie jest to najlepsza odpowiedź.
Dla tych, którzy korzystają z SunOS, który nie jest GNU, pomoże następujący kod:
sed '1d' test.dat > tmp.dat
Nie, to mniej więcej tak wydajne, jak chcesz. Możesz napisać program C, który mógłby wykonać zadanie nieco szybciej (mniej czasu uruchamiania i przetwarzania argumentów), ale prawdopodobnie będzie dążył do tej samej prędkości co sed, gdy pliki stają się duże (i zakładam, że są duże, jeśli zajmuje to minutę ).
Ale twoje pytanie cierpi z powodu tego samego problemu, co wielu innych, ponieważ z góry zakłada rozwiązanie. Jeśli chcesz nam szczegółowo powiedzieć, co chcesz zrobić, a następnie jak , możemy zaproponować lepszą opcję.
Na przykład, jeśli jest to plik A, który przetwarza inny program B, jednym rozwiązaniem byłoby nie usunięcie pierwszego wiersza, ale zmodyfikowanie programu B, aby przetwarzał go inaczej.
Powiedzmy, że wszystkie twoje programy dołączają się do tego pliku A, a program B odczytuje i przetwarza pierwszy wiersz przed jego usunięciem.
Możesz przeprojektować program B, aby nie próbował usunąć pierwszego wiersza, ale zachował trwałe (prawdopodobnie oparte na plikach) przesunięcie do pliku A, aby przy następnym uruchomieniu mógł szukać tego przesunięcia, przetworzyć linię tam i zaktualizuj przesunięcie.
Następnie, w spokojnym czasie (północ?), Mógłby wykonać specjalne przetwarzanie pliku A, aby usunąć wszystkie aktualnie przetwarzane linie i ustawić przesunięcie z powrotem na 0.
Z pewnością szybsze będzie otwieranie programu i wyszukiwanie pliku niż otwieranie i przepisywanie. Ta dyskusja zakłada oczywiście, że masz kontrolę nad programem B. Nie wiem, czy tak jest, ale mogą istnieć inne możliwe rozwiązania, jeśli przekażesz dodatkowe informacje.
awk FNR-1 *.csvjest prawdopodobnie szybszy.
Państwo może edytować pliki w kolejności: wystarczy użyć Perl -iflagę tak:
perl -ni -e 'print unless $. == 1' filename.txt
Powoduje to, że pierwsza linia znika, tak jak pytasz. Perl będzie musiał przeczytać i skopiować cały plik, ale ustawia zapis danych wyjściowych pod nazwą oryginalnego pliku.
Jak powiedział Pax, prawdopodobnie nie będziesz szybciej niż to. Powodem jest to, że prawie nie ma systemów plików obsługujących obcinanie od początku pliku, więc będzie to noperacja O ( ), w której njest rozmiar pliku. Tym, co możesz zrobić znacznie szybciej, jest zastąpienie pierwszego wiersza tą samą liczbą bajtów (być może ze spacjami lub komentarzem), co może działać dla Ciebie w zależności od tego, co próbujesz zrobić (co przy okazji?).
spongeUtil unika konieczności żonglowania pliku tymczasowego:
tail -n +2 "$FILE" | sponge "$FILE"
spongejest rzeczywiście znacznie czystszy i bardziej niezawodny niż przyjęte rozwiązanie ( tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE")
spongebuforuje cały plik w pamięci? To nie zadziała, jeśli będzie to setki GB.
spongebędzie go wchłaniał, ponieważ używa pliku / tmp jako kroku pośredniego, który jest następnie używany do zastąpienia oryginału.
Jeżeli chcesz zmodyfikować plik w miejscu, zawsze można użyć oryginalnego edzamiast swojego s treaming następcy sed:
ed "$FILE" <<<$'1d\nwq\n'
edKomenda był oryginalny edytor tekstu UNIX, zanim nie było nawet terminale pełnoekranowe, stacje robocze znacznie mniej graficznych. exRedaktor, znany jako co używasz podczas wpisywania w okrężnicy szybki w vi, jest ex tendencję wersja ed, więc wiele z tej samej pracy poleceń. Chociaż edma być używany interaktywnie, można go również używać w trybie wsadowym, wysyłając do niego ciąg poleceń, co właśnie robi to rozwiązanie.
Sekwencja <<<$'1d\nwq\n'wykorzystuje wsparcie dla atakujących tutaj-strings ( <<<) i cytaty POSIX ( $'... ') do wejścia zasilającego do edpolecenia składające się z dwóch linii: 1d, która d eletes ustawiają 1 , a następniewq , co wagowo obrzędów plik z powrotem do dysk, a następnie q uits sesję edycji.
powinien pokazywać linie oprócz pierwszej linii:
cat textfile.txt | tail -n +2
Przydałby się do tego vim:
vim -u NONE +'1d' +'wq!' /tmp/test.txt
Powinno to być szybsze, ponieważ vim nie będzie czytał całego pliku podczas przetwarzania.
+wq!czy twoja powłoka jest bash. Prawdopodobnie nie, ponieważ nie !ma go na początku słowa, ale nawyk cytowania rzeczy jest prawdopodobnie dobry. (A jeśli dążysz do super-wydajności, nie przytaczając niepotrzebnie, nie potrzebujesz cytatów wokół 1djednego z nich.)
Ponieważ wygląda na to, że nie mogę przyspieszyć usuwania, myślę, że dobrym rozwiązaniem może być przetworzenie pliku w partiach takich jak ten:
While file1 not empty
file2 = head -n1000 file1
process file2
sed -i -e "1000d" file1
end
Wadą tego jest to, że jeśli program zostanie zabity w środku (lub jeśli jest tam trochę złego sql - powodując śmierć lub blokowanie części „procesu”), pojawią się wiersze, które są pomijane lub przetwarzane dwukrotnie .
(plik1 zawiera wiersze kodu SQL)
Czy wykonanie ogona w wierszach N-1 i przekierowanie go do pliku, a następnie usunięcie starego pliku i zmiana nazwy nowego pliku na starą nazwę, wystarczy?
Gdybym robił to programowo, czytałbym plik i pamiętał przesunięcie pliku, po przeczytaniu każdej linii, więc mogłem wrócić do tej pozycji, aby odczytać plik z jedną linią mniejszą.