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

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"

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.


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.


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

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

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


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


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.


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.


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.


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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.