วิธีการตรวจสอบว่าสตริงมีสตริงย่อยใน Bash


2566

ฉันมีสตริงใน Bash:

string="My string"

ฉันจะทดสอบว่ามันมีสตริงอื่นได้อย่างไร?

if [ $string ?? 'foo' ]; then
  echo "It's there!"
fi

??ตัวดำเนินการที่ไม่รู้จักของฉันอยู่ที่ไหน ฉันใช้เสียงสะท้อนgrepหรือไม่?

if echo "$string" | grep 'foo'; then
  echo "It's there!"
fi

ที่ดูเหมือนเงอะงะเล็กน้อย


4
สวัสดีถ้าสตริงว่างเปล่าเป็นเท็จทำไมคุณคิดว่ามันเงอะงะ? มันเป็นวิธีเดียวที่ได้ผลสำหรับฉันแม้จะมีวิธีแก้ไขปัญหาที่เสนอ
ericson.cepeda

1
คุณสามารถใช้exprคำสั่งได้ที่นี่
cli__

4
นี่คือหนึ่งสำหรับเชลล์ posix: stackoverflow.com/questions/2829613/…
sehe

คำตอบ:


3650

คุณสามารถใช้คำตอบของ Marcus (* wildcard)นอกคำสั่ง case ได้เช่นกันหากคุณใช้วงเล็บสองตัว:

string='My long string'
if [[ $string == *"My long"* ]]; then
  echo "It's there!"
fi

โปรดทราบว่าช่องว่างในสตริงเข็มจะต้องอยู่ระหว่างเครื่องหมายคำพูดคู่และ*สัญลักษณ์แทนควรอยู่ด้านนอก นอกจากนี้ยังทราบว่ามีการดำเนินการเปรียบเทียบง่ายๆคือใช้ (คือ==) =~ไม่ใช่ผู้ประกอบการ


127
โปรดทราบว่าคุณสามารถย้อนกลับการเปรียบเทียบโดยเพียงแค่เปลี่ยนเป็น! = ในการทดสอบ ขอบคุณสำหรับคำตอบ!
Quinn Taylor

63
@Jonik: คุณอาจจะหายไป shebang #!/bin/shหรือมีมันเป็น ลอง#!/bin/bashแทน
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

10
เว้นช่องว่างระหว่างวงเล็บและเนื้อหา
ราคาพอล

17
คุณไม่จำเป็นต้องพูดตัวแปรภายใน [[]] สิ่งนี้จะได้ผลเช่นกัน: [[$ string == $ needle ]] && echo พบ
Orwellophile

12
@Orwellophile ระวัง! [[คุณสามารถละเว้นคำพูดสองในอาร์กิวเมนต์แรก ดูideone.com/ZSy1gZ
nyuszika7h

611

หากคุณต้องการวิธีการ regex:

string='My string';

if [[ $string =~ "My" ]]
then
   echo "It's there!"
fi

2
ต้องแทนที่ egrep regex ในสคริปต์ทุบตีนี้ทำงานได้อย่างสมบูรณ์แบบ!
blast_hardcheese

114
=~ผู้ประกอบการแล้วค้นหาสตริงทั้งสำหรับการแข่งขัน; .*ของที่นี่มีภายนอก นอกจากนี้ราคาโดยทั่วไปจะดีกว่า backslashes:[[ $string =~ "My s" ]]
bukzor

16
@bukzor คำคมหยุดทำงานที่นี่ในฐานะของทุบตี 3.2+: tiswww.case.edu/php/chet/bash/FAQ E14)อาจเป็นการดีที่สุดที่จะกำหนดให้กับตัวแปร (โดยใช้เครื่องหมายคำพูด) จากนั้นเปรียบเทียบ เช่นนี้:re="My s"; if [[ $string =~ $re ]]
seanf

36
ทดสอบถ้ามันไม่ไม่ประกอบด้วยสตริง: if [[ ! "abc" =~ "d" ]]เป็นความจริง
KrisWebDev

1
โปรดทราบว่าลำดับการทดสอบเกิดขึ้น ผลลัพธ์ของโค้ดบรรทัดต่อไปนี้จะถูกส่งต่อ 2number=2; if [[ "1, 2, 4, 5" = *${number}* ]]; then echo forward $number; fi; if [[ *${number}* = "1, 2, 4, 5" ]]; then echo backwards $number; fi;
Douwe van der Leest

356

ฉันไม่แน่ใจเกี่ยวกับการใช้คำสั่ง if แต่คุณสามารถได้รับผลกระทบที่คล้ายกันกับคำสั่งกรณี:

case "$string" in 
  *foo*)
    # Do stuff
    ;;
esac

76
นี่น่าจะเป็นทางออกที่ดีที่สุดเพราะมันพกพาไปยัง posix shells (aka ไม่มี bashisms)
technosaurus

25
@technosaurus ฉันคิดว่ามันค่อนข้างแปลกที่จะวิพากษ์วิจารณ์ "bashism" ในคำถามที่มีแท็กทุบตีเท่านั้น :)
PP

39
@PP มันไม่ได้เป็นคำวิจารณ์มากนักเนื่องจากความชอบของโซลูชันสากลที่มากกว่าโซลูชันที่ จำกัด มากขึ้น โปรดพิจารณาว่าหลายปีต่อมาผู้คน (อย่างฉัน) จะแวะมาหาคำตอบนี้และอาจยินดีที่พบว่ามีประโยชน์ในขอบเขตที่กว้างกว่าคำถามเดิม อย่างที่พวกเขาพูดในโลกของโอเพ่นซอร์ส: "ตัวเลือกนั้นดี!"
Carl Smotricz

2
@technosaurus, FWIW [[ $string == *foo* ]]ยังทำงานในบางรุ่นที่สอดคล้องกับ POSIX sh (เช่น/usr/xpg4/bin/shบน Solaris 10) และ ksh (> = 88)
maxschlepzig

3
Busybox ash ไม่รองรับเครื่องหมายดอกจันใน[[... สวิตช์ตัวพิมพ์เล็ก - ใหญ่ทำงานได้!
Ray Foss

232

stringContain หลากหลาย (เข้ากันได้หรือเป็นอิสระกรณี)

เนื่องจากคำตอบ Stack Overflow เหล่านี้ส่วนใหญ่บอกเกี่ยวกับBashฉันได้โพสต์ฟังก์ชัน Bash อิสระที่ด้านล่างสุดของโพสต์นี้ ...

อย่างไรก็ตามมีของฉัน

คำตอบที่เข้ากันได้

เนื่องจากมีคำตอบจำนวนมากที่ใช้คุณสมบัติเฉพาะของ Bash อยู่แล้วจึงมีวิธีการทำงานภายใต้เชลล์ที่มีคุณสมบัติด้อยกว่าเช่นBusyBox :

[ -z "${string##*$reqsubstr*}" ]

ในทางปฏิบัติสิ่งนี้สามารถให้:

string='echo "My string"'
for reqsubstr in 'o "M' 'alt' 'str';do
  if [ -z "${string##*$reqsubstr*}" ] ;then
      echo "String '$string' contain substring: '$reqsubstr'."
    else
      echo "String '$string' don't contain substring: '$reqsubstr'."
    fi
  done

สิ่งนี้ได้รับการทดสอบภายใต้ Bash, Dash , KornShell ( ksh) และash (BusyBox) และผลลัพธ์จะเป็นดังนี้:

String 'echo "My string"' contain substring: 'o "M'.
String 'echo "My string"' don't contain substring: 'alt'.
String 'echo "My string"' contain substring: 'str'.

เป็นฟังก์ชั่นเดียว

ตามที่ถูกถามโดย @EeroAaltonen ที่นี่เป็นเวอร์ชั่นของการสาธิตเดียวกันทดสอบภายใต้เชลล์เดียวกัน:

myfunc() {
    reqsubstr="$1"
    shift
    string="$@"
    if [ -z "${string##*$reqsubstr*}" ] ;then
        echo "String '$string' contain substring: '$reqsubstr'.";
      else
        echo "String '$string' don't contain substring: '$reqsubstr'."
    fi
}

แล้ว:

$ myfunc 'o "M' 'echo "My String"'
String 'echo "My String"' contain substring 'o "M'.

$ myfunc 'alt' 'echo "My String"'
String 'echo "My String"' don't contain substring 'alt'.

แจ้งให้ทราบล่วงหน้า:คุณต้องหลบหนีหรือสองครั้งใส่เครื่องหมายคำพูดและ / หรือเครื่องหมายคำพูดคู่:

$ myfunc 'o "M' echo "My String"
String 'echo My String' don't contain substring: 'o "M'.

$ myfunc 'o "M' echo \"My String\"
String 'echo "My String"' contain substring: 'o "M'.

ฟังก์ชั่นที่เรียบง่าย

สิ่งนี้ถูกทดสอบภายใต้ BusyBox, Dash และแน่นอน Bash:

stringContain() { [ -z "${2##*$1*}" ]; }

จากนั้นตอนนี้:

$ if stringContain 'o "M3' 'echo "My String"';then echo yes;else echo no;fi
no
$ if stringContain 'o "M' 'echo "My String"';then echo yes;else echo no;fi
yes

... หรือถ้าสตริงที่ส่งอาจว่างเปล่าตามที่ระบุโดย @Sjlver ฟังก์ชันจะกลายเป็น:

stringContain() { [ -z "${2##*$1*}" ] && [ -z "$1" -o -n "$2" ]; }

หรือตามที่แนะนำโดยข้อคิดเห็นของ Adrian Günterหลีกเลี่ยง-oสวิตช์:

stringContain() { [ -z "${2##*$1*}" ] && { [ -z "$1" ] || [ -n "$2" ];};}

ฟังก์ชั่นสุดท้าย (ง่าย):

และการย้อนกลับการทดสอบเพื่อทำให้เร็วขึ้น:

stringContain() { [ -z "$1" ] || { [ -z "${2##*$1*}" ] && [ -n "$2" ];};}

ด้วยสตริงว่าง:

$ if stringContain '' ''; then echo yes; else echo no; fi
yes
$ if stringContain 'o "M' ''; then echo yes; else echo no; fi
no

กรณีเป็นอิสระ (ทุบตีเท่านั้น!)

สำหรับการทดสอบสตริงโดยไม่สนใจขนาดตัวพิมพ์เพียงแปลงแต่ละสตริงเป็นตัวพิมพ์เล็ก:

stringContain() {
    local _lc=${2,,}
    [ -z "$1" ] || { [ -z "${_lc##*${1,,}*}" ] && [ -n "$2" ] ;} ;}

ตรวจสอบ:

stringContain 'o "M3' 'echo "my string"' && echo yes || echo no
no
stringContain 'o "My' 'echo "my string"' && echo yes || echo no
yes
if stringContain '' ''; then echo yes; else echo no; fi
yes
if stringContain 'o "M' ''; then echo yes; else echo no; fi
no

1
นี่จะดีกว่านี้ถ้าคุณสามารถหาวิธีที่จะนำไปใช้กับฟังก์ชันได้
Eero Aaltonen

2
@EeroAaltonen คุณจะหาฟังก์ชั่น (เพิ่มใหม่) ของฉันได้อย่างไร?
F. Hauri

2
ฉันรู้ว่า! หา -name "*" | xargs grep "myfunc" 2> / dev / null
eggmatters

6
มันวิเศษมากเพราะมันเข้ากันได้ดี หนึ่งข้อบกพร่อง แต่: มันไม่ทำงานหากสตริงที่กองหญ้าว่างเปล่า รุ่นที่ถูกต้องน่าจะเป็นstring_contains() { [ -z "${2##*$1*}" ] && [ -n "$2" -o -z "$1" ]; } ความคิดสุดท้าย: สตริงว่างมีสตริงว่างหรือไม่ เวอร์ชันด้านบนเป็นสิ่งที่ใช่ (เพราะ-o -z "$1"ส่วนหนึ่ง)
Sjlver

3
+1 ดีมาก! สำหรับฉันฉันเปลี่ยนคำสั่งซื้อ stringContain () {[-z "$ {1 ## * $ 2 *}"] && [-z "$ 2" -o -n "$ 1"]; }; "ค้นหาตำแหน่ง" "ค้นหาสิ่งที่" ทำงานใน busybox คำตอบที่ยอมรับข้างต้นไม่ทำงานใน busybox
user3439968

149

คุณควรจำไว้ว่าการเขียนสคริปต์เชลล์มีภาษาน้อยกว่าและมีชุดคำสั่งมากมาย สัญชาตญาณคุณคิดว่า "ภาษา" คุณจะต้องทำตามifด้วยหรือ[ [[ทั้งสองคำสั่งเป็นเพียงคำสั่งที่ส่งคืนสถานะการออกซึ่งบ่งชี้ความสำเร็จหรือความล้มเหลว (เช่นเดียวกับคำสั่งอื่น ๆ ทั้งหมด) ด้วยเหตุนี้ฉันจึงใช้grepไม่ใช่[คำสั่ง

แค่ทำ:

if grep -q foo <<<"$string"; then
    echo "It's there"
fi

ตอนนี้คุณกำลังคิดที่จะifทดสอบสถานะการออกของคำสั่งที่ตามมา (สมบูรณ์ด้วยเซมิโคลอน) ทำไมไม่ลองพิจารณาที่มาของสตริงที่คุณกำลังทดสอบอีกครั้ง

## Instead of this
filetype="$(file -b "$1")"
if grep -q "tar archive" <<<"$filetype"; then
#...

## Simply do this
if file -b "$1" | grep -q "tar archive"; then
#...

-qตัวเลือกทำให้ grep อะไรไม่ออกในขณะที่เราเพียงต้องการรหัสส่งคืน <<<ทำให้เชลล์ขยายคำถัดไปและใช้เป็นอินพุตของคำสั่งซึ่งเป็นเอกสารแบบบรรทัดเดียวที่<<นี่ (ฉันไม่แน่ใจว่านี่เป็นแบบมาตรฐานหรือแบบ Bashism)


7
พวกเขาถูกเรียกว่านี่คือสตริง (3.6.7)ฉันเชื่อว่ามันเป็นความจริง
alex.pilon

10
คุณสามารถใช้การทดแทนกระบวนการ if grep -q foo <(echo somefoothing); then
larsr

โปรดทราบว่าไม่echoสามารถทำสำเนาได้หากคุณผ่านตัวแปรให้ใช้printf '%s' "$stringแทน
nyuszika7h

5
ค่าใช้จ่ายของการนี้มีราคาแพงมาก: ทำgrep -q foo <<<"$mystring"implie 1 ส้อมและมีbashismและecho $mystring | grep -q fooimplie 2 งา (หนึ่งสำหรับท่อและครั้งที่สองสำหรับการเรียกใช้/path/to/grep)
เอฟ HAURI

1
@BrunoBronosky ที่echoไม่มีแฟล็กอาจยังมีปัญหาเรื่องการพกพาที่ไม่คาดคิดหากสตริงอาร์กิวเมนต์มีลำดับแบ็กสแลช echo "nope\c"คาดว่าในบางแพลตฟอร์มจะทำงานเหมือนecho -e "nope"อย่างอื่น printf '%s' "nope"vsprintf '%s\n' 'nope\c'
tripleee

92

คำตอบที่ได้รับการยอมรับนั้นดีที่สุด แต่เนื่องจากมีมากกว่าหนึ่งวิธีจึงเป็นวิธีแก้ไขปัญหาอื่น:

if [ "$string" != "${string/foo/}" ]; then
    echo "It's there!"
fi

${var/search/replace}อยู่$varกับอินสแตนซ์แรกของการsearchแทนที่ด้วยreplaceหากพบ (ไม่เปลี่ยนแปลง$var) หากคุณพยายามแทนที่fooโดยไม่มีอะไรและสตริงมีการเปลี่ยนแปลงแล้วfooก็พบว่าเห็นได้ชัด


5
โซลูชันของ ephemient ด้านบน:> `ถ้า [" $ string "! =" $ {string / foo /} "]; จากนั้นสะท้อนว่า "มันอยู่ที่นั่น!" fi` จะเป็นประโยชน์เมื่อใช้เปลือก BusyBox ของเถ้า โซลูชันที่ยอมรับไม่ทำงานกับ BusyBox เนื่องจากนิพจน์ทั่วไปของ bash บางอย่างไม่ได้ถูกนำไปใช้
TPoschel

1
ความไม่เท่าเทียมของความแตกต่าง คิดแปลก ๆ ! ฉันรักมัน
nitinr708

1
นอกเสียจากว่าสตริงของคุณจะ 'foo'
venimus

ฉันเขียนโซลูชันเดียวกันนี้เอง (เพราะล่ามของฉันจะไม่ตอบคำถามที่ดีที่สุด) จากนั้นไปหาดีกว่า แต่พบสิ่งนี้!
BuvinJ

56

ดังนั้นจึงมีคำตอบที่เป็นประโยชน์มากมายสำหรับคำถาม - แต่วิธีใดที่เร็วที่สุด / ใช้ทรัพยากรน้อยที่สุด?

การทดสอบซ้ำโดยใช้เฟรมนี้:

/usr/bin/time bash -c 'a=two;b=onetwothree; x=100000; while [ $x -gt 0 ]; do TEST ; x=$(($x-1)); done'

แทนที่ TEST ในแต่ละครั้ง:

[[ $b =~ $a ]]           2.92 user 0.06 system 0:02.99 elapsed 99% CPU

[ "${b/$a//}" = "$b" ]   3.16 user 0.07 system 0:03.25 elapsed 99% CPU

[[ $b == *$a* ]]         1.85 user 0.04 system 0:01.90 elapsed 99% CPU

case $b in *$a):;;esac   1.80 user 0.02 system 0:01.83 elapsed 99% CPU

doContain $a $b          4.27 user 0.11 system 0:04.41 elapsed 99%CPU

(doContain อยู่ในคำตอบของ F. Houri)

และสำหรับหัวเราะคิกคัก:

echo $b|grep -q $a       12.68 user 30.86 system 3:42.40 elapsed 19% CPU !ouch!

ดังนั้นตัวเลือกการเปลี่ยนตัวง่ายจะชนะได้อย่างแน่นอนไม่ว่าจะเป็นการทดสอบแบบขยายหรือกรณี กรณีเป็นแบบพกพา

การขุดออกไปถึง 100,000 greps นั้นเจ็บปวดอย่างคาดไม่ถึง! กฎเก่าเกี่ยวกับการใช้โปรแกรมอรรถประโยชน์ภายนอกโดยไม่จำเป็นต้องถือเป็นจริง


4
เกณฑ์มาตรฐานเรียบร้อย [[ $b == *$a* ]]เชื่อฉันจะใช้
นักฟิสิกส์บ้า

2
หากฉันอ่านสิ่งนี้ถูกต้องให้caseชนะด้วยการใช้เวลาโดยรวมน้อยที่สุด $b in *$aแม้ว่าคุณจะพลาดเครื่องหมายดอกจันหลังจากนั้น ฉันได้รับผลลัพธ์ที่เร็วกว่าเล็กน้อยเมื่อ[[ $b == *$a* ]]เทียบcaseกับการแก้ไขข้อบกพร่อง แต่อาจขึ้นอยู่กับปัจจัยอื่น ๆ ด้วยเช่นกัน
tripleee

1
ideone.com/5roEVtทำการทดสอบของฉันโดยแก้ไขข้อบกพร่องเพิ่มเติมบางอย่างและทดสอบสถานการณ์ที่แตกต่าง (ซึ่งสตริงนั้นไม่มีอยู่ในสตริงที่ยาวกว่า) ผลลัพธ์ส่วนใหญ่คล้ายกัน [[ $b == *$a* ]]รวดเร็วและcaseเกือบจะรวดเร็ว (และรองรับ POSIX ได้อย่างรื่นรมย์)
tripleee

28

สิ่งนี้ยังใช้งานได้:

if printf -- '%s' "$haystack" | egrep -q -- "$needle"
then
  printf "Found needle in haystack"
fi

และการทดสอบเชิงลบคือ:

if ! printf -- '%s' "$haystack" | egrep -q -- "$needle"
then
  echo "Did not find needle in haystack"
fi

ฉันคิดว่าสไตล์นี้ค่อนข้างคลาสสิกขึ้นอยู่กับคุณสมบัติของ Bash shell

--อาร์กิวเมนต์เป็นความหวาดระแวง POSIX บริสุทธิ์ที่ใช้ในการป้องกันการป้อนข้อมูลสตริงคล้ายกับตัวเลือกเช่นหรือ--abc-a

หมายเหตุ: ในห่วงคับรหัสนี้จะเป็นมากช้ากว่าการใช้คุณลักษณะเปลือกทุบตีภายในเป็นหนึ่ง (หรือสอง) กระบวนการแยกต่างหากจะถูกสร้างขึ้นและเชื่อมต่อผ่านทางท่อ


5
... แต่ OP ไม่ได้บอกว่าทุบตีเวอร์ชันไหน เช่นของ bash ที่เก่ากว่า (เช่น solaris มีบ่อย) อาจไม่รวมคุณลักษณะทุบตีใหม่กว่าเหล่านี้ (ผมเคยทำงานเป็นปัญหาตรงนี้ (จับคู่รูปแบบทุบตีไม่ได้ดำเนินการ) บน Solaris w / ทุบตี 2.0)
ไมเคิล

2
echoไม่สามารถส่งได้คุณควรใช้printf '%s' "$haystackแทน
nyuszika7h

2
Nope เพียงหลีกเลี่ยงechoโดยสิ้นเชิงสำหรับอะไร -แต่ข้อความตัวอักษรโดยไม่ต้องหนีออกมาที่ไม่ได้เริ่มต้นด้วย อาจใช้งานได้สำหรับคุณ แต่ไม่สามารถพกพาได้ แม้ bash's echoจะทำงานแตกต่างกันไปขึ้นอยู่กับว่าxpg_echoมีการตั้งค่าตัวเลือกไว้หรือไม่ PS:ฉันลืมปิดเครื่องหมายคำพูดคู่ในความคิดเห็นก่อนหน้าของฉัน
nyuszika7h

1
@kevinarpe ฉันไม่แน่ใจ--ไม่ได้ระบุไว้ในข้อมูลจำเพาะ POSIX สำหรับprintfแต่คุณควรใช้printf '%s' "$anything"ต่อไปเพื่อหลีกเลี่ยงปัญหาหาก$anythingมี%อักขระ
nyuszika7h

1
@kevinarpe จากนั้นอาจเป็น
nyuszika7h

21

Bash 4+ ตัวอย่าง หมายเหตุ: การไม่ใช้เครื่องหมายคำพูดจะทำให้เกิดปัญหาเมื่อคำต่างๆมีช่องว่าง ฯลฯ อ้างถึงใน Bash, IMO เสมอ

นี่คือตัวอย่างบางส่วนของ Bash 4+:

ตัวอย่างที่ 1 ตรวจสอบว่า 'ใช่' ในสตริง (ไม่คำนึงถึงขนาดตัวพิมพ์):

    if [[ "${str,,}" == *"yes"* ]] ;then

ตัวอย่างที่ 2 ตรวจสอบว่า 'ใช่' ในสตริง (ไม่คำนึงถึงขนาดตัวพิมพ์):

    if [[ "$(echo "$str" | tr '[:upper:]' '[:lower:]')" == *"yes"* ]] ;then

ตัวอย่างที่ 3 ตรวจสอบว่า 'ใช่' ในสตริง (ตรงตามตัวพิมพ์ใหญ่ - เล็ก):

     if [[ "${str}" == *"yes"* ]] ;then

ตัวอย่างที่ 4 ตรวจสอบว่า 'ใช่' ในสตริง (ตรงตามตัวพิมพ์ใหญ่ - เล็ก):

     if [[ "${str}" =~ "yes" ]] ;then

ตัวอย่างที่ 5 การจับคู่แบบตรงทั้งหมด (ตัวพิมพ์เล็กและใหญ่):

     if [[ "${str}" == "yes" ]] ;then

ตัวอย่างที่ 6 การจับคู่แบบตรงทั้งหมด (ตัวพิมพ์เล็กและตัวพิมพ์ใหญ่):

     if [[ "${str,,}" == "yes" ]] ;then

ตัวอย่างที่ 7 การจับคู่แบบตรงทั้งหมด:

     if [ "$a" = "$b" ] ;then

ตัวอย่างที่ 8 อักขระตัวแทนการจับคู่. text (ตัวพิมพ์เล็กและตัวพิมพ์ใหญ่):

     if echo "$a" | egrep -iq "\.(mp[3-4]|txt|css|jpg|png)" ; then

สนุก.


17

เกี่ยวกับสิ่งนี้:

text="   <tag>bmnmn</tag>  "
if [[ "$text" =~ "<tag>" ]]; then
   echo "matched"
else
   echo "not matched"
fi

2
= ~ สำหรับการจับคู่ regexp ดังนั้นจึงมีประสิทธิภาพเกินไปสำหรับวัตถุประสงค์ของ OP

16

ขณะที่พอลกล่าวถึงในการเปรียบเทียบประสิทธิภาพการทำงานของเขา

if echo "abcdefg" | grep -q "bcdef"; then
    echo "String contains is true."
else
    echo "String contains is not true."
fi

สิ่งนี้สอดคล้องกับ POSIX เช่น 'case "$ string" ใน' คำตอบที่ Marcusให้ไว้ แต่อ่านได้ง่ายกว่าคำตอบของ statement case เล็กน้อย โปรดทราบว่าสิ่งนี้จะช้ากว่าการใช้คำสั่ง case ดังที่เปาโลชี้ให้เห็นอย่าใช้มันเป็นวง



9
[[ $string == *foo* ]] && echo "It's there" || echo "Couldn't find"

ฉันจะเพิ่มว่าecho "Couldn't findคำสั่งในตอนท้ายเป็นเคล็ดลับที่ดีในการกลับสถานะทางออก 0 สำหรับคำสั่งจับคู่เหล่านี้
nicodjimenez

@nicodjimenez คุณไม่สามารถกำหนดเป้าหมายสถานะการออกได้อีกต่อไปด้วยโซลูชันนี้ สถานะการออกถูกกลืนหายไปกับข้อความสถานะ ...
Jahid

1
นั่นคือสิ่งที่ฉันหมายถึง ... หากคุณไม่มี|| echo "Couldn't find"คุณจะคืนสถานะข้อผิดพลาดออกหากไม่มีการจับคู่ซึ่งคุณอาจไม่ต้องการถ้าคุณกำลังเรียกใช้ไปป์ไลน์ CI เช่นที่คุณต้องการให้คำสั่งทั้งหมดกลับมา สถานะการออกจากข้อผิดพลาดที่ไม่ใช่
nicodjimenez

9

หนึ่งคือ:

[ $(expr $mystring : ".*${search}.*") -ne 0 ] && echo 'yes' ||  echo 'no'

2
exprเป็นหนึ่งในโปรแกรมอรรถประโยชน์มีดสวิสกองทัพที่มักจะทำสิ่งที่คุณต้องทำเมื่อคุณคิดว่าจะทำอย่างไร แต่เมื่อดำเนินการแล้วคุณไม่สามารถจำได้ว่าทำไมหรือวิธีการทำสิ่งที่มันทำดังนั้นคุณ อย่าแตะต้องมันอีกและหวังว่ามันจะไม่หยุดทำสิ่งที่กำลังทำอยู่
ไมเคิล

@AloisMahdal ฉันไม่เคยลงคะแนนเลยฉันแค่อ้างเหตุผลว่าทำไมได้รับการโหวต ความคิดเห็นเตือน ฉันใช้exprในบางโอกาสเมื่อการพกพาป้องกันการใช้ bash (เช่นพฤติกรรมที่ไม่สอดคล้องกันในเวอร์ชันเก่า), tr (ไม่สอดคล้องกันทุกที่) หรือ sed (บางครั้งช้าเกินไป) แต่จากประสบการณ์ส่วนตัวเมื่อใดก็ตามที่อ่านสิ่งมีexprชีวิตเหล่านี้อีกครั้งฉันต้องกลับไปที่หน้าคน ดังนั้นผมก็จะแสดงความคิดเห็นว่าการใช้งานของทุกexprจะแสดงความคิดเห็น ...
ไมเคิล

1
มีเวลาเมื่อคุณมีเปลือก Bourne เดิม มันขาดคุณสมบัติที่จำเป็นบางอย่างดังนั้นเครื่องมือเช่นนี้exprและtestถูกนำไปใช้เพื่อดำเนินการ ในยุคสมัยนี้มักจะมีเครื่องมือที่ดีกว่าหลาย ๆ ตัวสร้างอยู่ในกระสุนสมัยใหม่ ผมคิดว่าtestยังคงแขวนอยู่ในที่นั่น exprแต่ไม่มีใครดูเหมือนว่าจะหายไป
tripleee

6

ฉันชอบความสงบ

substr="foo"
nonsub="$(echo "$string" | sed "s/$substr//")"
hassub=0 ; [ "$string" != "$nonsub" ] && hassub=1

แก้ไขลอจิก:

  • ใช้ sed เพื่อลบอินสแตนซ์ของสตริงย่อยออกจากสตริง

  • หากสตริงใหม่แตกต่างจากสตริงเก่าสตริงย่อยจะมีอยู่


2
โปรดเพิ่มคำอธิบายบางอย่าง การให้เหตุผลพื้นฐานที่สำคัญกว่าเพียงแค่ให้รหัสเพราะช่วยให้ OP และผู้อ่านรายอื่นสามารถแก้ไขปัญหานี้และปัญหาที่คล้ายกันได้ด้วยตัวเอง
Zulan

5

grep -q มีประโยชน์สำหรับวัตถุประสงค์นี้

การใช้แบบเดียวกันawk:

string="unix-bash 2389"
character="@"
printf '%s' "$string" | awk -vc="$character" '{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }'

เอาท์พุท:

ไม่พบ

string="unix-bash 2389"
character="-"
printf '%s' "$string" | awk -vc="$character" '{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }'

เอาท์พุท:

พบ

แหล่งต้นฉบับ: http://unstableme.blogspot.com/2008/06/bash-search-letter-in-string-awk.html


3
echoไม่สามารถส่งได้คุณควรใช้printf '%s' "$string"แทน ฉันกำลังแก้ไขคำตอบเพราะผู้ใช้ไม่ปรากฏขึ้นอีกต่อไป
nyuszika7h

ในสิ่งที่วิธีคือechoไม่พกพา @ nyuszika7h?
starbeamrainbowlabs

5

ฉันพบว่าต้องการฟังก์ชั่นนี้ค่อนข้างบ่อยดังนั้นฉันจึงใช้ฟังก์ชั่นเชลล์ที่ทำเองที่บ้าน.bashrcซึ่งทำให้ฉันสามารถนำมันกลับมาใช้ใหม่ได้บ่อยเท่าที่ฉันต้องการด้วยชื่อที่จำง่าย:

function stringinstring()
{
    case "$2" in
       *"$1"*)
          return 0
       ;;
    esac
    return 1
}

เพื่อทดสอบว่า$string1(พูด, abc ) อยู่ใน$string2(พูด, 123abcABC ) ฉันแค่ต้องเรียกใช้stringinstring "$string1" "$string2"และตรวจสอบค่าตอบแทนเช่น

stringinstring "$str1" "$str2"  &&  echo YES  ||  echo NO

[["$ str" == $ substr ]] && echo YES || echo NO
elyase

ฉันรักแน่ใจว่าxสับจะต้องเฉพาะสำหรับมากเปลือกหอยเก่า
nyuszika7h

5

ไฟล์. bash_profileของฉันและวิธีการใช้ grep:

หากตัวแปรสภาพแวดล้อม PATH มีสองbinไดเรกทอรีของฉันอย่าผนวกเข้าด้วยกัน

# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

U=~/.local.bin:~/bin

if ! echo "$PATH" | grep -q "home"; then
    export PATH=$PATH:${U}
fi

1
นี่เป็นคำตอบใช่ไหม
codeforester

upvote ทำไมต้องเรียนรู้ว่า Bash ทำอย่างไรเมื่อ grep มีประสิทธิภาพมากกว่ามีแนวโน้มที่จะมีอยู่ grep -q -E 'pattern1|...|patternN'นอกจากนี้ยังขยายต่ออีกนิดด้วยการจับคู่กับรายการของรูปแบบ:
Mohamed Bana

4

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

วิธีนี้ใช้ได้กับอักขระพิเศษ:

# contains(string, substring)
#
# Returns 0 if the specified string contains the specified substring,
# otherwise returns 1.
contains() {
    string="$1"
    substring="$2"

    if echo "$string" | $(type -p ggrep grep | head -1) -F -- "$substring" >/dev/null; 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"

contains "abcd [efg] hij" "[efg]" && echo "abcd [efg] hij contains [efg]"
contains "abcd [efg] hij" "[effg]" || echo "abcd [efg] hij does not contain [effg]"

contains "abcd *efg* hij" "*efg*" && echo "abcd *efg* hij contains *efg*"
contains "abcd *efg* hij" "d *efg* h" && echo "abcd *efg* hij contains d *efg* h"
contains "abcd *efg* hij" "*effg*" || echo "abcd *efg* hij does not contain *effg*"

การทดสอบcontains "-n" "n"ไม่ทำงานที่นี่เพราะecho -nจะกลืน-nเป็นตัวเลือก! การแก้ไขที่เป็นที่นิยมสำหรับการที่จะใช้printf "%s\n" "$string"แทน
joeytwiddle

สตริงที่มีการแก้ไขอย่างสมบูรณ์แบบมีช่องว่าง
dengApro

4

เนื่องจากคำถามPOSIX / BusyBox ถูกปิดโดยไม่ต้องให้คำตอบที่ถูกต้อง (IMHO) ฉันจะโพสต์คำตอบที่นี่

คำตอบที่สั้นที่สุดคือ:

[ ${_string_##*$_substring_*} ] || echo Substring found!

หรือ

[ "${_string_##*$_substring_*}" ] || echo 'Substring found!'

โปรดทราบว่าการแฮชสองครั้งจะต้องมีเชลล์บางashตัว ด้านบนจะประเมิน[ stringvalue ]เมื่อไม่พบสตริงย่อย มันจะส่งกลับไม่มีข้อผิดพลาด [ ]เมื่อย่อยที่พบผลเป็นที่ว่างเปล่าและจะประเมิน นี้จะโยนรหัสข้อผิดพลาด 1 เนื่องจากสตริงจะถูกแทนที่อย่างสมบูรณ์ (เนื่องจาก*)

ไวยากรณ์ทั่วไปที่สั้นที่สุด:

[ -z "${_string_##*$_substring_*}" ] && echo 'Substring found!'

หรือ

[ -n "${_string_##*$_substring_*}" ] || echo 'Substring found!'

อีกหนึ่ง:

[ "${_string_##$_substring_}" != "$_string_" ] && echo 'Substring found!'

หรือ

[ "${_string_##$_substring_}" = "$_string_" ] || echo 'Substring found!'

หมายเหตุเครื่องหมายเท่ากับเดียว !


3

การจับคู่คำที่ตรงกัน:

string='My long string'
exactSearch='long'

if grep -E -q "\b${exactSearch}\b" <<<${string} >/dev/null 2>&1
  then
    echo "It's there"
  fi

3

ลอง oobash

มันเป็นไลบรารีสตริงสไตล์ OO สำหรับ Bash 4 มันมีการสนับสนุน umlauts เยอรมัน มันเขียนใน Bash

ฟังก์ชั่นจำนวนมากที่มี: -base64Decode, -base64Encode, -capitalize, -center, -charAt,-concat , -contains, -count, -endsWith, -equals, -equalsIgnoreCase, -reverse, -hashCode, -indexOf, -isAlnum, -isAlpha, -isAscii, -isDigit, -isEmpty, -isHexDigit, -isLowerCase, -isSpace, -isPrintable, -isUpperCase, -isVisible, -lastIndexOf, -length, -matches, -replaceAll, -replaceFirst, -startsWith, -substring, -swapCase, -toLowerCase, -toString, -toUpperCase, และ-trim-zfill

ดูตัวอย่างประกอบด้วย :

[Desktop]$ String a testXccc
[Desktop]$ a.contains tX
true
[Desktop]$ a.contains XtX
false

oobash สามารถใช้ได้ที่ Sourceforge.net


2

ฉันใช้ฟังก์ชั่นนี้ (ไม่ขึ้นอยู่กับการพึ่งพาใคร แต่ชัดเจน) มันผ่านการทดสอบที่แสดงด้านล่าง หากฟังก์ชันส่งคืนค่า> 0 ดังนั้นจะพบสตริง คุณสามารถส่งคืน 1 หรือ 0 ได้อย่างง่ายดายแทน

function str_instr {
   # Return position of ```str``` within ```string```.
   # >>> str_instr "str" "string"
   # str: String to search for.
   # string: String to search.
   typeset str string x
   # Behavior here is not the same in bash vs ksh unless we escape special characters.
   str="$(str_escape_special_characters "${1}")"
   string="${2}"
   x="${string%%$str*}"
   if [[ "${x}" != "${string}" ]]; then
      echo "${#x} + 1" | bc -l
   else
      echo 0
   fi
}

function test_str_instr {
   str_instr "(" "'foo@host (dev,web)'" | assert_eq 11
   str_instr ")" "'foo@host (dev,web)'" | assert_eq 19
   str_instr "[" "'foo@host [dev,web]'" | assert_eq 11
   str_instr "]" "'foo@host [dev,web]'" | assert_eq 19
   str_instr "a" "abc" | assert_eq 1
   str_instr "z" "abc" | assert_eq 0
   str_instr "Eggs" "Green Eggs And Ham" | assert_eq 7
   str_instr "a" "" | assert_eq 0
   str_instr "" "" | assert_eq 0
   str_instr " " "Green Eggs" | assert_eq 6
   str_instr " " " Green "  | assert_eq 1
}

ความสงสัย! อะไรคือการพึ่งพา?
Peter Mortensen

ฟังก์ชัน "str_escape_special_characters" มันอยู่ในไฟล์ GitHub arcshell_str.sh arcshell.io จะพาคุณไปที่นั่น
Ethan Post


0
msg="message"

function check {
    echo $msg | egrep [abc] 1> /dev/null

    if [ $? -ne 1 ];
    then 
        echo "found" 
    else 
        echo "not found" 
    fi
}

check

สิ่งนี้จะพบการเกิดขึ้นของ a หรือ b หรือ c


0

ตัวอย่างของกองหญ้าเข็มทั่วไปตามด้วยตัวแปร

#!/bin/bash

needle="a_needle"
haystack="a_needle another_needle a_third_needle"
if [[ $haystack == *"$needle"* ]]; then
    echo "needle found"
else
    echo "needle NOT found"
fi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.