substring split - Come verificare se una stringa contiene una sottostringa in Bash




comparazione maggiore (18)

[[ $string == *foo* ]] && echo "It's there" || echo "Couldn't find"

Ho una stringa in Bash:

string="My string"

Come posso testare se contiene un'altra stringa?

if [ $string ?? 'foo' ]; then
  echo "It's there!"
fi

Dove ?? è il mio operatore sconosciuto. Uso echo e grep ?

if echo "$string" | grep 'foo'; then
  echo "It's there!"
fi

Sembra un po 'goffo.


Quindi ci sono molte soluzioni utili alla domanda - ma quale è la più veloce / usa la minima risorsa?

Test ripetuti usando questo frame:

/usr/bin/time bash -c 'a=two;b=onetwothree; x=100000; while [ $x -gt 0 ]; do TEST ; x=$(($x-1)); done'

Sostituire TEST ogni volta:

[[ $b =~ $a ]]           2.92user 0.06system 0:02.99elapsed 99%CPU

[ "${b/$a//}" = "$b" ]   3.16user 0.07system 0:03.25elapsed 99%CPU

[[ $b == *$a* ]]         1.85user 0.04system 0:01.90elapsed 99%CPU

case $b in *$a):;;esac   1.80user 0.02system 0:01.83elapsed 99%CPU

doContain $a $b          4.27user 0.11system 0:04.41elapsed 99%CPU

(doContain era nella risposta di F. Houri)

E per le risate:

echo $b|grep -q $a       12.68user 30.86system 3:42.40elapsed 19%CPU !ouch!

Quindi, la semplice opzione di sostituzioni vince in modo prevedibile sia in un test esteso che in un caso. Il caso è portatile.

Piping out to 100000 greps è prevedibilmente doloroso! La vecchia regola sull'utilizzo di utilità esterne senza necessità è vera.


grep -q è utile per questo scopo.

Lo stesso con awk :

string="unix-bash 2389"
character="@"
printf '%s' "$string" | awk -vc="$character" '{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }'

Produzione:

Non trovato

string="unix-bash 2389"
character="-"
printf '%s' "$string" | awk -vc="$character" '{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }'

Produzione:

Trovato

Fonte originale: http://unstableme.blogspot.com/2008/06/bash-search-letter-in-string-awk.html


Dovresti ricordare che lo scripting della shell è meno di una lingua e più di una collezione di comandi. Istintivamente pensi che questo "linguaggio" richieda di seguire un if con un [ o un [[ . Entrambi sono solo comandi che restituiscono uno stato di uscita che indica il successo o il fallimento (proprio come ogni altro comando). Per questo motivo userei grep , e non il [ comando.

Basta fare:

if grep -q foo <<<"$string"; then
    echo "It's there"
fi

Ora che stai pensando if testare lo stato di uscita del comando che lo segue (completo di punto e virgola). Perché non riconsiderare la fonte della stringa che stai testando?

## Instead of this
filetype="$(file -b "$1")"
if grep -q "tar archive" <<<"$filetype"; then
#...

## Simply do this
if file -b "$1" | grep -q "tar archive"; then
#...

L'opzione -q fa in modo che grep non produca nulla, poiché vogliamo solo il codice di ritorno. <<< fa sì che la shell espanda la parola successiva e la usa come input per il comando, una versione a linea singola del documento << qui (non sono sicuro che sia standard o un bashismo).


Risposta compatibile

Poiché ci sono già molte risposte usando le funzionalità specifiche di Bash, c'è un modo di lavorare con shell con funzionalità più povere, come busybox :

[ -z "${string##*$reqsubstr*}" ]

In pratica, ciò potrebbe dare:

string='echo "My string"'
for reqsubstr in 'o "M' 'alt' 'str';do
  if [ -z "${string##*$reqsubstr*}" ] ;then
      echo "String '$string' contain substring: '$reqsubstr'."
    else
      echo "String '$string' don't contain substring: '$reqsubstr'."
    fi
  done

Questo è stato testato sotto bash , dash , ksh e ash (busybox), e il risultato è sempre:

String 'echo "My string"' contain substring: 'o "M'.
String 'echo "My string"' don't contain substring: 'alt'.
String 'echo "My string"' contain substring: 'str'.

In una funzione

Come richiesto da @EeroAtonton, ecco una versione della stessa demo, testata con le stesse shell:

myfunc() {
    reqsubstr="$1"
    shift
    string="[email protected]"
    if [ -z "${string##*$reqsubstr*}" ] ;then
        echo "String '$string' contain substring: '$reqsubstr'.";
      else
        echo "String '$string' don't contain substring: '$reqsubstr'." 
    fi
}

Poi:

$ myfunc 'o "M' 'echo "My String"'
String 'echo "My String"' contain substring 'o "M'.

$ myfunc 'alt' 'echo "My String"'
String 'echo "My String"' don't contain substring 'alt'.

Avviso: devi sfuggire o raddoppiare le virgolette e / o doppie virgolette:

$ myfunc 'o "M' echo "My String"
String 'echo My String' don't contain substring: 'o "M'.

$ myfunc 'o "M' echo \"My String\"
String 'echo "My String"' contain substring: 'o "M'.

Funzione semplice

Questo è stato testato sotto busybox , dash e, naturalmente, bash :

stringContain() { [ -z "${2##*$1*}" ]; }

È tutto gente!

Quindi ora:

$ if stringContain 'o "M3' 'echo "My String"';then echo yes;else echo no;fi
no
$ if stringContain 'o "M' 'echo "My String"';then echo yes;else echo no;fi
yes

... O se la stringa inviata potrebbe essere vuota, come indicato da @Sjlver, la funzione diventerebbe:

stringContain() { [ -z "${2##*$1*}" ] && [ -z "$1" -o -n "$2" ]; }

o come suggerito dal commento di Adrian Günter , evitando -o switche:

stringContain() { [ -z "${2##*$1*}" ] && { [ -z "$1" ] || [ -n "$2" ] ;} ; }

Con stringhe vuote:

$ if stringContain '' ''; then echo yes; else echo no; fi
yes
$ if stringContain 'o "M' ''; then echo yes; else echo no; fi
no

Cosa ne pensi di questo:

text="   <tag>bmnmn</tag>  "
if [[ "$text" =~ "<tag>" ]]; then
   echo "matched"
else
   echo "not matched"
fi

Non sono sicuro di utilizzare un'istruzione if, ma è possibile ottenere un effetto simile con un'istruzione case:

case "$string" in 
  *foo*)
    # Do stuff
    ;;
esac

My .bash_profile e come ho usato grep se il PATH includeva le mie 2 cartelle bin, non aggiungerle

# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

U=~/.local.bin:~/bin

if ! echo "$PATH" | grep -q "home"; then
    export PATH=$PATH:${U}   
fi

La risposta accettata è la migliore, ma poiché c'è più di un modo per farlo, ecco un'altra soluzione:

if [ "$string" != "${string/foo/}" ]; then
    echo "It's there!"
fi

${var/search/replace} è $var con la prima istanza di search sostituita da replace , se viene trovata (non cambia $var ). Se provi a sostituire foo con niente, e la stringa è cambiata, allora ovviamente è stato trovato foo .


Mi piace sed

substr="foo"
nonsub="$(echo "$string" | sed "s/$substr//")"
hassub=0 ; [ "$string" != "$nonsub" ] && hassub=1

Modifica, logica:

  • Usa sed per rimuovere l'istanza della sottostringa dalla stringa

  • Se la nuova stringa è diversa dalla vecchia stringa, esiste una sottostringa


Puoi utilizzare la risposta di Marcus (* caratteri jolly) anche all'esterno di un'istruzione case, se usi parentesi doppie:

string='My long string'
if [[ $string == *"My long"* ]]; then
  echo "It's there!"
fi

Si noti che gli spazi nella stringa dell'ago devono essere posizionati tra virgolette doppie e i caratteri jolly * devono essere all'esterno.


Uno è:

[ $(expr $mystring : ".*${search}.*") -ne 0 ] && echo 'yes' ||  echo 'no'

Questo funziona anche:

if printf -- '%s' "$haystack" | egrep -q -- "$needle"
then
  printf "Found needle in haystack"
fi

E il test negativo è:

if ! printf -- '%s' "$haystack" | egrep -q -- "$needle"
then
  echo "Did not find needle in haystack"
fi

Suppongo che questo stile sia un po 'più classico, meno dipendente dalle caratteristiche della shell di Bash.

L'argomento -- è pura paranoia POSIX, usata per proteggere da stringhe di input simili alle opzioni, come --abc o -a .

Nota: in un ciclo ristretto questo codice sarà molto più lento rispetto all'utilizzo delle funzioni di shell Bash interne, in quanto uno o due processi separati verranno creati e collegati tramite pipe.


Prova Oobash è una libreria di stringhe in stile OO per bash 4. Ha il supporto per le dieresi tedesche. È scritto in bash. Sono disponibili molte funzioni: -base64Decode , -base64Encode , -center , -charAt , -concat , -count , -endsWith , -equals , -equalsIgnoreCase , -reverse , -hashCode , -indexOf , -isAlnum , -isAlnum , -isAlnum , -isAlpha , -isAscii , -isDigit , -isEmpty , -isAlpha , -isAscii , -isDigit , -isEmpty , -isHexDigit , -isLowerCase , -isSpace , -isPrintable , -isUpperCase , -isVisible , -lastIndexOf , -startsWith , -substring , -swapCase , -toLowerCase , -toString , -toUpperCase , -trim e -zfill .

Guarda l'esempio contiene:

[Desktop]$ String a testXccc                                                  
[Desktop]$ a.contains tX                   
true                                                           
[Desktop]$ a.contains XtX      
false      

oobash è disponibile su Sourceforge.net .


Questa risposta di overflow dello stack era l'unica a intrappolare i caratteri di spazio e di trattino:

# For null cmd arguments checking   
to_check=' -t'
space_n_dash_chars=' -'
[[ $to_check == *"$space_n_dash_chars"* ]] && echo found

Ho trovato che ho bisogno di questa funzionalità abbastanza frequentemente, quindi sto usando una funzione di shell fatta in casa nel mio .bashrc come questo che mi permette di riutilizzarla tutte le volte che mi serve, con un nome facile da ricordare:

function stringinstring()
{
    case "$2" in 
       *"$1"*)
          return 0
       ;;
    esac   
    return 1
}

Per verificare se $string1 (ad esempio, abc ) è contenuto in $string2 (ad esempio, 123abcABC ), devo solo eseguire stringinstring "$string1" "$string2" e verificare il valore restituito, ad esempio

stringinstring "$str1" "$str2"  &&  echo YES  ||  echo NO

Come Paolo ha menzionato nel suo confronto delle prestazioni:

if echo "abcdefg" | grep -q "bcdef"; then
    echo "String contains is true."
else
    echo "String contains is not true."
fi

Questo è conforme a POSIX come il "caso" $ stringa "nella risposta fornita da Marcus, ma è leggermente più facile da leggere rispetto alla risposta dell'istanza. Si noti inoltre che questo sarà molto più lento rispetto all'utilizzo di una dichiarazione di un caso, come ha sottolineato Paul, non usarlo in un ciclo.


Questo dovrebbe farlo:

DIR=$(dirname "$(readlink -f "$0")")

Funziona con symlink e spazi nel percorso. Vedi le pagine man per dirname e readlink .

Modificare:

Dalla traccia commenti sembra non funzionare con Mac OS. Non ho idea del perché sia ​​così. Eventuali suggerimenti?







string bash substring