Bir dizenin POSIX sh içinde başka bir dizge içerip içermediğini nasıl anlarsınız?


136

Başka bir dizgenin içinde bir dizge varsa çeşitli mantık yapacak bir Unix kabuk betiği yazmak istiyorum. Örneğin, belirli bir klasördeysem, dallanın. Lütfen birisi bana bunu nasıl başaracağımı söyleyebilir mi? Mümkünse, bunu kabuğa özel değil (yani sadece bash değil) yapmak istiyorum, ancak bununla yapabileceğim başka bir yol yoksa.

#!/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
Bunun eski olduğunun farkındayım, ancak gelecek ziyaretçiler için dikkat edilmesi gereken birkaç nokta var: (1) SNAKE_CASE değişken adlarını ortam ve kabuk dahili değişkenleri için ayırmak genellikle iyi bir uygulamadır. (2) Ayar CURRENT_DIRgereksizdir; sadece kullanabilirsiniz $PWD.
nyuszika7h

Yanıtlar:


162

İşte başka bir çözüm. Bu, POSIX alt dize parametresi genişletmesini kullanır , bu nedenle Bash, Dash, KornShell (ksh), Z kabuğu (zsh) vb.

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

Bazı örneklerle işlevselleştirilmiş bir versiyon:

# 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
Alt dizge ters eğik çizgi içeriyorsa bu benim için çalışmaz. Her zamanki gibi substring="$( printf '%q' "$2" )"günü kurtarıyor.
Egor Tensin

bu da yanlış substratlarla eşleşiyor. string = "aplha beta betaone" substring = "beta". hem betaone hem de dbeta ile eşleşiyor, bunun yanlış olduğunu düşünüyorum.
rajeev

1
Joker karakterler kullanmaya ne dersiniz ? [[ $haystack == *"My needle"* ]]
Pablo A

4
Yanlış olan, çift parantezlerin POSIX olmamasıdır, bu sorunun dayanağı @Pablo'dur.
Rob Kennedy

1
Bu gibi özel karakterlerle çalışmaz []. Cevabıma bakın stackoverflow.com/a/54490453/712666 .
Alex Skrypnyk

85

Saf POSIX kabuğu:

#!/bin/sh
CURRENT_DIR=`pwd`

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

Ksh veya bash gibi genişletilmiş kabuklar, süslü eşleme mekanizmalarına sahiptir, ancak eski tarz caseşaşırtıcı derecede güçlüdür.


40

Ne yazık ki, bunu sh içinde yapmanın bir yolunu bilmiyorum. Bununla birlikte, bash kullanarak (3.0.0 sürümünden başlayarak, muhtemelen sahip olduğunuz şeydir), = ~ operatörünü şu şekilde kullanabilirsiniz:

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

Ek bir bonus (ve / veya dizelerinizde komik karakterler varsa bir uyarı) olarak = ~, tırnak işaretlerini dışarıda bırakırsanız normal ifadeleri doğru işlenen olarak kabul eder.


3
Regex alıntı yapma, ya da olacak değil genel olarak çalışırlar. Örneğin, deneyin [[ test =~ "test.*" ]]VS. [[ test =~ test.* ]].
l0b0

1
Orijinal soruda olduğu gibi, bir alt dizeyi test ediyorsanız gayet iyi çalışacaktır, ancak doğru işleneni bir normal ifade olarak ele almayacaktır. Bunu daha net hale getirmek için cevabımı güncelleyeceğim.
John Hyland

3
Bu Bash, soruya göre POSIX sh değil.
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
İstenmeyen çıktıyı önlemek grep -q "$2"için grep -q "$2" > /dev/nullolarak değiştirin .
Victor Sergienko

15

İşte sorununuzla ilgili çeşitli çözümlere bir bağlantı .

İnsanların okuyabileceği en mantıklı olanı bu benim favorim:

Yıldız Joker Yöntemi

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

Bununla shbirlikte "bilinmeyen işlenen" aldım. Bash ile birlikte çalışır.
2018

13
[[değil 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"

-> çıktı: Dize 1, dize 2'yi içerir


2

'Test' programının kılavuz sayfasına bakın. Sadece bir dizinin varlığını test ediyorsanız, normalde böyle bir şey yaparsınız:

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

Aslında bir dizeyi eşleştirmeye çalışıyorsanız, bash genişletme kurallarını ve joker karakterleri de kullanabilirsiniz:

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

Daha karmaşık bir şey yapmanız gerekiyorsa, ifade gibi başka bir program kullanmanız gerekecektir.


1
İkinci örneğinizde sahte bir çift alıntı (") var gibi görünüyor.
Alexis Wilke

2

Bir kelimenin uzun bir metinde yer alıp almadığını bulmak istediğiniz özel durumlarda, uzun metni bir döngü ile yineleyebilirsiniz.

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

Bu saf Bourne kabuğu.


1

"Test" kadar hızlı bir tek ksh yöntemi istiyorsanız, aşağıdaki gibi bir şey yapabilirsiniz:

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

Samanlıktaki iğneyi silerek ve ardından eski ve yeni samanlıkların dize uzunluğunu karşılaştırarak çalışır.


1
Sonucunu döndürmek mümkün olmaz mıydı test? Gibi return [ ${#haystack} -eq ${#1} ]mi?
Alexis Wilke

Evet doğru. Meslekten olmayanlar için anlaşılması daha kolay olduğu için bunu böyle bırakacağım. Kodunuzda kullanıyorsanız @AlexisWilke yöntemini kullanın.
JoeOfTex
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.