Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have a string contained in a variable, and I want to extract substrings based on position relative to another substring. My solution seems to work unless the string is sent to a function as an arg. I am using the bash shell.

#!/usr/bin/bash

var0="-a check one two three"
var1="check"

function getsubstr() {
echo ${*#*"${2}"} 
}

# this prints 'one two three' which is what I want
echo ${var0#*"${var1}"}

# this prints '-a one two three', not what I want.
getsubstr $var0

note that when I put echo $* in the getsubstr function it prints the same string as $var0 (-> '-a check one two three'), and that when I put echo $2 in the getsubstr function is prints the same string as $var1 (-> 'check'). So, it seems to me that I'm asking to print the same substring in both circumstances.

A further conundrum is that if, instead of echo ${*#*"${2}"} in the getsubstr function I use echo ${*%"${2}"*} , I get the exact same result.

Any help in understanding this behavior would be greatly appreciated.

BTW, I realize that ${*:3} inside the getsubstr function works to return the substring I want, but I'm trying to understand the #*<regexp> and %<regextp>* behavior.

share|improve this question

2 Answers 2

up vote 2 down vote accepted

Your getsubstr $var0 is passing 5 args to the function
Also, $* and $@ tests each individual $1 $2 etc.. arg against the # patttern

Regarding RegEx in bash: I've added some examples at the end) .. and btw, '*' is only a special regex char when it is used in a regex context, ie. when using =~ ...
In your first use of * in ${*, the asterisk's special use is as the (psuedo)name of a var which expands to a concatenation of all the vars: $1 $2 $...etc...
Your second use of an asterisk,in #*"${2}", means "$2" preceded by anything indluding nothing, is to be matched against each passed $1 etc arg seperately/individually.

The following script may help with $@ and $* (by example)...

#!/bin/bash
#   
getsubstr() {
  echo -n " ${#@} args";
  [[ "$1$2$3$4$5$6" == *\ * ]] && echo " (with embedded spaces)" || echo " (no spaces)"
  echo '                  "${*}"          '\|"${*}"\|
  echo '                   ${*}           '\|${*}\|
  echo '                  "${@}"          '\|"${@}"\|
  echo '                   ${@}           '\|${@}\|
  echo '                  "${*#*"${2}}"   '\|"${*#*"${2}"}"\|
  echo '                   ${*#*"${2}}    '\|${*#*"${2}"}\|
  echo '                  "${@#*"${2}}"   '\|"${@#*"${2}"}"\|
  echo '                   ${@#*"${2}}    '\|${@#*"${2}"}\|
  echo '                        ${*#B}    '\|${*#B}\|
  echo '                       "${*#B}"   '\|"${*#B}"\|
  echo '                        ${@#B}    '\|${@#B}\|
  echo '                       "${@#B}"   '\|"${@#B}"\|
}
var0="a B c      "
echo
echo -n "Passing "; getsubstr "$var0" ; echo
echo -n "Passing "; getsubstr  $var0  ; echo
echo -n "Passing "; getsubstr "$var0" "$var0" ; echo
echo -n "Passing "; getsubstr  $var0   $var0  ; echo
echo
exit 
###################################################################

RegEx in bash

# Regex checks: "=~" uses extended regular expression
#+  Parenthesized subexpressions within the regular expression are saved
#+  in the array variable BASH_REMATCH
#+  $BASH_REMATCH / ${BASH_REMATCH[0]} is the string matching the entire regular expression. 
#+  ${BASH_REMATCH[n]} is the sub string matching the nth parenthesized subexpression

  [[ "abcdef" =~ (.)(.)(.) ]] && echo "# $BASH_REMATCH"
# abc

  [[ "abcdef" =~ (.)(.)(.) ]] && echo "# ${BASH_REMATCH[0]}"
# abc

  [[ "abcdef" =~ (.)(.)(.) ]] && echo "# ${BASH_REMATCH[2]}"
# b

  [[ "abcdef" =~ (.)(.)(.) ]] && echo "# ${BASH_REMATCH[@]}"
# abc a b c

share|improve this answer
    
bear - Thanks so much. Your answer is informative and (to me) complete. I especially appreciate your examples. –  bev Apr 9 '11 at 3:34

Update with explanation

The reason you are seeing this type of behavior is because $* or even $@ expands to all the positional parameters: $1, $2 etc. When you attempt to do a Parameter Expansion (PE) on either one of those two special vars, you are applying the PE to each positional parameter and not a single string.

Excerpt from man bash

${parameter#word}
Remove matching prefix pattern. The word is expanded to produce a pattern just as in pathname expansion. If the pattern matches the beginning of the 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.

Essentially what you are doing is this:

getsubstr() { 
  tmp=$2
  for arg; do 
    printf "%s " ${1#*$tmp}
    shift
  done
}

The following function works by setting $* to a temp var $tmp because you are now applying the PE to a normal variable once.

getsubstr() {
  tmp=$*
  echo ${tmp#*$2}
}

P.S.

Don't use function as it's not POSIX and actually completely unnecessary if you are already using () after your function name.

P.P.S

This actually has nothing to do with regular expressions but rather globular expressions. More formally these are known as Parameter Expansions

share|improve this answer
    
thanks for your answer, and the extra info. I've looked around for more on the difference between regular expressions and globular expressions and have come up blank. Can you expand a little on what distinguished each? Thanks. –  bev Apr 9 '11 at 3:13
    
@bev look at the term 'wildcard', it's the same as globular expressions. –  SiegeX Apr 9 '11 at 8:37
    
This site (dartmouth.edu/~rc/classes/ksh/print_pages.shtml) says: "Wildcards are not full regular expressions. Sed, grep, awk etc. work with more flexible (and more complex) string matching operators." which seems to imply that wildcards are a subset of the full set of regexps, i.e. are regular expressions, but limited in the number of them as well as the commands that use them. Would you agree with that? –  bev Apr 11 '11 at 2:17
    
I'm not being difficult. I just want to understand whether wildcards and regexps are different in kind or just the same kind of thing, but in the context of bash, used in different circumstances. For example, in the source code for bash, are wildcards implemented using regexps? –  bev Apr 11 '11 at 2:18
    
@bev: Although a functionally equivalent regular expression can be made to match that of a globular expression, the reverse is not true. Globs are not a regular language and hence not a regular expression. –  SiegeX Apr 11 '11 at 4:52

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.