Como você sabe se uma string contém outra string no POSIX sh?


136

Eu quero escrever um script de shell Unix que faça várias lógicas se houver uma string dentro de outra string. Por exemplo, se eu estiver em uma determinada pasta, ramifique. Alguém poderia me dizer como fazer isso? Se possível, eu gostaria de tornar isso não específico do shell (ou seja, não apenas do bash), mas se não houver outra maneira de fazer isso.

#!/usr/bin/env sh

if [ "$PWD" contains "String1" ]
then
    echo "String1 present"
elif [ "$PWD" contains "String2" ]
then
    echo "String2 present"
else
    echo "Else"
fi

2
Sei que isso é antigo, mas aqui estão algumas coisas a serem observadas para futuros visitantes: (1) Geralmente, é uma boa prática reservar nomes de variáveis ​​SNAKE_CASE para variáveis ​​internas do ambiente e do shell. (2) A configuração CURRENT_DIRé redundante; você pode apenas usar $PWD.
nyuszika7h

Respostas:


162

Aqui está mais uma solução. Ele usa a expansão de parâmetro de substring POSIX , para que ele funcione no Bash, Dash, KornShell (ksh), shell Z (zsh), etc.

test "${string#*$word}" != "$string" && echo "$word found in $string"

Uma versão funcionalizada com alguns exemplos:

# contains(string, substring)
#
# Returns 0 if the specified string contains the specified substring,
# otherwise returns 1.
contains() {
    string="$1"
    substring="$2"
    if test "${string#*$substring}" != "$string"
    then
        return 0    # $substring is in $string
    else
        return 1    # $substring is not in $string
    fi
}

contains "abcd" "e" || echo "abcd does not contain e"
contains "abcd" "ab" && echo "abcd contains ab"
contains "abcd" "bc" && echo "abcd contains bc"
contains "abcd" "cd" && echo "abcd contains cd"
contains "abcd" "abcd" && echo "abcd contains abcd"
contains "" "" && echo "empty string contains empty string"
contains "a" "" && echo "a contains empty string"
contains "" "a" || echo "empty string does not contain a"
contains "abcd efgh" "cd ef" && echo "abcd efgh contains cd ef"
contains "abcd efgh" " " && echo "abcd efgh contains a space"

3
Isso não funciona para mim se a substring contém barras invertidas. Como de costume, substring="$( printf '%q' "$2" )"salva o dia.
Egor Tensin

isso também combina com substrinsg errado. string = "aplha beta betaone" substring = "beta". combina tanto betaone quanto dbeta, o que acho errado.
rajeev

1
Que tal usar curingas ? [[ $haystack == *"My needle"* ]]
Pablo #

4
O que há de errado é que colchetes duplos não são POSIX, que foi a premissa da pergunta, @Pablo.
Rob Kennedy

1
Isso não funciona com caracteres especiais como []. Veja minha resposta stackoverflow.com/a/54490453/712666 .
Alex Skrypnyk

85

Shell POSIX puro:

#!/bin/sh
CURRENT_DIR=`pwd`

case "$CURRENT_DIR" in
  *String1*) echo "String1 present" ;;
  *String2*) echo "String2 present" ;;
  *)         echo "else" ;;
esac

Os shells estendidos, como o ksh ou o bash, têm mecanismos de correspondência sofisticados, mas o estilo antigo caseé surpreendentemente poderoso.


40

Infelizmente, não conheço uma maneira de fazer isso no sh. No entanto, usando o bash (a partir da versão 3.0.0, que provavelmente é o que você possui), você pode usar o operador = ~ assim:

#!/bin/bash
CURRENT_DIR=`pwd`

if [[ "$CURRENT_DIR" =~ "String1" ]]
then
 echo "String1 present"
elif [[ "$CURRENT_DIR" =~ "String2" ]]
then
 echo "String2 present"
else
 echo "Else"
fi

Como um bônus adicional (e / ou um aviso, se suas strings tiverem caracteres engraçados), = ~ aceita expressões regulares como o operando certo se você deixar de fora as aspas.


3
Não cite a regex ou ela não funcionará em geral. Por exemplo, tente [[ test =~ "test.*" ]]vs. [[ test =~ test.* ]].
L0b0 11/01/12

1
Bem, funcionará bem se você estiver testando uma substring, como na pergunta original, mas não tratará o operando certo como um regex. Vou atualizar minha resposta para deixar isso mais claro.
John Hyland

3
Este é o Bash, não o POSIX sh, conforme a pergunta.
Reid

28
#!/usr/bin/env sh

# Searches a subset string in a string:
# 1st arg:reference string
# 2nd arg:subset string to be matched

if echo "$1" | grep -q "$2"
then
    echo "$2 is in $1"
else 
    echo "$2 is not in $1"
fi

2
Altere grep -q "$2"para grep -q "$2" > /dev/nullpara evitar saída indesejada.
Victor Sergienko

15

Aqui está um link para várias soluções do seu problema.

Este é o meu favorito, pois faz o sentido legível mais humano:

O método curinga da estrela

if [[ "$string" == *"$substring"* ]]; then
    return 1
fi
return 0

Em shrecebi "operando desconhecido" com isso. Funciona com o Bash.
precisa saber é o seguinte

13
[[não é POSIX
Ian #

14
case $(pwd) in
  *path) echo "ends with path";;
  path*) echo "starts with path";;
  *path*) echo "contains path";;
  *) echo "this is the default";;
esac


2
test $(echo "stringcontain" "ingcon" |awk '{ print index($1, $2) }') -gt 0 && echo "String 1 contain string 2"

-> saída: a sequência 1 contém a sequência 2


2

Veja a página de manual do programa 'test'. Se você está apenas testando a existência de um diretório, normalmente faria algo assim:

if test -d "String1"; then
  echo "String1 present"
end

Se você está realmente tentando corresponder a uma string, também pode usar regras de expansão bash e curingas:

if test -d "String*"; then
  echo "A directory starting with 'String' is present"
end

Se você precisar fazer algo mais complexo, precisará usar outro programa como o expr.


1
Parece haver uma aspa espúria ( ") em seu segundo exemplo.
Alexis Wilke

2

Em casos especiais em que você deseja descobrir se uma palavra está contida em um texto longo, é possível percorrer o texto longo com um loop.

found=F
query_word=this
long_string="many many words in this text"
for w in $long_string; do
    if [ "$w" = "$query_word" ]; then
          found=T
          break
    fi
done

Isso é pura casca de Bourne.


1

Se você quiser um método apenas do ksh que seja tão rápido quanto "test", poderá fazer algo como:

contains() # haystack needle
{
    haystack=${1/$2/}
    if [ ${#haystack} -ne ${#1} ] ; then
        return 1
    fi
    return 0
}

Ele funciona excluindo a agulha no palheiro e comparando o comprimento da corda dos antigos e novos.


1
Não seria possível retornar o resultado de test? Como em return [ ${#haystack} -eq ${#1} ]?
Alexis Wilke

Sim, está correto. Vou deixar assim, pois é mais fácil entender para leigos. Se você usar seu código, use o método @AlexisWilke.
21719 JoeOfTex
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.