0

I want to construct a glob pattern that matches two spaces or more followed by some text on a line of text for pattern replacement.

For instance, consider the following string

str="Here is a list of functions  Marionette Recutils"

I want to replace the last part consisting of two or more spaces followed by some text. That in a glob pattern matching " Marionette Recutils".

I want a pattern that matches two or more spaces followed by some text. Have come up with the following pattern

ptrn="[[:blank:]]+([[:blank:]])+([[:graph:]])"

Can the pattern for parameter substitution match a substring, allowing both leading and trailing characters to exist?

2
  • If you need to merge your account with any or all of your previous accounts, then please use the Contact link at the bottom of the page to request this. This would be preferable to create new accounts every few months.
    – Kusalananda
    Commented Mar 21, 2023 at 7:06
  • Do you want to use [:blank:] because you may have other blank characters besides spaces (e.g. tabs) delimiting your string?
    – Kusalananda
    Commented Mar 21, 2023 at 7:18

4 Answers 4

2

For two or more of something, with glob patterns, you can use:

  • {2,}(something) in ksh93
  • (something)(#c2,) in zsh -o extendedglob.
  • something(something)## in zsh -o extendedglob.
  • something+(something) in ksh or bash -O extglob (or zsh --emulate ksh).
  • somethingsomething*(something) in those as well.

So in bash:

shopt -s extglob
str="Here is a list of functions  Marionette Recutils"
newstr=${str/ +( )*/REPLACEMENT}

Or

newstr=${str/[[:blank:]]+([[:blank:]])*/REPLACEMENT}

In POSIX sh, you could also do:

case str in
  (*[[:blank:]][[:blank:]]*) newstr=${str%%[[:blank:]][[:blank:]]*}REPLACEMENT;;
  (*) newstr=$str;;
esac

The ${str%%pattern} operator (initially from ksh) removes the longest string that matches the pattern at the end of $str.

2
  • 1
    of course instead of ${str/ +( )*/REPLACEMENT} one could just use ${str/ */REPLACEMENT} (space-space-asterisk) since the * there matches any spaces too
    – ilkkachu
    Commented Mar 21, 2023 at 14:29
  • @ilkkachu, yes, like in the POSIX sh approach. My point was to show how to do 2-or-more, but you're making a very good one, worth mentioning. Commented Mar 21, 2023 at 14:54
1

I'm fairly certain this will not handle several edge cases, but I was successful with this...

# echo "Here is a list of functions  Marionette Recutils" | sed "s/  .*//"

Here is a list of functions
3
  • Would you know what is wrong with ptrn="([[:blank:]]{2,}).*" ?
    – Vera
    Commented Mar 21, 2023 at 1:24
  • I have been doing parameter replacement as vl=${vl//${ptrn}/}. Which fails with ptrn=" .*", but works with ptrn=" *".
    – Vera
    Commented Mar 21, 2023 at 1:41
  • 1
    @Randy You are trying to use a regular expression where the shell expects a globbing pattern. If you want to clarify your question, then please do so by editing your question.
    – Kusalananda
    Commented Mar 21, 2023 at 6:08
0

Brief: In Pure Bash: out=${str/* /}

Long:

str="Here is a list of functions  Marionette Recutils"
echo "${str/*  /}"   # <-- This way
# Out: Marionette Recutils

From man bash / Parameter Expansion

${parameter/pattern/string}

Pattern  substitution.   The pattern is expanded to produce a
pattern just as in pathname expansion, Parameter is  expanded
and  the  longest  match  of pattern against its value is re‐
placed with string.
3
  • Not good because in matches "Here is a list of functions ", rather than the opposite match, namely " Marionette Recutils.
    – Vera
    Commented Mar 21, 2023 at 3:11
  • Besides, I am working on using the character class [:blank:].
    – Vera
    Commented Mar 21, 2023 at 3:12
  • @Randy There is no need to use [:blank:] here as you have said that the delimiter consists of two spaces. The [:blank:] class matches spaces or tabs. If you have the last part of the string in a variable, you can use that to delete it from the original string with ${str%"$endstr"}. If you want to clarify the question, please do so by editing it.
    – Kusalananda
    Commented Mar 21, 2023 at 6:13
0

The confusing thing about character classes like [:blank:] is that they go inside of bracket expressions, so the whole thing has two concentric sets of brackets: [[:blank:]]. This also means you can combine them, like [[:blank:][:lower:]] (a single blank or lower-case letter) or [-_[:alpha:]] (a dash, underscore, or letter), or negate them like [^[:blank:]] (any character that isn't a blank).

So, you could use something like this:

ptrn="[[:blank:]][[:blank:]]*"
echo "${str%$ptrn}"

or just:

echo "${str%[[:blank:]][[:blank:]]*}"
2
  • It is not too confusing if one notes that the character class is [:class:]. For 0123456789 the character class would be [:digit:]. Thus the equivalent to [0123456789] is [[:digit:]].
    – Vera
    Commented Mar 21, 2023 at 13:19
  • @Randy, on some systems, [[:digit:]] matches more than 0123456789 like the decimal digits in other decimal numeric systems Commented Mar 21, 2023 at 18:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .