Jak usunąć puste linie, zaczynając od linii 5

10

Mam to:

sed -i '/^$/d' temp_spec.rb

który usuwa puste linie i działa dobrze. Jak mogę to zrobić tylko dla linii 5-999 (najlepiej 5 do końca pliku).

Próbowałem:

sed -n5,999 -i '/^$/d' temp_spec.rb
sed '5,999!d/^$/d' temp_spec.rb

ale żadne nie działało (bez błędów).

Michael Durrant
źródło
Dla pewności: czy chcesz zachować wiersze od 1 do 4, bez względu na to, czy są puste, czy nie, czy też chcesz usunąć wiersze od 1 do 4, bez względu na to, czy są puste, czy nie? (Do tej pory zakładałem, że to pierwsze, ale drugie polecenie, które wypróbowałeś, sprawia, że ​​zastanawiam się.)
Uwe

Odpowiedzi:

12

Jeśli chcesz usunąć wszystkie puste linie zaczynające się od linii 5 i zachować linie od 1 do 4, możesz użyć

sed -i '5,${;/^$/d;}' temp_spec.rb

{Jest operatorem grupowania, więc pierwsze polecenie 5,${czyli „od linii 5 do końca wejściowego ( $) wykonanie następujących poleceń do dopasowywania }”. Polecenia między {i }mogą być ponownie poprzedzone adresami, więc polecenie wewnętrzne /^$/doznacza „jeśli nic nie ma między początkiem ( ^) a końcem ( $) wiersza, usuń je”. Polecenia sed można rozdzielić ;. (Jest to źle udokumentowana funkcja sed. Jest obsługiwana przez większość implementacji sed, ale nie jest w pełni przenośna .) Jak zauważył Hauke, ;po {jest opcjonalne; poprzedni }jest jednak wymagany.

Jeśli chcesz usunąć wszystkie puste linie zaczynające się od linii 5, a także usunąć linie od 1 do 4, łatwiej:

sed -i '1,4d;/^$/d' temp_spec.rb
Uwe
źródło
Miły. Nie wiedziałem, że adresy można zagnieżdżać w ten sposób. Dla mnie to też działa bez przewodzenia ;.
Hauke ​​Laging
Tak, masz rację co do pierwszego ;.
Uwe
@Hauke ​​Laging Możesz rozdzielić polecenia w skrypcie sed przez ';' zamiast tego umieszczaj je w osobnych wierszach. Jest to funkcja „nieudokumentowana”.
Predrag Punosevac
4

Inna opcja za pomocą awk:

awk 'NR<5||/./'
Lri
źródło
2
sed '5,${s/^$//; t delete; b end; : delete; d; : end;}' temp_spec.rb

Edycja 1:

Mam to wyjaśnić, a zatem ...

Jest to niepotrzebnie skomplikowane. Nie wiedziałem, że zakresy adresów są dozwolone w obrębie {}. Musiałem więc inaczej wyrazić „usuń puste linie”. Podstawowym poleceniem tjest sed if ... then. Tbyłoby łatwiejsze, ale jest dostępne tylko dla GNU sed. Cytuję stronę podręcznika:

etykieta t: Jeśli as /// dokonał pomyślnego podstawienia od ostatniego odczytu linii wejściowej i od ostatniego polecenia t lub T, to należy przejść do etykiety; jeśli etykieta zostanie pominięta, przejdź do końca skryptu.

Nadużywam słynnego spolecenia. Nie zastępuje niczego, tylko sprawdza, czy linia jest pusta. Zastępuje więc pustą linię pustą linią (może wykorzystać wszystko jako zamiennik, ponieważ i tak linia jest usuwana).

Jeśli sdokonano „zamiany”, to linia jest pusta. W takim przypadku polecenie dzostanie wykonane. W przeciwnym razie nic nie można zrobić. Ponieważ tnie przeskakuje w przypadku sakcji, potrzebuję polecenia gałęzi, baby przejść do końca skryptu. : labelsą celami branżowymi. Jak gotowtedy w ciemnych czasach (kiedy wynaleziono sed ... te-hee).

Inną opcją byłoby s„zastąpienie” wszystkich niepustych linii, dzięki czemu sbardziej skomplikowana, ale reszta polecenia jest łatwiejsza:

sed '5,${s/^\(..*\)$/\1/; t end; d; : end;}' input

^..*$oznacza „niepustą linię” i \1„treść pierwszych nawiasów”.

Hauke ​​Laging
źródło
Trochę wyjaśnienia ze względu na OP (czuję, że ich mózg próbuje się czołgać przez uszy czytając to ...)
vonbrand