Ze względu na nierozpoznany błąd aplikacji mam kilkaset serwerów z pełnym dyskiem. Istnieje jeden plik, który został wypełniony zduplikowanymi wierszami - nie plik dziennika, ale plik środowiska użytkownika ze zmiennymi definicjami (więc nie mogę po prostu usunąć pliku).
Napisałem proste sed
polecenie, aby sprawdzić błędnie dodane wiersze i je usunąć, i przetestowałem je na lokalnej kopii pliku. Działa zgodnie z przeznaczeniem.
Jednak gdy wypróbowałem go na serwerze z pełnym dyskiem, otrzymałem w przybliżeniu następujący błąd (pochodzi z pamięci, nie kopiuj i wklej):
sed: couldn't flush /path/to/file/sed8923ABC: No space left on deviceServerHostname
Oczywiście wiem, że nie ma już miejsca. Dlatego próbuję usunąć rzeczy! ( sed
Polecenie, którego używam, zmniejszy plik linii 4000+ do około 90 linii.)
Moje sed
polecenie jest słusznesed -i '/myregex/d' /path/to/file/filename
Czy istnieje sposób na zastosowanie tego polecenia pomimo pełnego dysku?
(Musi być zautomatyzowany, ponieważ muszę go zastosować na kilkuset serwerach jako szybką poprawkę).
(Oczywiście błąd aplikacji musi zostać zdiagnozowany, ale w międzyczasie serwery nie działają poprawnie ....)
Aktualizacja: Sytuacja, z którą się spotkałem, została rozwiązana poprzez usunięcie czegoś innego, co, jak się okazało, mogę usunąć, ale nadal chciałbym uzyskać odpowiedź na to pytanie, które byłoby pomocne w przyszłości i dla innych osób.
/tmp
jest zakazem; jest w tym samym systemie plików.
Przed zwolnieniem miejsca na dysku przetestowałem i okazało się, że mogę usunąć wiersze vi
, otwierając plik i uruchamiając, :g/myregex/d
a następnie z powodzeniem zapisując zmiany za pomocą :wq
. Wydaje się, że powinno być możliwe zautomatyzowanie tego, bez uciekania się do osobnego systemu plików do przechowywania pliku tymczasowego .... (?)
źródło
sed -i
tworzy tymczasową kopię do działania. Podejrzewam, że taked
byłoby lepiej, choć nie jestem wystarczająco znajomy, aby zakazać rzeczywistego rozwiązaniaed
uruchomionym:printf %s\\n g/myregex/d w q | ed -s infile
pamiętaj jednak, że niektóre implementacje używają również plików tymczasowych tak jaksed
(możesz spróbować busyboksa ed - afaik nie tworzy pliku tymczasowego)echo
. użyćprintf
. ised
dodaj znak, który upuścisz w ostatnim wierszu, aby uniknąć utraty spacji końcowych. Ponadto twoja powłoka musi być w stanie obsłużyć cały plik w jednym wierszu poleceń. to twoje ryzyko - najpierw przetestuj.bash
jest w tym szczególnie niedobry (myślę, że ma to do czynienia z miejscem na stosie?) i może cię w każdej chwili zranić. te dwased
zalecane przynajmniej wykorzystałyby bufor potoku jądra, aby uzyskać dobry efekt między nimi, ale metoda jest dość podobna. twoje polecenie podrzędne również obetnie,file
czy sed w / in zakończy się powodzeniem.sed '/regex/!H;$!d;x' <file|{ read v && cat >file;}
i jeśli działa, przeczytaj resztę mojej odpowiedzi.Odpowiedzi:
Ta
-i
opcja nie zastępuje oryginalnego pliku. Tworzy nowy plik z danymi wyjściowymi, a następnie zmienia nazwę na oryginalną nazwę pliku. Ponieważ nie masz miejsca w systemie plików dla tego nowego pliku, nie działa.Musisz to zrobić sam w skrypcie, ale utwórz nowy plik w innym systemie plików.
Ponadto, jeśli tylko usuwasz wiersze pasujące do wyrażenia regularnego, możesz użyć
grep
zamiastsed
.Zasadniczo programy rzadko używają tego samego pliku jako danych wejściowych i wyjściowych - gdy tylko zacznie zapisywać do pliku, część programu, która czyta z pliku, nie będzie już widzieć oryginalnej zawartości. Więc albo najpierw musi skopiować gdzieś oryginalny plik, albo napisz do nowego pliku i zmień jego nazwę po zakończeniu.
Jeśli nie chcesz używać pliku tymczasowego, możesz spróbować buforować zawartość pliku w pamięci:
źródło
rsync -a --no-owner --no-group --remove-source-files "$backupfile" "$destination"
od tutajsed -i
zachowuje to?sed -i
nie zachowuje żadnej z tych rzeczy. Właśnie wypróbowałem go z plikiem, którego nie posiadam, ale znajduje się w katalogu, który posiadam, i pozwala mi to wymienić plik. Zastąpienie jest własnością mnie, a nie pierwotnego właściciela.var=$(< FILE); echo "$FILE" | grep '^"' > FILE
v=$(<file)&& printf %s\\n "$v" >file
jednak nawet nie używać&&
. Pytający mówi o uruchomieniu go w skrypcie - zautomatyzowaniu nadpisywania pliku jego częścią. powinieneś przynajmniej sprawdzić, czy możesz pomyślnie otworzyć dane wejściowe i wyjściowe. Powłoka może również eksplodować.Tak to
sed
działa. W przypadku użycia z-i
edycjąsed
lokalną tworzy plik tymczasowy z nową zawartością przetwarzanego pliku. Po zakończeniused
zastępuje bieżący plik roboczy tymczasowym. Narzędzie nie edytuje pliku na miejscu . To jest dokładnie zachowanie każdego edytora.To tak, jakbyś wykonał następujące zadanie w powłoce:
W tym momencie
sed
próbuje opróżnić buforowane dane do pliku wymienionego w komunikacie o błędzie za pomocąfflush()
wywołania systemowego:W przypadku twojego problemu widzę rozwiązanie polegające na zamontowaniu oddzielnego systemu plików (na przykład
tmpfs
, jeśli masz wystarczającą ilość pamięci lub zewnętrznego urządzenia pamięci masowej) i przeniesienie tam niektórych plików, przetworzenie ich tam i przeniesienie ich z powrotem.źródło
Od czasu opublikowania tego pytania nauczyłem się, że
ex
jest to program zgodny z POSIX. Jest prawie uniwersalnie dowiązany dovim
, ale tak czy inaczej, poniżej (według mnie) kluczowym zagadnieniemex
związanym z systemami plików (wziętymi ze specyfikacji POSIX):„... wpływa na każdy plik ...” Wierzę, że umieszczenie czegoś w systemie plików (w ogóle nawet pliku tymczasowego) liczyłoby się jako „wpływające na dowolny plik”. Może?*
Dokładne przestudiowanie specyfikacji POSIX w celu
ex
wskazania niektórych „gotchas” na temat zamierzonego przenośnego zastosowania w porównaniu do typowych skryptowych zastosowańex
znalezionych online (które są wypełnionevim
poleceniami -specyficznymi).+cmd
jest opcjonalne zgodnie z POSIX.-c
opcji jest również opcjonalne.:g
„zjada” wszystko aż do następnej nowej linii bez znaków ucieczki (i dlatego uruchamia ją po każdym znalezionym dopasowaniu wyrażenia regularnego, a nie raz na końcu).-c 'g/regex/d | x'
Usuwa więc tylko jedną instancję, a następnie zamyka plik.Tak więc, zgodnie z tym, co zbadałem, zgodna z POSIX metoda edycji na miejscu pliku w pełnym systemie plików w celu usunięcia wszystkich wierszy pasujących do określonego wyrażenia regularnego to:
Powinno to działać, pod warunkiem, że masz wystarczającą ilość pamięci, aby załadować plik do bufora.
* Jeśli znajdziesz coś, co wskazuje inaczej, proszę o tym wspomnieć w komentarzach.
źródło
ex +g/match/d -scx file
jest również zgodny z POSIX?vi
działało na pełnym systemie plików, uważam, że w większości przypadków on również działałbyex
- choć może nie w przypadku gigantycznego pliku.sed -i
nie działa na pełnym systemie plików bez względu na rozmiar pliku.Użyj fajki, Luke!
Przeczytaj plik | filtr | odpisać
w tym przypadku
sed
nie tworzy nowego pliku, a jedynie wysyła potok wyjściowy, dodd
którego otwiera ten sam plik . Oczywiście można użyćgrep
w szczególnym przypadkunastępnie skróć pozostałe.
źródło
sed
zawsze używa plików tymczasowych?grep
tak czy inaczejsponge
dowodzenia. Tak,sed
ze-i
zawsze tworzy pliki lilke „seduyUdmw” z 000 praw.Jak zauważono w innych odpowiedziach,
sed -i
działa poprzez skopiowanie pliku do nowego pliku w tym samym katalogu , dokonanie zmian w procesie, a następnie przeniesienie nowego pliku na oryginał. Dlatego to nie działa.ed
(oryginalny edytor wierszy) działa w nieco podobny sposób, ale ostatnio sprawdzałem, że używa/tmp
pliku scratch. Jeśli masz/tmp
inny system plików niż ten, który jest pełny,ed
może wykonać to za Ciebie.Spróbuj tego (w interaktywnym monitie powłoki):
P
(Co jest kapitał P) nie jest to bezwzględnie konieczne. Włącza monit; bez tego pracujesz w ciemności, a niektórym ludziom to przeszkadza.w
Iq
są w rytu q UIT.Jeśli twój
/tmp
katalog znajduje się w systemie plików, który jest pełny (lub jeśli jego system plików jest również pełny), spróbuj gdzieś znaleźć miejsce. chaos wspominał o montażu tmpfs lub zewnętrznego urządzenia pamięci masowej (np. dysku flash); ale jeśli masz wiele systemów plików i nie wszystkie są pełne, możesz po prostu użyć jednego z pozostałych. chaos sugeruje skopiowanie pliku (ów) do innego systemu plików, edycję ich tam (za pomocąsed
), a następnie skopiowanie ich z powrotem. W tym momencie może to być najprostsze rozwiązanie. Ale alternatywą byłoby utworzenie katalogu do zapisu w systemie plików, który ma trochę wolnego miejsca, ustawienie zmiennej środowiskowej tak,TMPDIR
aby wskazywało ten katalog, a następnie uruchomienieed
. (Ujawnienie: Nie jestem pewien, czy to zadziała, ale nie zaszkodzi.)Gdy zaczniesz
ed
działać, możesz to zautomatyzować, wykonując następujące czynnościw skrypcie. Lub , jak sugeruje don_crissti.
printf '%s\n' 'g/myregex/d' w q | ed -s filename
źródło
ed
lub zex
), aby pamięć była używana zamiast osobnego systemu plików? Właśnie o to mi chodziło (i dlatego nie przyjąłem odpowiedzi).ed
wiele lat temu. Wciąż istniały takie rzeczy, jak komputery 16-bitowe, na których procesy były ograniczone do przestrzeni adresowej o wielkości 64 KB (!), Więc pomysł edytora odczytującego cały plik do pamięci był niemożliwy do uruchomienia. Od tego czasu pamięć oczywiście się powiększyła - ale także dyski i pliki. Ponieważ dyski są tak duże, ludzie nie odczuwają potrzeby radzenia sobie z ewentualnością/tmp
braku miejsca. Właśnie rzuciłem okiem na kod źródłowy najnowszej wersjied
i nadal wydaje się… (ciąg dalszy)ed
(lubex
lubvi
) oferuje opcję zachowania bufora w pamięci. Z drugiej strony, Edycja tekstu za pomocą ed i vi - Rozdział 11: Przetwarzanie tekstu - Część II: Poznawanie systemu Red Hat Linux - Sekrety Red Hat Linux 9 Professional - Systemy Linux mówią, żeed
bufor edycji znajduje się w pamięci… (ciąg dalszy )vi
(co jest tym samym programem coex
). Uważam, że używają po prostu niechlujnych, nieprecyzyjnych sformułowań - ale jeśli jest to w Internecie (lub w formie drukowanej), to musi być prawda, prawda? Płacisz pieniądze i podejmujesz decyzję.Można dość łatwo obciąć plik, jeśli można uzyskać liczbę bajtów do przesunięcia, a linie pojawiają się od punktu początkowego do końca.
Albo jeśli masz
${TMPDIR:-/tmp}
inny system plików, być może:Ponieważ (większość) powłok umieszcza swoje dokumenty tutaj w usuniętym pliku tymczasowym. Jest całkowicie bezpieczny, o ile
<<FILE
deskryptor jest utrzymywany od początku do końca i${TMPDIR:-/tmp}
ma tyle miejsca, ile potrzebujesz.Powłoki, które nie używają plików tymczasowych, używają potoków, więc korzystanie z nich w ten sposób nie jest bezpieczne. Te pociski są zazwyczaj
ash
pochodne, takie jakbusybox
,dash
BSDsh
-zsh
,bash
,ksh
, a Bourne shell, jednak wszystkie pliki tymczasowe stosowanie.najwyraźniej napisałem mały program powłoki w lipcu zeszłego roku, aby zrobić coś bardzo podobnego do tego
Jeśli
/tmp
nie jest to wykonalne, to tak długo, jak można zmieścić plik w pamięci coś w rodzaju ...... jako ogólny przypadek zapewniłby przynajmniej pełne buforowanie pliku przez pierwszy
sed
proces przed próbą obcięcia pliku wejścia / wyjścia.Bardziej ukierunkowanym - i wydajnym - rozwiązaniem może być:
... bo to i tak nie przeszkadza w buforowaniu linii, które chciałeś usunąć.
Test przypadku ogólnego:
źródło
/tmp
które znajdują się w tym samym systemie plików. Podoba mi się twoja podwójnased
wersja. Myślę, że kombinacja Barmara i twojej odpowiedzi byłaby prawdopodobnie najlepsza, coś w stylu:myvar="$(sed '/myregex/d' < file)" && [ -n "$myvar" ] && echo "$myvar" > file ; unset myvar
(W tym przypadku nie dbam o zachowanie końcowych linii).sed
|cat
powyższa rzecz nigdy nie otwiera danych wyjściowych, chybased
że buforował już cały plik i jest gotowy do zapisania wszystkich danych wyjściowych. Jeśli spróbuje buforować plik i nie powiedzie się -read
nie powiedzie się, ponieważ odnajdzie EOF na|
potoku przed odczytaniem pierwszej nowej linii, a więccat >out
nigdy nie zdarzy się, dopóki nie wypisze go całkowicie z pamięci. przelew lub coś podobnego po prostu zawiedzie. również cały rurociąg zwraca sukces lub porażkę za każdym razem. przechowywanie go w var jest po prostu bardziej ryzykowne.file=$(sed '/regex/!H;$!d;x' <file | read v && tee file) && cmp - file <<<"$file" || shite
więc plik wyjściowy i var byłyby zapisywane jednocześnie, co tworzyłoby jedną lub jedną skuteczną kopię zapasową, co jest jedynym powodem, dla którego chcesz komplikuj rzeczy bardziej niż potrzebujesz.read script
iread v
twojej odpowiedzi. Jeśli będziecie mogli rozwinąć więcej na ten temat, będę bardzo wdzięczny, dzięki!$script
tosed
skrypt, którego użyłbyś do kierowania na dowolną część pliku, którą chciałeś; jest to skrypt, który zapewnia wynik końcowy, który chcesz w strumieniu.v
jest tylko symbolem zastępczym pustej linii. wbash
powłoce nie jest to konieczne, ponieważ zamiast niejbash
automatycznie użyje$REPLY
zmiennej powłoki, ale POSIXly zawsze powinieneś to robić. Nawiasem mówiąc, cieszę się, że okaże się przydatny. powodzenia z tym. im mikeserv @ gmail, jeśli potrzebujesz czegoś dogłębnie. powinienem mieć komputer za kilka dniTa odpowiedź zapożycza pomysły z tej drugiej odpowiedzi i tej innej odpowiedzi, ale opiera się na nich, tworząc odpowiedź, która ma bardziej ogólne zastosowanie:
W pierwszym wierszu uruchamiana jest
sed
komenda z wyjściem zapisanym na standardowe wyjście (a nie do pliku); konkretnie do potoku,wc
aby policzyć znaki. W drugim wierszu uruchamiana jest równieżsed
komenda z wyjściem zapisanym na standardowe wyjście, które w tym przypadku jest przekierowywane do pliku wejściowego w trybie nadpisywania odczytu / zapisu (bez obcinania), o którym tutaj mowa . Jest to dość niebezpieczna rzecz; jest bezpieczny tylko wtedy, gdy polecenie filtrowania nigdy nie zwiększa ilości danych (tekstu); tzn. dla każdego n odczytanych bajtów zapisuje n lub mniej bajtów. Dotyczy to oczywiściesed '/myregex/d'
polecenia; dla każdej linii, którą czyta, zapisuje dokładnie tę samą linię lub nic. (Inne przykłady:s/foo/fu/
lubs/foo/bar/
będzie bezpieczna, ales/fu/foo/
is/foo/foobar/
nie będzie).Na przykład:
ponieważ te 32 bajty danych:
został zastąpiony tymi 25 znakami:
pozostawiając siedem bajtów
night.\n
pozostałych na końcu.Na koniec
dd
polecenie szuka końca nowych, oczyszczonych danych (bajt 25 w tym przykładzie) i usuwa resztę pliku; tzn. w tym momencie obcina plik.Jeśli z jakiegoś powodu
1<>
sztuczka nie działa, możesz to zrobićPamiętaj też, że dopóki wszystko, co robisz, to usuwanie linii, wszystko czego potrzebujesz to
grep -v myregex
(jak zauważył Barmar ).źródło
sed -i 'd' / path / to / file / filename
źródło