Hoe te controleren of een string een substring bevat in Bash

Ik heb een string in Bash:

string="My string"

Hoe kan ik testen of het een andere tekenreeks bevat?

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

Waar ??is mijn onbekende operator. Gebruik ik echoen grep?

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

Dat ziet er een beetje onhandig uit.


Antwoord 1, autoriteit 100%

Je kunt Marcus’s antwoord (*jokertekens)ook buiten een case-statement gebruiken als je dubbele haakjes gebruikt:

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

Houd er rekening mee dat spaties in de naaldtekenreeks tussen dubbele aanhalingstekens moeten worden geplaatst en dat de jokertekens *erbuiten moeten staan. Merk ook op dat er een eenvoudige vergelijkingsoperator wordt gebruikt (d.w.z. ==), niet de regex-operator =~.


Antwoord 2, autoriteit 18%

Als u de voorkeur geeft aan de regex-aanpak:

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

Antwoord 3, autoriteit 10%

Ik weet niet zeker of ik een if-statement moet gebruiken, maar je kunt een soortgelijk effect krijgen met een case-statement:

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

Antwoord 4, autoriteit 7%

stringContainvarianten (compatible of case zelfstandig)

Omdat deze stack overflow antwoorden vooral over Bash vertellen, ik heb gepost case onafhankelijke Bash functie op de bodem van dit bericht …

Hoe dan ook, daar is mijn

Compatible antwoord

Omdat er al veel antwoorden met behulp van Bash-specifieke kenmerken, is er een manier werkt onder armere-featured schelpen, zoals BusyBox :

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

In de praktijk zou dit kunnen geven:

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

Dit werd getest onder Bash, Dash kornshell (ksh) en ash (BusyBox), en het resultaat is altijd:

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 één functie

Zoals gesteld door @EeroAaltonen hier is een versie van dezelfde demo, getest onder dezelfde schalen:

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

Dan:

$ 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'.

Opmerking:u moet aanhalingstekens escapen of dubbel omsluiten en/of dubbele aanhalingstekens:

$ 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'.

Eenvoudige functie

Dit is getest onder BusyBox, Dash en natuurlijk Bash:

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

Dan nu:

$ 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

… Of als de ingediende string leeg zou kunnen zijn, zoals aangegeven door @Sjlver, zou de functie worden:

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

of zoals gesuggereerd door Adrian Günter’s opmerking, waarbij -o-switches worden vermeden:

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

Finale (eenvoudige) functie:

en het omkeren van de tests om ze potentieel sneller te maken:

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

Met lege snaren:

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

Case onafhankelijk (alleen bash!)

Om snaren zonder verzorging te testen, converteert u eenvoudig elke reeks naar kleine letters:

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

Controleer:

stringContain 'o "M3' 'echo "my string"' && echo yes || echo no
no
stringContain 'o "My' 'echo "my string"' && echo yes || echo no
yes
if stringContain '' ''; then echo yes; else echo no; fi
yes
if stringContain 'o "M' ''; then echo yes; else echo no; fi
no

5, Autoriteit 4%

Denk eraan dat shell-scripting minder is van een taal en meer van een verzameling commando’s. Instinctief denkt u dat deze “taal” vereist dat u een ifmet een [of een [[[. Beide zijn gewoon opdrachten die een exit-status retourneren die het succes of falen aangeeft (net als elke andere opdracht). Om die reden zou ik grepgebruiken en niet de opdracht [.

Doe gewoon:

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

Nu je denkt aan ifals het testen van de exit-status van het commando dat erop volgt (compleet met puntkomma), waarom heroverweeg je dan niet de bron van de string die je aan het testen bent?

p>

## 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
#...

De optie -qzorgt ervoor dat grep niets uitvoert, omdat we alleen de retourcode willen. <<<zorgt ervoor dat de shell het volgende woord uitbreidt en gebruikt als invoer voor de opdracht, een éénregelige versie van het <<hier document (Ik weet niet zeker of dit standaard is of een bashisme).


Antwoord 6, autoriteit 2%

Het geaccepteerde antwoord is het beste, maar aangezien er meer dan één manier is om het te doen, is hier een andere oplossing:

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

${var/search/replace}is $varmet de eerste instantie van searchvervangen door replace, als het wordt gevonden (het verandert $varniet). Als je foodoor niets probeert te vervangen, en de string is veranderd, dan is duidelijk foogevonden.


Antwoord 7, autoriteit 2%

Er zijn dus veel nuttige oplossingen voor de vraag – maar welke is het snelst / gebruikt de minste middelen?

Herhaalde tests met dit frame:

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

TEST elke keer vervangen:

[[ $b =~ $a ]]           2.92 user 0.06 system 0:02.99 elapsed 99% CPU
[ "${b/$a//}" = "$b" ]   3.16 user 0.07 system 0:03.25 elapsed 99% CPU
[[ $b == *$a* ]]         1.85 user 0.04 system 0:01.90 elapsed 99% CPU
case $b in *$a):;;esac   1.80 user 0.02 system 0:01.83 elapsed 99% CPU
doContain $a $b          4.27 user 0.11 system 0:04.41 elapsed 99%CPU

(doContain zat in het antwoord van F. Houri)

En om te giechelen:

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

Dus de eenvoudige vervangingsoptie wint voorspelbaar, of het nu gaat om een uitgebreide test of een zaak. De case is draagbaar.

Pijpen tot 100000 greps is voorspelbaar pijnlijk! De oude regel over het gebruik van externe hulpprogramma’s zonder noodzaak geldt.


Antwoord 8

Dit werkt ook:

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

En de negatieve test is:

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

Ik veronderstel dat deze stijl wat klassieker is — minder afhankelijk van de kenmerken van de Bash-shell.

Het argument --is pure POSIX-paranoia, gebruikt om te beschermen tegen invoerreeksen die vergelijkbaar zijn met opties, zoals --abcof -a.

OPMERKING: In een strakke lus is deze code veel langzamer dan het gebruik van interne bash shell-functies, omdat één (of twee) afzonderlijke processen via leidingen worden gemaakt en verbonden via leidingen.


9

Bash 4+ voorbeelden. OPMERKING: Niet gebruiken van offertes zal problemen veroorzaken wanneer woorden spaties bevatten, enz. Wezen altijd in Bash, IMO.

Hier zijn enkele voorbeelden bash 4 +:

Voorbeeld 1, cheque ‘ja’ in string (niet hoofdlettergevoelig):

   if [[ "${str,,}" == *"yes"* ]] ;then

Voorbeeld 2, controleer op ‘ja’ in string (case ongevoelig):

   if [[ "$(echo "$str" | tr '[:upper:]' '[:lower:]')" == *"yes"* ]] ;then

Voorbeeld 3, controleer op ‘ja’ in string (hoofdlettergevoelig):

    if [[ "${str}" == *"yes"* ]] ;then

Voorbeeld 4, controleer op ‘ja’ in string (hoofdlettergevoelig):

    if [[ "${str}" =~ "yes" ]] ;then

Voorbeeld 5, exacte overeenkomst (hoofdlettergevoelig):

    if [[ "${str}" == "yes" ]] ;then

Voorbeeld 6, exacte overeenkomst (case ongevoelig):

    if [[ "${str,,}" == "yes" ]] ;then

Voorbeeld 7, exacte overeenkomst:

    if [ "$a" = "$b" ] ;then

Voorbeeld 8, wildcard match .Ext (case ongevoelig):

    if echo "$a" | egrep -iq "\.(mp[3-4]|txt|css|jpg|png)" ; then

Geniet.


10

Zoals Paul vermeldde in zijn prestatievergelijking:

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

Dit is POSIX-compatibel, zoals de ‘case “$string” in’ het antwoord van Marcus, maar het is iets gemakkelijker te lezen dan het antwoord op de case-statement. Houd er ook rekening mee dat dit veel langzamer gaat dan het gebruik van een case-statement. Zoals Paul al zei, gebruik het niet in een lus.


Antwoord 11

Wat dacht je hiervan:

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

Antwoord 12

Dit Stack Overflow-antwoordwas de enige die spatie- en streepjestekens opving:

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

Antwoord 13

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

Antwoord 14

Een daarvan is:

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

Antwoord 15

Mijn .bash_profile-bestand en hoe ik grep heb gebruikt:

Als de omgevingsvariabele PATH mijn twee bin-mappen bevat, voeg ze dan niet toe,

# .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

Antwoord 16

grep -qis hiervoor handig.

Hetzelfde met awk:

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

Uitvoer:

Niet gevonden

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

Uitvoer:

Gevonden

Originele bron: http:// onstabiele.blogspot.com/2008/06/bash-search-letter-in-string-awk.html


Antwoord 17

Ik hou van sed.

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

Bewerken, logica:

  • Gebruik sed om instantie van substring uit string te verwijderen

  • Als de nieuwe tekenreeks verschilt van de oude tekenreeks, bestaat er een subtekenreeks


Antwoord 18

Ik merkte dat ik deze functionaliteit vrij vaak nodig had, dus ik gebruik een zelfgemaakte shell-functie in mijn .bashrczoals deze, waardoor ik deze zo vaak kan hergebruiken als nodig is, met een gemakkelijk te onthouden naam:

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

Om te testen of $string1(zeg, abc) is opgenomen in $string2(zeg, 123abcABC) Ik hoef alleen maar stringinstring "$string1" "$string2"uit te voeren en te controleren op de geretourneerde waarde, bijvoorbeeld

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

Antwoord 19

Uitbreiding van de vraag die hier wordt beantwoord Hoe weet je of een string een andere string bevat in POSIX sh?:

Deze oplossing werkt met speciale tekens:

# contains(string, substring)
#
# Returns 0 if the specified string contains the specified substring,
# otherwise returns 1.
contains() {
    string="$1"
    substring="$2"
    if echo "$string" | $(type -p ggrep grep | head -1) -F -- "$substring" >/dev/null; 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"
contains "abcd [efg] hij" "[efg]" && echo "abcd [efg] hij contains [efg]"
contains "abcd [efg] hij" "[effg]" || echo "abcd [efg] hij does not contain [effg]"
contains "abcd *efg* hij" "*efg*" && echo "abcd *efg* hij contains *efg*"
contains "abcd *efg* hij" "d *efg* h" && echo "abcd *efg* hij contains d *efg* h"
contains "abcd *efg* hij" "*effg*" || echo "abcd *efg* hij does not contain *effg*"

20

Exacte woordwedstrijd:

string='My long string'
exactSearch='long'
if grep -E -q "\b${exactSearch}\b" <<<${string} >/dev/null 2>&1
  then
    echo "It's there"
  fi

21

Probeer Oobash.

Het is een OO-stijl string-bibliotheek voor bash 4. Het heeft ondersteuning voor Duitse Umlauts. Het is geschreven in bash.

Veel functies zijn beschikbaar: -base64Decode, -base64Encode, -capitalize, -center, -charAt, -concat, -contains, -count, -endsWith, -equals, -equalsIgnoreCase, -reverse, -hashCode, -indexOf, -isAlnum, -isAlpha, -isAscii, -isDigit, -isEmpty, -isHexDigit, -isLowerCase, -isSpace, -isPrintable, -isUpperCase, -isVisible, -lastIndexOf, -length, -matches, -replaceAll, -replaceFirst, -startsWith, -substring, -swapCase, -toLowerCase, -toString, -toUpperCase, -trim, en -zfill.

Bekijk de Bevat Voorbeeld:

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

oobash is beschikbaar bij SourceForge.net .


22

case $string in (*foo*)
  # Do stuff
esac

Dit is hetzelfde antwoord als https://stackoverflow.com/a/229585/11267590. Maar eenvoudige stijl en ook POSIX-compatibel.


Antwoord 23

Ik gebruik deze functie (één afhankelijkheid niet inbegrepen, maar duidelijk). Het doorstaat de onderstaande tests. Als de functie een waarde retourneert > 0 dan is de string gevonden. U kunt in plaats daarvan net zo gemakkelijk 1 of 0 retourneren.

function str_instr {
   # Return position of ```str``` within ```string```.
   # >>> str_instr "str" "string"
   # str: String to search for.
   # string: String to search.
   typeset str string x
   # Behavior here is not the same in bash vs ksh unless we escape special characters.
   str="$(str_escape_special_characters "${1}")"
   string="${2}"
   x="${string%%$str*}"
   if [[ "${x}" != "${string}" ]]; then
      echo "${#x} + 1" | bc -l
   else
      echo 0
   fi
}
function test_str_instr {
   str_instr "(" "'foo@host (dev,web)'" | assert_eq 11
   str_instr ")" "'foo@host (dev,web)'" | assert_eq 19
   str_instr "[" "'foo@host [dev,web]'" | assert_eq 11
   str_instr "]" "'foo@host [dev,web]'" | assert_eq 19
   str_instr "a" "abc" | assert_eq 1
   str_instr "z" "abc" | assert_eq 0
   str_instr "Eggs" "Green Eggs And Ham" | assert_eq 7
   str_instr "a" "" | assert_eq 0
   str_instr "" "" | assert_eq 0
   str_instr " " "Green Eggs" | assert_eq 6
   str_instr " " " Green "  | assert_eq 1
}

Antwoord 24

msg="message"
function check {
    echo $msg | egrep [abc] 1> /dev/null
    if [ $? -ne 1 ];
    then 
        echo "found" 
    else 
        echo "not found" 
    fi
}
check

Hiermee vindt u elk voorkomen van a of b of c

Other episodes