Jak mogę edytować ostatnie n wierszy w pliku?

10

Czy istnieje polecenie, które pozwoli mi edytować ostatnie n wierszy w pliku? Mam kilka plików, z których wszystkie mają inną liczbę linii w środku. Ale chciałbym zmodyfikować ostatnie n wierszy w każdym pliku. Celem jest zastąpienie przecinków średnikami w ostatnich n wierszach. Ale tylko w ostatnich n wierszach.

Nie chcę usuwać żadnych wierszy, chcę po prostu zastąpić każdy przecinek średnikiem w ostatnich n wierszach każdego pliku.

Za pomocą polecenia sed jestem w stanie zastąpić ostatni wiersz tym poleceniem. Jak opisano tutaj: Jak mogę usunąć tekst z ostatniego wiersza pliku?

Ale to pozwala mi tylko modyfikować ostatnią linię, a nie ostatnią n liczbę linii.

sku2003
źródło
1
Po prostu użyj sed '24,$s/,/:/g' filename gdzie 24jest linia początkowa`
Valentin Bajrami

Odpowiedzi:

13

Aby zastąpić przecinki średnikami w ostatnich n wierszach ed:

n=3
ed -s input <<< '$-'$((n-1))$',$s/,/;/g\nwq'

Podział tego na części:

  • ed -s = uruchom ed po cichu (nie zgłaszaj bajtów zapisanych na końcu)
  • '$-'= od końca pliku ( $) minus ...
  • $((n-1)) = n-1 linii ...
  • ( $' ... '= cytuje resztę polecenia, aby chronić je przed powłoką)
  • ,$s/,/;/g= ... do końca pliku ( ,$), wyszukaj i zamień wszystkie przecinki na średniki.
  • \nwq = zakończ poprzednie polecenie, a następnie zapisz i wyjdź

Aby zastąpić przecinki średnikami w ostatnich n wierszach sed:

n=3
sed -i "$(( $(wc -l < input) - n + 1)),\$s/,/;/g" input

Rozbijając to na części:

  • -i = edytuj plik „w miejscu”
  • $(( ... )) = zrób trochę matematyki:
  • $( wc -l < input) = uzyskaj liczbę wierszy w pliku
  • -n + 1 = cofnij się o n-1 linii
  • ,\$ = od n-1 linii do końca pliku:
  • s/,/;/g = zamień przecinki na średniki.
Jeff Schaller
źródło
Mogą zastąpić wc -l < inputz wc -l input. Powinno być szybsze o kilka nanosekund :)
ogrodnik
1
z wyjątkiem tego, że wc -l inputwyświetla również nazwę pliku; chcemy tylko liczby linii
Jeff Schaller
Pierwsza rada z ed jest w porządku i prosta.
sku2003
W rzeczywistości otrzymałem pomoc z zupełnie innej strony. Spowodowało to powstanie tego dziwnego fragmentu kodu, który działa, ale jest długi i dziwny. Myślę, że to niektóre z dziwnych rzeczy, z którymi możesz skończyć, kiedy poskładać rozwiązanie. W każdym razie twój jest ładniejszy, krótszy i mniej skomplikowany niż mój: Kod tutaj również działał:
sku2003
cat input.file | sed 's /, /, \ n / g' | sed -n '1! G; h; $ p' | awk -vn = 2 'NR <= n {gsub (",", ";", 0 $)} {print}' | sed -n '1! G; h; $ p' | sed '/ ^ \ s * $ / d'> output.file
sku2003
6

Rozwiązanie za pomocą tac i sed, aby zastąpić każdy przecinek średnikiem w ostatnich 50 wierszach pliku.txt:

tac file.txt | sed '1,50s/,/;/g' | tac
Gohu
źródło
5

Z GNU headi powłoką podobną do Bourne'a:

n=20
{ head -n -"$n"; tr , ';'; } < file 1<> file

Zastępujemy sam plik. To jest OK tu na transliteracji bajt-do-bajta ale niekoniecznie jeśli modyfikacja implikuje zmianę rozmiaru pliku (w tym przypadku, to chciałby zamienić 1<> filez > other-file && mv other-file filena przykład).

Stéphane Chazelas
źródło
1
Oto dokumenty1<> , których nie znałem. Ponadto spongez moreutils jest dobrym narzędziem do rurociągów, w których chcesz zastąpić dane wejściowe.
Miles,
1

Załóżmy, że chcemy zastąpić ostatnie 7wiersze poniższej sekwencji skryptem powłoki i implementacją GNU sed:

$ seq 20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Krok 1 : pozwala uzyskać ostatni numer wiersza sekwencji, jak poniżej. Spójrz na to i tamto :

$ lastLine=`seq 20|sed -n '$='`

$ echo $lastLine 
20

Krok 2 : Ustawmy liczbę linii (na końcu sekwencji), które zamierzamy edytować:

$ numberOfLines=7

$ echo $numberOfLines 
7

Krok 3 : pozwala obliczyć linię początkową na podstawie poprzednich zmiennych, takich jak następujące. Spójrz na to :

$ startLine=`expr $lastLine - $numberOfLines + 1`

$ echo $startLine 
14

Krok 4 : Teraz możemy zamienić ostatnie 7 wierszy sekwencji na coś innego, na przykład: Spójrz na to :

$ seq 20|sed -e "$startLine,+$numberOfLines{s/[12]/WoW/}"
1
2
3
4
5
6
7
8
9
10
11
12
13
WoW4
WoW5
WoW6
WoW7
WoW8
WoW9
WoW0

Krok 4 wykorzystuje sekcja 4.4 strony podręcznika sed, która mówi:

'ADDR1,+N'
     Matches ADDR1 and the N lines following ADDR1.

Krok 4 również wykorzystuje podwójne cudzysłowy, jak wspomniano tutaj .


Cóż, 4 kroki są niepotrzebne, jeśli użyjemy odpowiedzi Gohu w następujący sposób:

$ seq 20 |tac|sed -e '1,7{s/[12]/WoW/}'|tac
1
2
3
4
5
6
7
8
9
10
11
12
13
WoW4
WoW5
WoW6
WoW7
WoW8
WoW9
WoW0
użytkownik3405291
źródło
0

Użyj taili potokuj, aby sed:

tail -n 20 file | sed 's/,/;/g'

Działa to na plik dla ostatnich 20 linii. Jeśli chcesz, aby twoje pliki bezpośrednio kierowały do ​​pliku, użyj:

tail -n 20 file | sed -i 's/,/;/g'

John Goofy
źródło