14

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
  • 1
    Simply using sed '24,$s/,/:/g' filename where 24 is the starting line` Commented Jun 21, 2017 at 12:43

5 Answers 5

15

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.
5
  • Can replace wc -l < input with wc -l input. Should be faster by a few nanoseconds :)
    – gardenhead
    Commented Jun 21, 2017 at 17:09
  • 1
    except that wc -l input outputs the filename as well; we only want the line count
    – Jeff Schaller
    Commented Jun 21, 2017 at 17:38
  • The first advice with ed is fine and simple.
    – sku2003
    Commented Aug 22, 2017 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
    Commented Aug 22, 2017 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
    Commented Aug 22, 2017 at 19:48
8

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
6

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
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'

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .