10

Is there a command that will allow me to edit the last n lines in a file? I have several files, that all have a different number of lines inside. But I would like to modify the last n lines in each file. The goal is to replace commas with semicolons in the last n lines. But only in the very last n lines.

I do not want to delete any lines, I just want to replace every comma with a semicolon in the last n lines in each file.

Using the sed command I am able to replace the very last line with this command. As described here: How can I remove text on the last line of a file?

But this only enables me to modify the very last line, and not the last n number of lines.

  • 1
    Simply using sed '24,$s/,/:/g' filename where 24 is the starting line` – Valentin Bajrami Jun 21 '17 at 12:43
13

To replace commas with semicolons on the last n lines with ed:

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

Splitting that apart:

  • ed -s = run ed silently (don't report the bytes written at the end)
  • '$-' = from the end of the file ($) minus ...
  • $((n-1)) = n-1 lines ...
  • ( $' ... ' = quote the rest of the command to protect it from the shell )
  • ,$s/,/;/g = ... until the end of the file (,$), search and replace all commas with semicolons.
  • \nwq = end the previous command, then save and quit

To replace commas with semicolons on the last n lines with sed:

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

Breaking that apart:

  • -i = edit the file "in-place"
  • $(( ... )) = do some math:
  • $( wc -l < input) = get the number of lines in the file
  • -n + 1 = go backwards n-1 lines
  • ,\$ = from n-1 lines until the end of the file:
  • s/,/;/g = replace the commas with semicolons.
  • Can replace wc -l < input with wc -l input. Should be faster by a few nanoseconds :) – gardenhead Jun 21 '17 at 17:09
  • 1
    except that wc -l input outputs the filename as well; we only want the line count – Jeff Schaller Jun 21 '17 at 17:38
  • The first advice with ed is fine and simple. – sku2003 Aug 22 '17 at 19:45
  • I actually ended up getting help from a completely different side. This resulted in this odd piece of code, that does the trick but is long and strange. I guess this is some of weird things you can end up with when to piece together a solution. Anyways yours is prettier , shorter and less complicated than my own: The code here also worked: – sku2003 Aug 22 '17 at 19:47
  • cat input.file | sed 's/,/,\n/g' | sed -n '1!G;h;$p' | awk -v n=2 'NR<=n{gsub(",",";",$0)} {print}' | sed -n '1!G;h;$p' | sed '/^\s*$/d' > output.file – sku2003 Aug 22 '17 at 19:48
6

Solution using tac and sed to replace every comma with a semicolon in the last 50 lines of file.txt:

tac file.txt | sed '1,50s/,/;/g' | tac
5

With GNU head and a Bourne-like shell:

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

We're overwriting the file over itself. That's OK here for a byte-to-byte transliteration but wouldn't necessarily if the modification implies changing the size of the file (in which case, you'd want to replace 1<> file with > other-file && mv other-file file for instance).

1

Lets assume we want to replace the last 7 lines of the following sequence with shell script and GNU implementation of sed:

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

Step 1: lets get the last line number of sequence like the following. Take a look at this and that:

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

$ echo $lastLine 
20

Step 2: lets set the number of lines (at the end of sequence) we intend to edit:

$ numberOfLines=7

$ echo $numberOfLines 
7

Step 3: lets calculate the start line based on previous variables, like the following. Take a look at this:

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

$ echo $startLine 
14

Step 4: Now, we can replace the last 7 lines of sequence with something else, like the following. Take a look at this:

$ 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

Step 4 is using section 4.4 of sed man page which says:

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

Step 4, also is using double-quotes as mentioned here.


Well, the 4 steps are unnecessary if we use the answer of Gohu like this:

$ 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
0

Use tail and pipe to sed:

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

This works on the file for the last 20 lines. If you want to have thee canges direct to the file use:

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for?Browse other questions tagged or ask your own question.