14

I have a file as show below

1.2.3.4.ask
sanma.nam.sam
c.d.b.test

I want to remove the last field from each line, the delimiter is . and the number of fields are not constant.

Can anybody help me with an awk or sed to find out the solution. I can't use perl here.

Chris Seymour
  • 83,387
  • 30
  • 160
  • 202
user1745857
  • 153
  • 1
  • 1
  • 6

4 Answers4

26

Both these sed and awk solutions work independent of the number of fields.

Using sed:

$ sed -r 's/(.*)\..*/\1/' file
1.2.3.4
sanma.nam
c.d.b

Note: -r is the flag for extended regexp, it could be -E so check with man sed. If your version of sed doesn't have a flag for this then just escape the brackets:

sed 's/\(.*\)\..*/\1/' file
1.2.3.4
sanma.nam
c.d.b

The sed solution is doing a greedy match up to the last . and capturing everything before it, it replaces the whole line with only the matched part (n-1 fields). Use the -i option if you want the changes to be stored back to the files.

Using awk:

$ awk 'BEGIN{FS=OFS="."}{NF--; print}' file
1.2.3.4
sanma.nam
c.d.b

The awk solution just simply prints n-1 fields, to store the changes back to the file use redirection:

$ awk 'BEGIN{FS=OFS="."}{NF--; print}' file > tmp && mv tmp file
Chris Seymour
  • 83,387
  • 30
  • 160
  • 202
  • 1
    awesome Sudo_O , the awk solution worked . actually sed should have also worked but the -r option doesn't work with solaris sed. Solaris has pretty old sed and awk. anyways thanks for the really fast help – user1745857 Dec 13 '12 at 10:39
  • Yes I thought that just after I posted and updated my answer, see the edit on sed and extended regexp :) also please consider [accepting this answer](http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work) – Chris Seymour Dec 13 '12 at 10:54
  • +1 for the correct solution, and there's the sed-like GNU awk alternative `gawk '{print gensub(/(.*)\..*/,"\\1","")}'` just for completeness. – Ed Morton Dec 13 '12 at 11:09
  • +1 Great answer. Thanks for showing both `sed` and `awk` solutions. – Geoff Dec 13 '12 at 14:23
  • +1. If there are no empty lines in the input, the awk script can be reduced to `NF--`, e.g. `awk NF-- FS=. OFS=.`. – Thor Dec 13 '12 at 14:37
  • To also work with empty lines an extra check is needed `NF==0 || NF--`. – Thor Dec 13 '12 at 14:44
  • 2
    @thor got to love `awk` ;) – Chris Seymour Dec 13 '12 at 14:54
  • How to print the fields with `awk` but using comma as field separator? – Sigur Sep 14 '17 at 00:18
  • in Mac I could only make it work with `$NF=""`. `print` was not updated with `NF--` – aerijman Jan 29 '21 at 23:19
8

Reverse, cut, reverse back.

rev file | cut -d. -f2- | rev >newfile

Or, replace from last dot to end with nothing:

sed 's/\.[^.]*$//' file >newfile

The regex [^.] matches one character which is not dot (or newline). You need to exclude the dot because the repetition operator * is "greedy"; it will select the leftmost, longest possible match.

tripleee
  • 175,061
  • 34
  • 275
  • 318
2

With cut on the reversed string

cat youFile | rev |cut -d "." -f 2- | rev
Pilou
  • 1,398
  • 13
  • 24
  • @sudo I think your comment is unhelpful. Clearly your answer is better, just let it speak for itself. I'd rather we not discourage people from answering for fear of being mocked. Even if this answer's not the best, seeing different ways of answering can be helpful to future readers with similar, but different problems. – Geoff Dec 13 '12 at 14:21
  • @Geoff I take you point but I also think it's important that any pitfalls are pointed out with an answer both for the answerers benefit and any readers, probably should have been more explanatory. – Chris Seymour Dec 13 '12 at 14:27
1

If you want to keep the "." use below:

awk '{gsub(/[^\.]*$/,"");print}' your_file
Vijay
  • 65,327
  • 90
  • 227
  • 319