substrings e regexps

4

Eu tenho uma string contida em uma variável e quero extrair substrings com base na posição relativa a outra substring. Minha solução parece funcionar a menos que a string seja enviada para uma função como um arg. Eu estou usando o shell bash.

#!/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

observe que quando eu coloco echo $* na função getsubstr , ele imprime a mesma string que $var0 (- > '-a verifica um dois três'), e quando coloco echo $2 no getsubstr function imprime a mesma string que $var1 (- > 'check'). Então, parece-me que estou pedindo para imprimir a mesma substring em ambas as circunstâncias.

Um outro dilema é que se, em vez de echo ${*#*"${2}"} na função getsubstr eu usar echo ${*%"${2}"*} , eu obtiver exatamente o mesmo resultado.

Qualquer ajuda para entender esse comportamento seria muito apreciada.

BTW, percebo que ${*:3} dentro da função getsubstr funciona para retornar a subseqüência desejada, mas estou tentando entender o comportamento #*<regexp> e %<regextp>* .

    
por bev 07.04.2011 / 02:27

2 respostas

5

Seu getsubstr $var0 está passando 5 args para a função.
Além disso, $ * e $ @ testam cada argumento individual de $ 1 $ 2 etc .. contra a # patttern.

Em relação ao RegEx em bash : eu adicionei alguns exemplos no final, e btw, '*' é apenas um especial regex char quando é usado em um contexto regex, ie. ao usar = ~ . Em seu primeiro uso de * em ${* , o uso especial do asterisco é como o nome (psuedo) de uma var que se expande para uma concatenação de todas as vars: $ 1 $ 2 $ ... etc. ..
Seu segundo uso de um asterisco, em #*"${2}" , significa "$ 2" precedido por qualquer coisa, incluindo nada , será correspondido com cada $ 1 etc arg separado / individualmente.

O script a seguir pode ajudar com $ @ e $ * (por exemplo) ...

#!/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 em 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
por 07.04.2011 / 07:35
4

Atualizar com explicação

O motivo pelo qual você está vendo esse tipo de comportamento é porque $* ou mesmo $@ se expande para todos os parâmetros posicionais: $1 , $2 etc. Quando você tenta fazer um Expansão de Parâmetros (PE) em qualquer uma dessas duas variáveis especiais, você está aplicando o PE a cada parâmetro posicional e não uma única string.

Trecho de 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.

Essencialmente, o que você está fazendo é isto:

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

A função a seguir funciona configurando $* para um temp var $tmp porque agora você está aplicando o PE a uma variável normal uma vez.

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

P.S.

Não use function , pois não é POSIX e, na verdade, completamente desnecessário se você já estiver usando () após o nome da sua função.

P.P.S

Isso, na verdade, não tem nada a ver com expressões regulares , mas sim expressões globulares . Mais formalmente, estas são conhecidas como Expansões de Parâmetros

    
por 07.04.2011 / 02:32