Skąd wiadomo, czy łańcuch zawiera inny łańcuch w POSIX sh?


140

Chcę napisać skrypt powłoki systemu Unix, który będzie wykonywał różne logiki, jeśli w innym ciągu znajduje się ciąg. Na przykład, jeśli jestem w określonym folderze, odejdź. Czy ktoś mógłby mi powiedzieć, jak to osiągnąć? Jeśli to możliwe, chciałbym, aby to nie było specyficzne dla powłoki (tj. Nie tylko dla basha), ale jeśli nie ma innego sposobu, mogę to zrobić.

#!/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
Zdaję sobie sprawę, że to jest stare, ale oto kilka rzeczy, na które należy zwrócić uwagę przyszłym odwiedzającym: (1) Zwykle dobrą praktyką jest zarezerwowanie nazw zmiennych SNAKE_CASE dla zmiennych środowiskowych i wewnętrznych powłoki. (2) Ustawienie CURRENT_DIRjest zbędne; możesz po prostu użyć $PWD.
nyuszika7h

Odpowiedzi:


171

Oto jeszcze jedno rozwiązanie. Wykorzystuje rozwinięcie parametrów podciągów POSIX , więc działa w Bash, Dash, KornShell (ksh), Z shell (zsh) itp.

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

Sfunkcjonalizowana wersja z kilkoma przykładami:

# 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
To nie działa dla mnie, jeśli podciąg zawiera odwrotne ukośniki. Jak zwykle substring="$( printf '%q' "$2" )"ratuje dzień.
Egor Tensin

to również pasuje do niewłaściwego substrinsg. string = "aplha beta betaone" substring = "beta". pasuje zarówno do betaone, jak i do dbety, co moim zdaniem jest złe.
rajeev

1
A co z używaniem symboli wieloznacznych ? [[ $haystack == *"My needle"* ]]
Pablo Bianchi

6
Nieprawidłowe jest to, że podwójne nawiasy nie są zgodne z POSIX, co było założeniem pytania @Pablo.
Rob Kennedy

2
Nie działa to w przypadku znaków specjalnych, takich jak []. Zobacz moją odpowiedź stackoverflow.com/a/54490453/712666 .
Alex Skrypnyk

88

Czysta powłoka POSIX:

#!/bin/sh
CURRENT_DIR=`pwd`

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

Rozszerzone powłoki, takie jak ksh lub bash, mają fantazyjne mechanizmy dopasowywania, ale stary styl casejest zaskakująco potężny.


38

Niestety, nie wiem, jak to zrobić w sh. Jednak używając basha (zaczynając od wersji 3.0.0, którą prawdopodobnie masz), możesz użyć operatora = ~ w następujący sposób:

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

Jako dodatkowy bonus (i / lub ostrzeżenie, jeśli twoje łańcuchy mają jakieś śmieszne znaki), = ~ akceptuje wyrażenia regularne jako właściwy operand, jeśli pominiesz cudzysłowy.


3
Nie zacytować regex, czy będzie to nie działa w ogóle. Na przykład, spróbuj [[ test =~ "test.*" ]]Vs. [[ test =~ test.* ]].
l0b0

1
Cóż, będzie działać dobrze, jeśli testujesz podciąg, tak jak w pierwotnym pytaniu, ale nie potraktuje właściwego operandu jako wyrażenia regularnego. Zaktualizuję odpowiedź, aby była bardziej zrozumiała.
John Hyland,

3
To jest Bash, a nie POSIX sh, jak zadaje pytanie.
Reid,

29
#!/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
Zmień grep -q "$2"na, grep -q "$2" > /dev/nullaby uniknąć niepożądanego wyjścia.
Victor Sergienko

Zmień grep -q "$2"na, grep -q "$2" 2>/dev/nullaby uniknąć niepożądanego wyjścia.
ulidtko

14
case $(pwd) in
  *path) echo "ends with path";;
  path*) echo "starts with path";;
  *path*) echo "contains path";;
  *) echo "this is the default";;
esac

12

Oto łącze do różnych rozwiązań Twojego problemu.

To jest mój ulubiony, ponieważ ma najbardziej czytelny dla człowieka sens:

Metoda Star Wildcard

if [[ "$string" == *"$substring"* ]]; then
    return 1
fi
return 0

Na shdostałam „nieznany argument” z tym. Działa jednak z Bash.
halfer

15
[[nie jest POSIX
Ian


2
test $(echo "stringcontain" "ingcon" |awk '{ print index($1, $2) }') -gt 0 && echo "String 1 contain string 2"

-> wyjście: ciąg 1 zawiera ciąg 2


2

Jeśli potrzebujesz metody opartej tylko na ksh , która jest tak szybka jak „test”, możesz zrobić coś takiego:

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

Działa poprzez usunięcie igły w stogu siana, a następnie porównanie długości sznurka starego i nowego stogu.


1
Czy nie byłoby możliwe zwrócenie wyniku test? Jak w return [ ${#haystack} -eq ${#1} ]?
Alexis Wilke

Tak to jest poprawne. Zostawię to w ten sposób, ponieważ jest to łatwiejsze do zrozumienia dla laików. Jeśli używasz w swoim kodzie, użyj metody @AlexisWilke.
JoeOfTex

2

Zobacz stronę podręcznika programu „test”. Jeśli po prostu sprawdzasz istnienie katalogu, normalnie zrobiłbyś coś takiego:

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

Jeśli faktycznie próbujesz dopasować ciąg, możesz również użyć reguł rozwinięcia bash i symboli wieloznacznych:

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

Jeśli chcesz zrobić coś bardziej złożonego, musisz użyć innego programu, takiego jak expr.


1
Wydaje się, że w drugim przykładzie znajduje się fałszywy podwójny cudzysłów („).
Alexis Wilke,

2

W szczególnych przypadkach, gdy chcesz sprawdzić, czy słowo jest zawarte w długim tekście, możesz iterować po długim tekście za pomocą pętli.

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

To jest czysta skorupa Bourne'a.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.