Come si fa a sapere se una stringa contiene un'altra stringa in POSIX sh?


136

Voglio scrivere uno script di shell Unix che farà varie logiche se c'è una stringa dentro un'altra stringa. Ad esempio, se mi trovo in una determinata cartella, diramare. Qualcuno potrebbe dirmi come realizzare questo? Se possibile, vorrei che questo non fosse specifico della shell (cioè non solo bash) ma se non c'è altro modo in cui posso accontentarmi.

#!/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
Mi rendo conto che questo è vecchio, ma qui ci sono alcune cose da notare per i futuri visitatori: (1) Di solito è buona norma riservare nomi di variabili SNAKE_CASE a variabili interne di ambiente e shell. (2) L'impostazione CURRENT_DIRè ridondante; puoi semplicemente usare $PWD.
nyuszika7h,

Risposte:


162

Ecco ancora un'altra soluzione. Questo utilizza l' espansione dei parametri della sottostringa POSIX , quindi funziona in Bash, Dash, KornShell (ksh), Z shell (zsh), ecc.

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

Una versione funzionalizzata con alcuni esempi:

# 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
Questo non funziona per me se la sottostringa contiene barre rovesciate. Come al solito, substring="$( printf '%q' "$2" )"salva la giornata.
Egor Tensin,

questo corrisponde anche a substrinsg errati. string = "aplha beta betaone" sottostringa = "beta". corrisponde sia al betaone che al dbeta, che penso sia sbagliato.
rajeev,

1
Che dire dell'utilizzo di caratteri jolly ? [[ $haystack == *"My needle"* ]]
Pablo A

4
Cosa c'è di sbagliato è che le doppie parentesi non sono POSIX, che era la premessa della domanda, @Pablo.
Rob Kennedy,

1
Questo non funziona con caratteri speciali come []. Vedi la mia risposta 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

Le shell estese come ksh o bash hanno meccanismi di abbinamento fantasiosi, ma il vecchio stile caseè sorprendentemente potente.


40

Purtroppo, non sono a conoscenza di un modo per farlo in sh. Tuttavia, usando bash (a partire dalla versione 3.0.0, che è probabilmente quello che hai), puoi usare l'operatore = ~ in questo modo:

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

Come bonus aggiuntivo (e / o un avvertimento, se le tue stringhe contengono caratteri divertenti), = ~ accetta le regex come l'operando giusto se tralasci le virgolette.


3
Non citare il regex, o non funzionerà in generale. Ad esempio, provare [[ test =~ "test.*" ]]contro [[ test =~ test.* ]].
10

1
Bene, funzionerà bene se stai testando una sottostringa, come nella domanda originale, ma non tratterà l'operando giusto come una regex. Aggiornerò la mia risposta per renderlo più chiaro.
John Hyland,

3
Questo è Bash, non POSIX sh come pone la domanda.
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
Passare grep -q "$2"a grep -q "$2" > /dev/nullper evitare output indesiderati.
Victor Sergienko,

15

Ecco un link a varie soluzioni del tuo problema.

Questo è il mio preferito in quanto ha il senso più leggibile dall'uomo:

Il metodo jolly stella

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

Su shho ricevuto "operando sconosciuto" con questo. Funziona con Bash però.
halfer

13
[[non è 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"

-> output: la stringa 1 contiene la stringa 2


2

Vedi la manpage per il programma 'test'. Se stai solo testando l'esistenza di una directory normalmente faresti qualcosa del genere:

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

Se stai effettivamente cercando di abbinare una stringa, puoi usare anche regole di espansione e caratteri jolly bash:

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

Se devi fare qualcosa di più complesso, dovrai usare un altro programma come expr.


1
Sembra che ci sia una doppia citazione spuria (") nel tuo secondo esempio.
Alexis Wilke,

2

In casi speciali in cui si desidera scoprire se una parola è contenuta in un testo lungo, è possibile scorrere il lungo testo con un ciclo.

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

Questa è pura conchiglia Bourne.


1

Se vuoi un metodo solo ksh che sia veloce come "test", puoi fare qualcosa del tipo:

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

Funziona eliminando l'ago nel pagliaio e quindi confrontando la lunghezza della stringa di pagliai vecchi e nuovi.


1
Non sarebbe possibile restituire il risultato di test? Come in return [ ${#haystack} -eq ${#1} ]?
Alexis Wilke,

Si, è corretto. Lascerò così come è più facile da capire per i laici. Se usi nel tuo codice, usa il metodo @AlexisWilke.
JoeOfTex,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.