Is there a parameter substitution/expansion alternative for "| cut -f1,2,3 -d:" a.k.a. trim after and including n-th character occurence?

With a regex:

$ var=a:b:c:d:e:f:g:h:i:j:k:l
$ [[ $var =~ ([^:]*:){6}([^:]*) ]] && echo "${BASH_REMATCH[0]}"
a:b:c:d:e:f:g

This should work in standard shell:

#!/bin/sh
var=a:b:c:d:e:f:g:h:i:j:k:l
while true; do 
    case "$var" in
        *:*:*:*:*:*:*:*) var=${var%:*} ;;
        *) break ;;
    esac
done
echo "$var"

Or if we do allow setting IFS for the duration of read:

$ IFS=: read -a arr <<< "$var"
$ arr=("${arr[@]:0:7}")
$ echo "${arr[@]}"
a b c d e f g
$ printf "%s:%s:%s:%s:%s:%s:%s\n" "${arr[0]}" "${arr[1]}" "${arr[2]}" "${arr[3]}" "${arr[4]}" "${arr[5]}"  "${arr[6]}" 
a:b:c:d:e:f:g

This uses only parameter expansion:

${var%:"${var#*:*:*:*:*:*:*:}"}

Example:

$ var=client-ip:server-ip:gw-ip:netmask:hostname:device:autoconf:morefields:another:youwantanother:haveanother:
$ echo "${var%:"${var#*:*:*:*:*:*:*:}"}"
client-ip:server-ip:gw-ip:netmask:hostname:device:autoconf

Thanks ilkkachu for coming up with a fix to the trailing :!


${parameter#word}
${parameter##word}

The word is expanded to produce a pattern just as in filename expansion (see Filename Expansion). If the pattern matches the beginning of the expanded value of parameter, then the result of the expansion is the expanded value of parameter with the shortest matching pattern (the ‘#’ case) or the longest matching pattern (the ‘##’ case) deleted. If parameter is ‘@’ or ‘’, the pattern removal operation is applied to each positional parameter in turn, and the expansion is the resultant list. If parameter is an array variable subscripted with ‘@’ or ‘’, the pattern removal operation is applied to each member of the array in turn, and the expansion is the resultant list.

This will attempt to match the beginning of your parameter, and if it does it will strip it.

Example:

$ var=a:b:c:d:e:f:g:h:i
$ echo "${var#a}"
:b:c:d:e:f:g:h:i
$ echo "${var#a:b:}"
c:d:e:f:g:h:i
$ echo "${var#*:*:}"
c:d:e:f:g:h:i
$ echo "${var##*:}"    # Two hashes make it greedy
i

${parameter%word}
${parameter%%word}

The word is expanded to produce a pattern just as in filename expansion. If the pattern matches a trailing portion of the expanded value of parameter, then the result of the expansion is the value of parameter with the shortest matching pattern (the ‘%’ case) or the longest matching pattern (the ‘%%’ case) deleted. If parameter is ‘@’ or ‘’, the pattern removal operation is applied to each positional parameter in turn, and the expansion is the resultant list. If parameter is an array variable subscripted with ‘@’ or ‘’, the pattern removal operation is applied to each member of the array in turn, and the expansion is the resultant list.

This will attempt to match the end of your parameter, and if it does it will strip it.

Example:

$ var=a:b:c:d:e:f:g:h:i
$ echo "${var%i}"
a:b:c:d:e:f:g:h:
$ echo "${var%:h:i}"
a:b:c:d:e:f:g
$ echo "${var%:*:*}"
a:b:c:d:e:f:g
$ echo "${var%%:*}"    # Two %s make it greedy
a

So in the answer:

${var%:"${var#*:*:*:*:*:*:*:}"}

(note the quotes around ${var#...} so that it is treated as a literal string (not a pattern) to be stripped off the end of $var).

When applied to:

var=client-ip:server-ip:gw-ip:netmask:hostname:device:autoconf:morefields:another:youwantanother:haveanother:

${var#*:*:*:*:*:*:*:} = morefields:another:youwantanother:haveanother:

That is expanded inside ${var%: ... } like so:

${var%:morefields:another:youwantanother:haveanother:}

So you are saying give me:

client-ip:server-ip:gw-ip:netmask:hostname:device:autoconf:morefields:another:youwantanother:haveanother:

But trim :morefields:another:youwantanother:haveanother: off the end.

The Bash Reference Manual (3.5.3)