ANDed condicional usando regexp e variáveis

3

Eu quero testar se uma linha, lida de um arquivo, tem um começo específico e uma final contendo uma palavra contida em uma variável. Aqui está um código:

O arquivo de entrada é:

line one
#; line two
#; line three blah
line four

Um script mínimo, que falha, é:

declare ENDOFLINE= "blah"
exec 3< "inputfile"

while read LINE <&3
do
    if [[ ("$LINE" =~ "^#;") && (( ("$LINE" =~ "${ENDOFLINE%$}") )) ]]; 
    then
    echo score!
    else echo no score
    fi
done

Mas, se eu fizer isso:

if [[ ("$LINE" =~ "^#;") && (( ("$LINE" =~ "blah$") )) ]];

consegue identificar a linha correta (= > #; line three blah). Em outras palavras, eu preciso de uma condição de teste composto em que o primeiro teste é se o início da linha é '#;' e o final da linha é uma string contida na variável $ ENDOFLINE.

Obrigado por qualquer ajuda.

    
por bev 06.04.2011 / 22:22

2 respostas

6

Parece um erro simples: ${ENDOFLINE%$} retira um $ no final de $ENDOFLINE , mas o que você quer fazer é usar $ENDOFLINE literalmente e ter um $ para indicar o fim de a linha.

if [[ ("$LINE" =~ "^#;") && (( ("$LINE" =~ "${ENDOFLINE}$") )) ]];

Isso funciona em zsh, mas não em ksh ou bash. O Bash requer que o regexp $ seja não-referenciado (caso contrário, ele é interpretado literalmente), e o ksh não gosta de parênteses duplos dentro de [[ … ]] (ele os interpreta como uma instrução aritmética). Esta linha mais simples funciona em todos os três shells (note que [[…]] é um dos poucos lugares onde você não precisa de "…" em torno de expansões variáveis):

if [[ $LINE =~ ^"#;" && $LINE =~ $ENDOFLINE$ ]];

Se $ENDOFLINE nunca sobrepor com #; (por exemplo, se ENDOFLINE for ;foo , você aceitará #;;foo , mas rejeitar #;foo ), poderá reduzir isso para um único teste:

if [[ $LINE =~ ^"#;".*$ENDOFLINE$ ]];

Você também pode usar um padrão curinga em vez de um regexp aqui:

if [[ $LINE = "#;"*"$ENDOFLINE" ]];

A construção [[…]] não existe em todas as shells, é específica para bash, ksh e zsh. Em outras shells (Bourne, dash, qualquer coisa POSIX), você pode fazer combinações curinga com o case construct:

case $LINE in
  "#;"*"$ENDOFLINE") echo score;;
  *) echo no score;;
esac
    
por 06.04.2011 / 22:38
0

Você também pode fazer isso usando grep:

end=blah
while read line; do
    if grep -Eq "^#;.*$end$" <<< "$line"; then
        echo score!
    else
        echo no score
    fi
done

(Isso é no bash; a sintaxe pode ser ligeiramente diferente em outros shells, mas o princípio é o mesmo.)

    
por 06.04.2011 / 23:45