Woran erkennt man, ob eine Zeichenfolge in POSIX sh eine andere Zeichenfolge enthält?

135

Ich möchte ein Unix-Shell-Skript schreiben, das verschiedene Logikfunktionen ausführt, wenn sich eine Zeichenfolge in einer anderen Zeichenfolge befindet. Wenn ich mich beispielsweise in einem bestimmten Ordner befinde, verzweigen Sie. Könnte mir bitte jemand sagen, wie das geht? Wenn möglich, möchte ich dies nicht Shell-spezifisch machen (dh nicht nur Bash), aber wenn es keinen anderen Weg gibt, kann ich damit auskommen.

#!/usr/bin/env sh

if [ "$PWD" contains "String1" ]
then
    echo "String1 present"
elif [ "$PWD" contains "String2" ]
then
    echo "String2 present"
else
    echo "Else"
fi
Matt
quelle
2
Mir ist klar, dass dies alt ist, aber für zukünftige Besucher sind einige Dinge zu beachten: (1) Es ist normalerweise empfehlenswert, SNAKE_CASE-Variablennamen für interne Umgebungs- und Shell-Variablen zu reservieren. (2) Einstellung CURRENT_DIRist redundant; Sie können nur verwenden $PWD.
Nyuszika7h

Antworten:

161

Hier ist noch eine andere Lösung. Dies verwendet die Erweiterung der POSIX-Teilzeichenfolgenparameter und funktioniert daher in Bash, Dash, KornShell (ksh), Z-Shell (zsh) usw.

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

Eine funktionalisierte Version mit einigen Beispielen:

# 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"
fjarlq
quelle
3
Dies funktioniert bei mir nicht, wenn der Teilstring Backslashes enthält. Wie üblich, substring="$( printf '%q' "$2" )"rettet den Tag.
Egor Tensin
Dies passt auch zu falschen Substraten. string = "aplha beta betaone" substring = "beta". es passt sowohl zu betaone als auch zu dbeta, was ich für falsch halte.
Rajeev
1
Was ist mit Platzhaltern ? [[ $haystack == *"My needle"* ]]
Pablo A
3
Was falsch ist, ist, dass doppelte Klammern nicht POSIX sind, was die Prämisse der Frage war, @Pablo.
Rob Kennedy
1
Dies funktioniert nicht mit Sonderzeichen wie []. Siehe meine Antwort stackoverflow.com/a/54490453/712666 .
Alex Skrypnyk
84

Reine POSIX-Shell:

#!/bin/sh
CURRENT_DIR=`pwd`

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

Erweiterte Shells wie ksh oder bash haben ausgefallene Matching-Mechanismen, aber der alte Stil caseist überraschend mächtig.

Norman Ramsey
quelle
40

Leider ist mir kein Weg bekannt, dies in sh zu tun. Mit bash (ab Version 3.0.0, was wahrscheinlich das ist, was Sie haben) können Sie den Operator = ~ jedoch wie folgt verwenden:

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

Als zusätzlichen Bonus (und / oder als Warnung, wenn Ihre Zeichenfolgen lustige Zeichen enthalten) akzeptiert = ~ reguläre Ausdrücke als richtigen Operanden, wenn Sie die Anführungszeichen weglassen.

John Hyland
quelle
3
Sie nicht zitieren die Regex, oder es wird nicht im Allgemeinen funktionieren. Zum Beispiel versuchen [[ test =~ "test.*" ]]vs. [[ test =~ test.* ]].
10.
1
Nun, es wird gut funktionieren, wenn Sie wie in der ursprünglichen Frage auf einen Teilstring testen, aber es wird den richtigen Operanden nicht als regulären Ausdruck behandeln. Ich werde meine Antwort aktualisieren, um das klarer zu machen.
John Hyland
3
Dies ist Bash, nicht POSIX sh, wie die Frage stellt.
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
a.saurabh
quelle
2
Wechseln Sie grep -q "$2"zu grep -q "$2" > /dev/null, um unerwünschte Ausgaben zu vermeiden.
Victor Sergienko
15

Hier finden Sie einen Link zu verschiedenen Lösungen Ihres Problems.

Dies ist mein Favorit, da es am menschlichsten lesbar ist:

Die Star Wildcard-Methode

if [[ "$string" == *"$substring"* ]]; then
    return 1
fi
return 0
ma77c
quelle
Am sherhielt ich damit "unbekannten Operanden". Funktioniert aber mit Bash.
halfer
13
[[ist nicht 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
Hilfsmethode
quelle
6

Es gibt reguläre Bash-Ausdrücke . Oder es gibt 'Ausdruck':

 if expr "$link" : '/.*' > /dev/null; then
    PRG="$link"
  else
    PRG=`dirname "$PRG"`/"$link"
  fi
bmargulies
quelle
2
test $(echo "stringcontain" "ingcon" |awk '{ print index($1, $2) }') -gt 0 && echo "String 1 contain string 2"

-> Ausgabe: String 1 enthält String 2


quelle
2

Auf der Manpage finden Sie das Testprogramm. Wenn Sie nur die Existenz eines Verzeichnisses testen, tun Sie normalerweise Folgendes:

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

Wenn Sie tatsächlich versuchen, eine Zeichenfolge abzugleichen, können Sie auch Bash-Erweiterungsregeln und Platzhalter verwenden:

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

Wenn Sie etwas komplexeres tun müssen, müssen Sie ein anderes Programm wie expr verwenden.

jdeseno
quelle
1
Es scheint ein falsches doppeltes Anführungszeichen (") in Ihrem zweiten Beispiel zu geben.
Alexis Wilke
2

In besonderen Fällen, in denen Sie herausfinden möchten, ob ein Wort in einem Langtext enthalten ist, können Sie den Langtext mit einer Schleife durchlaufen.

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

Dies ist reine Bourne-Shell.

Kemin Zhou
quelle
1

Wenn Sie eine Nur- Ksh- Methode wünschen , die so schnell wie "Test" ist, können Sie Folgendes tun:

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

Es funktioniert, indem die Nadel im Heuhaufen gelöscht und dann die Saitenlänge von alten und neuen Heuhaufen verglichen wird.

JoeOfTex
quelle
1
Wäre es nicht möglich, das Ergebnis von zurückzugeben test? Wie in return [ ${#haystack} -eq ${#1} ]?
Alexis Wilke
Ja, das ist richtig. Ich werde es so belassen, da es für Laien leichter zu verstehen ist. Wenn Sie in Ihrem Code verwenden, verwenden Sie die @ AlexisWilke-Methode.
JoeOfTex