23

To simplify the discussion, let N = 3.

My current approach to extracting the last three characters of every line in a file or stream is to use sed to capture the last three characters in a group and replace the entire line with that group.

sed 's/^.*\(.\{3\}\)/\1/'

It works but it seems excessively verbose, especially when we compare to a method for getting the first three characters in a line.

cut -c -3

Is there a cleaner way to extract the last N characters in every line?

merlin2011
  • 71,677
  • 44
  • 195
  • 329

4 Answers4

46

It's very simple with grep -o '...$':

cat /etc/passwd  | grep -o '...$'
ash
/sh
/sh
/sh
ync
/sh
/sh
/sh

Or better yer:

N=3; grep -o ".\{$N\}$" </etc/passwd
ash
/sh
/sh
/sh
ync
/sh
/sh

That way you can adjust your N for whatever value you like.

Tiago Lopo
  • 7,619
  • 1
  • 30
  • 51
  • 3
    This answer works in the general case as well with `egrep -o '.{3}$'`. I'll accept it when the timer is up. :) – merlin2011 Jun 26 '14 at 09:42
  • 4
    @merlin2011 This will discard lines that have less than 3 characters. If this is the behavior you want, fine, otherwise, you might want to consider `.{1,3}$` instead. – gniourf_gniourf Jun 26 '14 at 10:29
  • FYI, ` egrep -o '.{3}$` expects the same character 3 times in a row, so it doesn't generalize to this case – Jezzamon Apr 19 '18 at 22:57
  • 1
    @Jezzamon no it doesn't, it expects any 3 characters. `egrep` is deprecated in favor of `grep -E` btw. – Ed Morton Nov 05 '20 at 23:51
19

Why emphasize brevity when it's a tiny command either way? Generality is much more important:

$ cat file
123456789
abcdefghijklmn

To print 3 characters starting from the 4th character:

$ awk '{print substr($0,4,3)}' file
456
def

To print 3 characters starting from the 4th-last character:

$ awk '{print substr($0,length($0)-3,3)}' file
678
klm

To print 3 characters from [around] the middle of each line:

$ awk '{print substr($0,(length($0)-3)/2,3)}' file
345
efg
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • How would one save it to a variable? – Yesh Oct 11 '17 at 03:53
  • Are you asking 1) how to save the output of the awk command to a shell variable or 2) how to save the substring to an awk variable or 3) something else? – Ed Morton Oct 11 '17 at 13:27
  • I was talking about 1. I've found it though... ```f_id="$(echo $filename | awk '{print substr($0,length($0)-5,2)}')"``` – Yesh Oct 17 '17 at 04:46
  • Just a note: `length($0)` is equivalent to just `length` – kvantour Mar 10 '20 at 12:24
  • 1
    Yeah, I know, I just find it clearer to add the 4 extra chars. I could live with `length()` but then you're only saving 2 chars over stating explicitly what you're getting the length of, `$0`, so it doesn't seem a worthwhile tradeoff and I hate `length` as it looks like a variable and variations of that are a pretty common name for a variable (i.e. we frequently have to save the length of some string or array in a variable). – Ed Morton Mar 10 '20 at 13:41
18
rev /path/file | cut -c -3 | rev
Cyrus
  • 84,225
  • 14
  • 89
  • 153
2

Pure bash solution:

$ while read -r in; do echo "${in: -3}"; done
hello
llo
$

sed

$ sed 's,.*\(.\{3\}\)$,\1,'
hallo
llo
$
lx42.de
  • 418
  • 2
  • 4