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 x
linie. tail -n 5
dałoby ci 5 ostatnich linii danych wejściowych. +
Rodzaju znakiem odwraca argument i dokonać tail
wydruku cokolwiek ale pierwsze x-1
linie. tail -n +1
wypisałby cały plik, tail -n +2
wszystko oprócz pierwszej linii itp.
GNU tail
jest znacznie szybszy niż sed
. tail
jest również dostępny w BSD, a -n +2
flaga 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; tail
powinien po prostu czytać plik linia po linii, podczas gdy sed
wykonuje 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 tail
zostanie wywołane przez powłokę:
$FILE
tail
tail
procesu na$FILE
tail
czyta z teraz pustego $FILE
Jeś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.
-r
opcją. Może gdzieś w systemie jest ustawiony bufor? A -n
może 32-bitowy numer ze znakiem?
tail
bę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 *.csv
jest prawdopodobnie szybszy.
Państwo może edytować pliki w kolejności: wystarczy użyć Perl -i
flagę 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 n
operacja O ( ), w której n
jest 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?).
sponge
Util unika konieczności żonglowania pliku tymczasowego:
tail -n +2 "$FILE" | sponge "$FILE"
sponge
jest rzeczywiście znacznie czystszy i bardziej niezawodny niż przyjęte rozwiązanie ( tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
)
sponge
buforuje cały plik w pamięci? To nie zadziała, jeśli będzie to setki GB.
sponge
bę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 ed
zamiast swojego s treaming następcy sed
:
ed "$FILE" <<<$'1d\nwq\n'
ed
Komenda był oryginalny edytor tekstu UNIX, zanim nie było nawet terminale pełnoekranowe, stacje robocze znacznie mniej graficznych. ex
Redaktor, 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ż ed
ma 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 ed
polecenia 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ół 1d
jednego 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ą.