Remove leading and trailing numbers from string, while leaving 2 numbers, using sed or awk

Viewed 256

I have a file containing lines like:

353451word2423157
anotherword
7412yetanother1
3262andherese123anotherline4359013
5342512354325324523andherese123anotherline45913
532453andherese123anotherline413

I'd like to strip most of the leading and tailing numbers (0-9), while still leaving 2 leading and trailing numbers in place, if any...

To clarify, for the list above, the expected output would be:

51word24
anotherword
12yetanother1
62andherese123anotherline43
23andherese123anotherline45
53andherese123anotherline41

Preferred tools would be sed or awk, but any other suggestions are welcome...

I've tried something like sed 's/[0-9]\+$//' | sed 's/^[0-9]\+//', but obviously this strips all leading and trailing numbers...

5 Answers

You may try this sed:

sed -E 's/^[0-9]+([0-9]{2})|([0-9]{2})[0-9]+$/\1\2/g' file

51word24
anotherword
12yetanother1
62andherese123anotherline43
23andherese123anotherline45
53andherese123anotherline41

Command Details:

  • ^[0-9]+([0-9]{2}): Match 1+ digits at start if that is followed by 2 digits (captured in a group) and replace with 2 digits in group #1.
  • ([0-9]{2})[0-9]+$: Match 1+ digits at the end if that is preceded by 2 digits (captured in a group) and replace with 2 digits in group #2.

I suggest using perl:

perl -pe 's/^\d+(?=\d{2})|(\d{2})\d+$/$1/' file

See the online demo and the regex demo.

Regex details:

  • ^ - start of string
  • \d+ - one or more digits
  • (?=\d{2}) - on the right, there must be two digits (not added to the match as the lookahead is a non-consuming pattern)
  • | - or
  • (\d{2}) - two digits captured into Group 1 ($1)
  • \d+ - one or more digits
  • $ - end of string.

Here is an awk that trims digits to a max of 2 on each side of a string:

awk '{  match($0, /^[0-9]*/); lh=RLENGTH
        s=substr($0, lh>2 ? lh-1 : 1)
        match(s, /[0-9]*$/); rh=RLENGTH
        print substr(s, 1, rh>2 ? length(s)-rh+2 : length(s))
}' file

Prints:

51word24
anotherword
12yetanother1
62andherese123anotherline43
23andherese123anotherline45
53andherese123anotherline41

Using GNU awk gensub() function with parentheses in the regexp to mark the components and then specifying them in the replacement (here "\\2\\3")

awk '{print gensub(/^([[:digit:]]*)([[:digit:]]{2})|([[:digit:]]{2})([[:digit:]]*)$/,"\\2\\3","g",$0)}' file
51word24
anotherword
12yetanother1
62andherese123anotherline43
23andherese123anotherline45
53andherese123anotherline41

I would use GNU AWK following way, let file.txt content be

353451word2423157
anotherword
7412yetanother1
3262andherese123anotherline4359013
5342512354325324523andherese123anotherline45913
532453andherese123anotherline413

then

awk 'BEGIN{FPAT="[0-9]+|[^0-9]+";OFS=""}$1~/[0-9]+/{$1=substr($1,length($1)-1)}$NF~/[0-9]+/{$NF=substr($NF,1,2)}{print}' file.txt

output

51word24
anotherword
12yetanother1
62andherese123anotherline43
23andherese123anotherline45
53andherese123anotherline41

Explanation: I instruct GNU AWK to split into fields which consist solely of digits or solely of non-digits using FPAT. If 1st column ($1) consist of digits, I slice it to get 2 last characters. If last column ($NF) consist solely of digits, I slice it to get 2 first characters. Finally whole line is printed using empty string as output field seperator (OFS).

(tested in gawk 4.2.1)

Related