Not exactly a one-liner, but a bullet-proof method:
IFS=, read -d '' -ra ary <<< "$variable"
unset ary[${#ary[@]}-1]
will create an array ary, the fields of which are the fields in your variable variable (fields are comma-separated). Then, if you want to remove all the numbers:
for i in "${!ary[@]}"; do
[[ ${ary[i]} = +([[:digit:]]) ]] && unset ary[$i]
done
or to remove all the elements that start with a hash:
for i in "${!ary[@]}"; do
[[ ${ary[i]} = \#* ]] && unset ary[$i]
done
Finally, to put that back into your variable:
printf -v variable '%s,' "${ary[@]}"
A function that does that:
remove_number() {
local ary i
IFS=, read -d '' -ra ary <<< "${!1}"
unset ary[${#ary[@]}-1]
for i in "${!ary[@]}"; do
[[ ${ary[i]} = +([[:digit:]]) ]] && unset ary[$i]
done
printf -v "$1" '%s,' "${ary[@]}"
}
Demo (assuming this function is defined in session):
$ a1="aaa,bbb3,12,ccc,"
$ remove_number a1
$ echo "$a1"
aaa,bbb3,ccc,
This also works if there are newlines in variable (notice: it doesn't remove the negative number... that was not really clear from your requirements, but it's rather trivial to modify the function to also handle this, see is_number below):
$ a=$'aaa,\n\nnewline here\n,123456,-1234,abc\n,'
$ echo "$a"
aaa,
newline here
,123456,-1234,abc
,
$ remove_number a
$ echo "$a"
aaa,
newline here
,-1234,abc
,
Oh, and it works if the number appears in the first field (unlike some other answers):
$ a='1234,abc,'
$ b='1234,'
$ remove_number a
$ remove_number b
$ echo "a='$a'"; echo "b='$b'"
a='abc,'
b=','
Also fine for "empty" lists:
$ a=','
$ remove_number a
$ echo "$a"
,
A more functional approach: make a function that removes a field if a condition, given by a function, is met:
remove_cond() {
# $1 is condition
# $2 is name of variable that contains list
local ary i
IFS=, read -d '' -ra ary <<< "${!2}"
unset ary[${#ary[@]}-1]
for i in "${!ary[@]}"; do
"$1" "${ary[i]}" && unset ary[$i]
done
printf -v "$2" '%s,' "${ary[@]}"
}
Then a couple of conditions:
is_number() {
[[ $1 = ?(-)+([[:digit:]]) ]]
}
is_bang() {
[[ $1 = \#* ]]
}
is_bang_or_number() {
is_number "$1" || is_bang "$1"
}
With these set in current session:
$ a="aaa,bbb3,#zu_45,ccc,"
$ remove_cond is_bang a
$ echo "$a"
aaa,bbb3,ccc,
$ a='1234,#lol,keep me please,-1234,'
$ remove_cond is_bang_or_number a
$ echo "$a"
keep me please,