Bagaimana Anda tahu jika sebuah string berisi string lain di POSIX sh?

136

Saya ingin menulis skrip shell Unix yang akan melakukan berbagai logika jika ada string di dalam string lain. Misalnya, jika saya berada di folder tertentu, cabut cabang. Bisakah seseorang tolong beri tahu saya cara melakukannya? Jika memungkinkan saya ingin membuat shell ini tidak spesifik (yaitu bukan bash saja) tetapi jika tidak ada cara lain saya dapat melakukannya dengan itu.

#!/usr/bin/env sh

if [ "$PWD" contains "String1" ]
then
    echo "String1 present"
elif [ "$PWD" contains "String2" ]
then
    echo "String2 present"
else
    echo "Else"
fi
Mat
sumber
2
Saya menyadari ini sudah tua, tetapi berikut adalah beberapa hal yang perlu diperhatikan untuk pengunjung masa depan: (1) Biasanya merupakan praktik yang baik untuk memesan SNAKE_CASE nama variabel untuk lingkungan dan shell variabel internal. (2) Pengaturan CURRENT_DIRredundan; Anda bisa menggunakannya $PWD.
nyuszika7h

Jawaban:

162

Inilah solusi lain. Ini menggunakan ekspansi parameter substring POSIX , sehingga berfungsi di Bash, Dash, KornShell (ksh), Z shell (zsh), dll.

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

Versi yang difungsikan dengan beberapa contoh:

# 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
sumber
3
Ini tidak berfungsi untuk saya jika substring berisi garis miring terbalik. Seperti biasa, substring="$( printf '%q' "$2" )"menghemat hari.
Egor Tensin
ini juga cocok dengan substrinsg. string = "aplha beta betaone" substring = "beta". cocok dengan kedua betaone dan dbeta, yang menurut saya salah.
rajeev
1
Bagaimana dengan menggunakan wildcard ? [[ $haystack == *"My needle"* ]]
Pablo A
4
Apa yang salah adalah bahwa tanda kurung ganda bukan POSIX, yang merupakan premis dari pertanyaan, @Pablo.
Rob Kennedy
1
Ini tidak berfungsi dengan karakter khusus seperti []. Lihat jawaban saya stackoverflow.com/a/54490453/712666 .
Alex Skrypnyk
85

Shell POSIX murni:

#!/bin/sh
CURRENT_DIR=`pwd`

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

Kerang diperpanjang seperti ksh atau bash memiliki mekanisme pencocokan mewah, tetapi gaya lama casesangat kuat.

Norman Ramsey
sumber
40

Sayangnya, saya tidak mengetahui cara untuk melakukan ini di sh. Namun, menggunakan bash (mulai versi 3.0.0, yang mungkin milik Anda), Anda dapat menggunakan operator = ~ seperti ini:

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

Sebagai bonus tambahan (dan / atau peringatan, jika string Anda memiliki karakter lucu di dalamnya), = ~ menerima regex sebagai operan yang tepat jika Anda meninggalkan tanda kutip.

John Hyland
sumber
3
Jangan mengutip regex, atau itu tidak akan berfungsi secara umum. Sebagai contoh, cobalah [[ test =~ "test.*" ]]vs [[ test =~ test.* ]].
l0b0
1
Yah, itu akan berfungsi dengan baik jika Anda menguji untuk substring, seperti pada pertanyaan awal, tetapi itu tidak akan memperlakukan operan yang tepat sebagai regex. Saya akan memperbarui jawaban saya untuk membuatnya lebih jelas.
John Hyland
3
Ini Bash, bukan POSIX sh saat pertanyaan diajukan.
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
sumber
2
Ubah grep -q "$2"ke grep -q "$2" > /dev/nulluntuk menghindari hasil yang tidak diinginkan.
Victor Sergienko
15

Berikut ini tautan ke berbagai solusi masalah Anda.

Ini adalah favorit saya karena masuk akal terbaca oleh manusia:

Metode Bintang Wildcard

if [[ "$string" == *"$substring"* ]]; then
    return 1
fi
return 0
ma77c
sumber
Pada shsaya menerima "operan tidak dikenal" dengan ini. Bekerja dengan Bash.
halfer
13
[[bukan 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
metode helpermet
sumber
6

Ada ekspresi reguler Bash . Atau ada 'expr':

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

-> output: String 1 berisi string 2


sumber
2

Lihat halaman manual untuk program 'test'. Jika Anda hanya menguji keberadaan direktori, Anda biasanya akan melakukan hal seperti ini:

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

Jika Anda benar-benar mencoba mencocokkan string, Anda juga dapat menggunakan aturan ekspansi bash & wildcard:

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

Jika Anda perlu melakukan sesuatu yang lebih kompleks, Anda harus menggunakan program lain seperti expr.

Jdeseno
sumber
1
Tampaknya ada kutipan ganda palsu (") dalam contoh kedua Anda.
Alexis Wilke
2

Dalam kasus khusus di mana Anda ingin menemukan apakah suatu kata terkandung dalam teks yang panjang, Anda dapat mengulangi teks yang panjang dengan sebuah loop.

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

Ini shell Bourne murni.

Kemin Zhou
sumber
1

Jika Anda menginginkan metode hanya ksh yang secepat "tes", Anda dapat melakukan sesuatu seperti:

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

Ini bekerja dengan menghapus jarum di tumpukan jerami dan kemudian membandingkan panjang tali tumpukan jerami lama dan baru.

JoeOfTex
sumber
1
Apakah tidak mungkin mengembalikan hasil test? Seperti di dalam return [ ${#haystack} -eq ${#1} ]?
Alexis Wilke
Ya itu benar. Saya akan membiarkannya seperti ini karena lebih mudah dipahami oleh orang awam. Jika Anda menggunakan kode Anda, gunakan metode @AlexisWilke.
JoeOfTex