Come determinare se una stringa è una sottostringa di un'altra in bash?


49

Voglio vedere se una stringa è all'interno di una porzione di un'altra stringa.
per esempio:

'ab' in 'abc' -> true
'ab' in 'bcd' -> false

Come posso farlo in un condizionale di uno script bash?

Risposte:


27

Puoi usare il modulo in ${VAR/subs}cui VARcontiene la stringa più grande ed subsè la sottostringa che stai cercando di trovare:

my_string=abc
substring=ab
if [ "${my_string/$substring}" = "$my_string" ] ; then
  echo "${substring} is not in ${my_string}"
else
  echo "${substring} was found in ${my_string}"
fi

Questo funziona perché ${VAR/subs}è uguale $VARma con la prima occorrenza della stringa subsrimossa, in particolare se $VARnon contiene la parola subsnon verrà modificata.


Penso che dovresti cambiare la sequenza delle echoaffermazioni. Perché ottengoab is not in abc
Lucio

Hai ragione! : P
edwin,

Mmm .. No, la sceneggiatura è sbagliata. Mi piace ab was found in abc, ma se lo uso substring=zottengoz was found in abc
Lucio

1
Adesso capisco ab is not in abc. Ma z was found in abc. Questo è divertente: D
Lucio

1
Duh! Gli echi erano proprio all'inizio di questo! XD
edwin,

47

[[ "bcd" =~ "ab" ]]
[[ "abc" =~ "ab" ]]

le parentesi sono per il test, e siccome sono doppie parentesi, possono fare alcuni test extra =~.

Quindi potresti usare questo modulo in qualche modo

var1="ab"
var2="bcd"
if [[ "$var2" =~ "$var1" ]]; then
    echo "pass"
else
    echo "fail"
fi

Modifica: corretto "= ~", è stato capovolto.


1
Ottengo failcon questi parametri:var2="abcd"
Lucio

3
@Lucio Il corretto è [[ $string =~ $substring ]]. Ho aggiornato la risposta.
Eric Carvalho,

12

Uso dei modelli di nome file bash (noti anche come modelli "glob")

substr=ab
[[ abc == *"$substr"* ]] && echo yes || echo no    # yes
[[ bcd == *"$substr"* ]] && echo yes || echo no    # no

if [["$ JAVA_OPTS"! = "-XX: + UseCompressedOops" ]]; quindi esportare JAVA_OPTS = "$ JAVA_OPTS -XX: + UseCompressedOops"; fi
Mike Slinn,

10

I seguenti due approcci funzioneranno su qualsiasi ambiente compatibile con POSIX, non solo su bash:

substr=ab
for s in abc bcd; do
    if case ${s} in *"${substr}"*) true;; *) false;; esac; then
        printf %s\\n "'${s}' contains '${substr}'"
    else
        printf %s\\n "'${s}' does not contain '${substr}'"
    fi
done
substr=ab
for s in abc bcd; do
    if printf %s\\n "${s}" | grep -qF "${substr}"; then
        printf %s\\n "'${s}' contains '${substr}'"
    else
        printf %s\\n "'${s}' does not contain '${substr}'"
    fi
done

Entrambi i risultati sopra:

'abc' contains 'ab'
'bcd' does not contain 'ab'

Il primo ha il vantaggio di non generare un grepprocesso separato .

Nota che uso printf %s\\n "${foo}"invece di echo "${foo}"because echopotrebbe rovinarsi ${foo}se contiene barre rovesciate.


La prima versione funziona perfettamente per trovare la sottostringa del nome del xrandrmonitor nell'elenco dei nomi dei monitor memorizzati in una variabile. +1 e benvenuto nel club di ripetizione 1K :)
WinEunuuchs2Unix

6

istruzione case shell

Questa è la soluzione più portatile, funzionerà anche su vecchie shell Bourne e Korn shell

#!/bin/bash
case "abcd" in
    *$1*) echo "It's a substring" ;;
    *) echo "Not a substring" ;;
esac

Esecuzione di esempio:

$ ./case_substr.sh "ab"                                                                                           
It's a substring
$ ./case_substr.sh "whatever"                                                                                     
Not a substring

Si noti che non è necessario utilizzare in modo specifico echoè possibile utilizzare exit 1e exit 0per indicare il successo o il fallimento.

Ciò che potremmo anche fare è creare una funzione (che può essere utilizzata in script di grandi dimensioni se necessario) con valori di ritorno specifici (0 in corrispondenza, 1 in nessuna corrispondenza):

$ ./substring_function.sh                                  
ab is substring

$ cat substring_function.sh                                
#!/bin/sh

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

main(){
   if is_substring "ab" "abcdefg"
   then
       echo "ab is substring"
   fi
}

main $@

grep

$ grep -q 'ab' <<< "abcd" && echo "it's a substring" || echo "not a substring"                                    
it's a substring

Questo particolare approccio è utile con le istruzioni if-else in bash. Anche per lo più portatile

AWK

$ awk '$0~/ab/{print "it is a substring"}' <<< "abcd"                                                             
it is a substring

Pitone

$ python -c 'import sys;sys.stdout.write("it is a substring") if "ab" in sys.stdin.read() else exit(1)' <<< "abcd"
it is a substring

Rubino

$ ruby -e ' puts "is substring" if  ARGV[1].include? ARGV[0]'  "ab" "abcdef"                                             
is substring

+1 per andare al di là di tutti gli altri. Ho notato qui e su altri siti di scambio di stack nessuna risposta restituisce l'offset della sottostringa all'interno della stringa. Qual è la missione di questa sera :)
WinEunuuchs2Unix

@ WinEunuuchs2Unix Hai intenzione di farlo in bash?
Sergiy Kolodyazhnyy,

Sì e No. Sto realizzando un progetto Frankenstein in cui Python ottiene tutti i metadati dei messaggi di Gmail.com e Bash lo analizza e presenta un elenco di GUI con drill-down. Ho trovato la risposta anche se qui: stackoverflow.com/questions/5031764/...
WinEunuuchs2Unix

@ WinEunuuchs2Unix OK. Sembra interessante. Personalmente preferirei analizzare tutto in Python. Ha molte più capacità di elaborazione del testo rispetto a bash da solo.
Sergiy Kolodyazhnyy,

Conosco le tue preferenze da circa due anni e le rispetto. Ma sto solo imparando Python e farmi lavorare al suo interno mi sembra ingombrante. Per non parlare di tutte le elaborazioni di array con cui mi trovo già a mio agio in bash. Ma almeno ho scritto il mio primo script Python per aspirare tutto dal gmail.com di google nel file flat di Linux, giusto? :)
WinEunuuchs2Unix

5

Ricorda il [[e ":

[[ $a == z* ]]   # True if $a starts with an "z" (pattern matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).

[ $a == z* ]     # File globbing and word splitting take place.
[ "$a" == "z*" ] # True if $a is equal to z* (literal matching).

Quindi, come ha detto @glenn_jackman, ma tieni presente che se racchiudi l'intero secondo termine tra virgolette, il test passerà alla corrispondenza letterale .

Fonte: http://tldp.org/LDP/abs/html/comparison-ops.html


4

Simile alla risposta di Edwin, ma con una migliore portabilità per posix & ksh e un tocco meno rumoroso di quello di Richard:

substring=ab

string=abc
if [ "$string" != "${string%$substring*}" ]; then
    echo "$substring IS in $string"
else
    echo "$substring is NOT in $string"
fi

string=bcd
if [ "$string" != "${string%$substring*}" ]; then
    echo "$string contains $substring"
else
    echo "$string does NOT contain $substring"
fi

Produzione:

abc contains ab
bcd does NOT contain ab
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.