คุณจะทราบได้อย่างไรว่าสตริงมีสตริงอื่นใน POSIX sh?


140

ฉันต้องการเขียนสคริปต์เชลล์ Unix ที่จะทำตรรกะต่างๆหากมีสตริงอยู่ในสตริงอื่น ตัวอย่างเช่นหากฉันอยู่ในโฟลเดอร์ใดโฟลเดอร์หนึ่งให้แยกสาขาออก ใครช่วยบอกวิธีทำให้สำเร็จได้ไหม ถ้าเป็นไปได้ฉันต้องการทำให้สิ่งนี้ไม่ใช่เชลล์เฉพาะ (เช่นไม่ใช่ทุบตีเท่านั้น) แต่ถ้าไม่มีวิธีอื่นที่ฉันสามารถทำได้

#!/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
ฉันรู้ว่านี่เป็นเรื่องเก่า แต่มีบางสิ่งที่ควรทราบสำหรับผู้เยี่ยมชมในอนาคต: (1) โดยปกติแล้วการสงวนชื่อตัวแปร SNAKE_CASE สำหรับสภาพแวดล้อมและตัวแปรภายในเชลล์เป็นแนวทางปฏิบัติที่ดี (2) การตั้งค่าCURRENT_DIRซ้ำซ้อน $PWDคุณก็สามารถใช้
nyuszika7h

คำตอบ:


171

นี่เป็นอีกวิธีหนึ่ง สิ่งนี้ใช้การขยายพารามิเตอร์สตริงย่อย POSIXดังนั้นจึงใช้งานได้ใน Bash, Dash, KornShell (ksh), Z shell (zsh) เป็นต้น

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

เวอร์ชันที่ใช้งานได้พร้อมตัวอย่างบางส่วน:

# 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
สิ่งนี้ใช้ไม่ได้สำหรับฉันหากสตริงย่อยมีแบ็กสแลช ตามปกติsubstring="$( printf '%q' "$2" )"บันทึกวัน
Egor Tensin

สิ่งนี้ตรงกับ substrinsg ผิดด้วย string = "aplha beta betaone" substring = "beta" มันตรงทั้ง betaone และ dbeta ซึ่งฉันคิดว่าผิด
rajeev

1
แล้วการใช้สัญลักษณ์แทนล่ะ? [[ $haystack == *"My needle"* ]]
Pablo Bianchi

6
มีอะไรผิดปกติที่วงเล็บสองชั้นไม่ใช่ POSIX ซึ่งเป็นหลักฐานของคำถาม @Pablo
Rob Kennedy

2
สิ่งนี้ใช้ไม่ได้กับอักขระพิเศษเช่น[]. ดูคำตอบของฉันstackoverflow.com/a/54490453/712666
Alex Skrypnyk

88

เปลือก POSIX บริสุทธิ์:

#!/bin/sh
CURRENT_DIR=`pwd`

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

เปลือกหอยแบบขยายเช่น ksh หรือ bash มีกลไกการจับคู่ที่แปลกใหม่ แต่รูปแบบเก่าcaseนั้นทรงพลังอย่างน่าประหลาดใจ


38

น่าเศร้าที่ฉันไม่รู้วิธีทำใน sh อย่างไรก็ตามการใช้ bash (เริ่มต้นในเวอร์ชัน 3.0.0 ซึ่งอาจเป็นสิ่งที่คุณมี) คุณสามารถใช้ตัวดำเนินการ = ~ ดังนี้:

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

เป็นโบนัสเพิ่มเติม (และ / หรือคำเตือนหากสตริงของคุณมีอักขระตลก ๆ อยู่ในนั้น) = ~ ยอมรับ regexes เป็นตัวถูกดำเนินการที่ถูกต้องหากคุณไม่ใส่เครื่องหมายคำพูด


3
อย่าอ้างนิพจน์ทั่วไปมิฉะนั้นจะไม่สามารถใช้งานได้โดยทั่วไป ตัวอย่างเช่นลองเทียบกับ[[ test =~ "test.*" ]] [[ test =~ test.* ]]
l0b0

1
มันจะใช้งานได้ดีถ้าคุณกำลังทดสอบสตริงย่อยเหมือนในคำถามเดิม แต่จะไม่ถือว่าตัวถูกดำเนินการเป็นนิพจน์ทั่วไป ฉันจะอัปเดตคำตอบเพื่อให้ชัดเจนยิ่งขึ้น
John Hyland

3
นี่คือ Bash ไม่ใช่ POSIX sh ตามที่คำถามถาม
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
เปลี่ยนgrep -q "$2"เป็นgrep -q "$2" > /dev/nullเพื่อหลีกเลี่ยงเอาต์พุตที่ไม่ต้องการ
Victor Sergienko

เปลี่ยนgrep -q "$2"เป็นgrep -q "$2" 2>/dev/nullเพื่อหลีกเลี่ยงเอาต์พุตที่ไม่ต้องการ
ulidtko


12

นี่คือลิงค์ไปยังวิธีแก้ปัญหาต่างๆของคุณ

นี่เป็นสิ่งที่ฉันชอบเพราะมันทำให้มนุษย์เข้าใจได้ง่ายที่สุด:

วิธี Star Wildcard

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

เมื่อshฉันได้รับ "ตัวถูกดำเนินการที่ไม่รู้จัก" ด้วยสิ่งนี้ ทำงานร่วมกับ Bash แม้ว่า
แขวน

15
[[ไม่ใช่ POSIX
เอียน

4

มีทุบตีแสดงออกปกติ หรือมี 'expr':

 if expr "$link" : '/.*' > /dev/null; then
    PRG="$link"
  else
    PRG=`dirname "$PRG"`/"$link"
  fi

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

-> เอาต์พุต: สตริง 1 ประกอบด้วยสตริง 2


2

หากคุณต้องการวิธีเฉพาะkshที่เร็วเท่ากับ "test" คุณสามารถทำสิ่งต่อไปนี้

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

มันทำงานโดยการลบเข็มในกองหญ้าแล้วเปรียบเทียบความยาวสตริงของหญ้าแห้งเก่าและใหม่


1
จะไม่สามารถคืนผลลัพธ์ของtest? เช่นเดียวกับในreturn [ ${#haystack} -eq ${#1} ]?
Alexis Wilke

ใช่ถูกต้อง ฉันจะปล่อยไว้แบบนี้เพื่อให้ฆราวาสเข้าใจง่ายขึ้น หากคุณใช้ในโค้ดของคุณให้ใช้เมธอด @AlexisWilke
JoeOfTex

2

ดู manpage สำหรับโปรแกรม 'test' หากคุณกำลังทดสอบการมีอยู่ของไดเร็กทอรีโดยปกติคุณจะต้องดำเนินการดังนี้:

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

หากคุณพยายามจับคู่สตริงจริงๆคุณสามารถใช้กฎการขยาย bash & wildcards ได้เช่นกัน:

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

หากคุณต้องการทำสิ่งที่ซับซ้อนมากขึ้นคุณจะต้องใช้โปรแกรมอื่นเช่น expr


1
ดูเหมือนว่าจะมีเครื่องหมายอัญประกาศ (") ปลอมในตัวอย่างที่สอง
Alexis Wilke

2

ในกรณีพิเศษที่คุณต้องการตรวจสอบว่ามีคำใดอยู่ในข้อความยาวหรือไม่คุณสามารถวนซ้ำข้อความยาว ๆ ด้วยการวนซ้ำได้

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

นี่คือเปลือกบอร์นบริสุทธิ์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.