How to create numeronyms in bash

Although I find the use of awk with an empty field separator somewhat 'innovative', the simplest solution in my opinion is just a small expansion of yours:

awk -F "" '{print $1 (NF-2) $NF}'

This works only with words of three or more letters, of course. To handle the general case:

awk -F "" '{if (NF>2) print $1 (NF-2) $NF; else print $0}'

As an explanation:

  • By setting the field separator to "empty" with -F "", the input is split into fields after every character, i.e. every character of the input is considered an individual "field" that is accessible via $n in awk expressions (with n being the field number ranging from 1 to NF). Btw, the GNU Awk User's Guide explicitly provides such use cases as examples, so I stand corrected on my previous concerns about using an empty FS. Still, note that the manual says "This is a common extension; it is not specified by the POSIX standard".
  • if the number of fields (i.e. characters, here) is larger than two, print the first field/character ($1), the evaluated expression (NF-2) which amounts to the number of characters in between the first and the last, and the last field/character ($NF). Note that the print call as used here does not produce space between the individual output tokens; this only happens when separating the arguments with commas instead of space (see e.g. the GNU Awk User's Guide).
  • else simply print the entire input expression, which is accessible via $0

Note that if we fould feed a two-character input, e.g. at, to the first code example, we would get unwanted (but formally correct) output like a0t (because there are, in this case, zero characters between first and last).

Note also, and this is important, that if you supply a string containing leading or trailing whitespace to this awk call, like in echo " hello" | awk <etc.>, then that leading/trailing whitespace would be treated as the first/last character, thus giving unwanted behaviour!


In ksh93, bash or zsh:

numeronym() {
  (( ${#1} > 2 )) || return
  printf '%s%d%s\n' "${1:0:1}" "$(( ${#1} - 2 ))" "${1: -1:1}"
}

This works on the first (only) parameter by printing the first letter, (number of characters minus 2), and last letter.


Another awk solution:

awk '{l=length($1); print substr($1,1,1) l-2 substr($1,l,1)}'
  • l=length($1) - Set the l variable to the length of your string (Assuming the input string is in the first column and does not contain whitespace)
  • substr($1,1,1) - (column #, starting point, ending point) so print column 1, starting at position 1, and print 1 character.
  • l-2 - Length of string minus 2
  • substr($1,l,1) - Print column 1 starting at position l (length of string) and print 1 character.

Tags:

Bash

Awk

Cut