Pertanyaan String berisi substring di Bash


Saya memiliki string di Bash:

string="My string"

Bagaimana saya bisa menguji apakah itu berisi string lain?

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

Dimana ?? adalah operator yang tidak saya kenal. Apakah saya menggunakan echo dan grep?

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

Itu terlihat agak canggung.


1742
2017-10-23 12:37


asal


Jawaban:


Kamu dapat memakai Jawaban Marcus (* wildcard) di luar pernyataan kasus, juga, jika Anda menggunakan tanda kurung ganda:

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

Perhatikan bahwa spasi dalam string jarum harus ditempatkan di antara tanda kutip ganda, dan * wildcard harus berada di luar.


2487
2017-10-23 12:55



Jika Anda lebih memilih pendekatan regex:

string='My string';

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

386
2017-10-23 20:10



Saya tidak yakin tentang menggunakan pernyataan if, tetapi Anda bisa mendapatkan efek yang sama dengan pernyataan kasus:

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

239
2017-10-23 12:47



Jawaban yang kompatibel

Karena sudah ada banyak jawaban menggunakan fitur khusus Bash, ada cara bekerja di bawah cangkang fitur yang lebih buruk, seperti :

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

Dalam prakteknya, ini bisa memberi:

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

Ini diuji di bawah , ,  dan  (busybox), dan hasilnya selalu:

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

Ke satu fungsi

Seperti yang diminta oleh @EeroAaltonen di sini adalah versi demo yang sama, diuji di bawah cangkang yang sama:

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
}

Kemudian:

$ 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'.

Melihat: Anda harus melarikan diri atau menggandakan tanda kutip dan / atau tanda kutip ganda:

$ 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'.

Fungsi sederhana

Ini diuji di bawah ,  dan tentu saja :

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

Itu semua orang!

Kemudian sekarang:

$ 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

... Atau jika string yang dikirimkan bisa kosong, seperti yang ditunjukkan oleh @Sjlver, fungsinya akan menjadi:

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

atau seperti yang disarankan oleh Komentar Adrian Günter, menghindari -o switche:

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

Dengan string kosong:

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

135
2017-12-08 23:06



Anda harus ingat bahwa shell scripting kurang dari bahasa dan lebih banyak kumpulan perintah. Secara naluriah Anda berpikir bahwa "bahasa" ini mengharuskan Anda untuk mengikuti if dengan [ atau a [[. Keduanya hanyalah perintah yang mengembalikan status keluar yang menunjukkan keberhasilan atau kegagalan (sama seperti setiap perintah lainnya). Untuk alasan itulah saya akan menggunakannya grep, dan bukan [ perintah.

Kerjakan saja:

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

Sekarang yang Anda pikirkan if sebagai pengujian status keluar dari perintah yang mengikutinya (lengkap dengan titik koma). Mengapa tidak mempertimbangkan kembali sumber string yang Anda uji?

## 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
#...

Itu -q pilihan membuat grep tidak menghasilkan apa-apa, karena kami hanya ingin kode kembali. <<< membuat shell memperluas kata berikutnya dan menggunakannya sebagai input ke perintah, versi satu baris dari << di sini dokumen (saya tidak yakin apakah ini standar atau bashism).


110
2017-10-27 14:57



Jawaban yang diterima adalah yang terbaik, tetapi karena ada lebih dari satu cara untuk melakukannya, inilah solusi lain:

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

${var/search/replace} aku s $var dengan contoh pertama search digantikan oleh replace, jika ditemukan (tidak berubah $var). Jika Anda mencoba untuk mengganti foo oleh apa-apa, dan string telah berubah, maka jelas foo ditemukan.


73
2017-10-23 14:35



Jadi ada banyak solusi yang berguna untuk pertanyaan - tetapi yang paling cepat / menggunakan sumber daya paling sedikit?

Tes berulang menggunakan bingkai ini:

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

Mengganti TES setiap kali:

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

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

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

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

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

(doContain ada di jawaban F. Houri)

Dan untuk cekikikan:

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

Jadi pilihan substituion sederhana predicatbly menang apakah dalam tes diperpanjang atau kasus. Casingnya portabel.

Perpipaan ke 100000 greps bisa ditebak menyakitkan! Aturan lama tentang penggunaan utilitas eksternal tanpa perlu berlaku.


31
2017-08-27 19:42



Ini juga berfungsi:

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

Dan tes negatifnya adalah:

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

Saya kira gaya ini sedikit lebih klasik - kurang bergantung pada fitur Bash shell.

Itu -- argumen paranoia POSIX murni, digunakan untuk melindungi terhadap string input yang mirip dengan opsi, seperti --abc atau -a.

Catatan: Dalam lingkaran yang ketat kode ini akan menjadi banyak lebih lambat daripada menggunakan fitur Bash shell internal, karena satu (atau dua) proses terpisah akan dibuat dan terhubung melalui pipa.


22
2017-12-01 15:41



Bagaimana dengan ini:

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

11
2018-02-09 06:15



Seperti yang disebutkan Paulus dalam perbandingan kinerjanya:

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

Ini POSIX compliant seperti 'kasus' $ string "dalam" jawaban yang disediakan oleh Marcus, tetapi sedikit lebih mudah dibaca daripada jawaban pernyataan kasus. Juga perhatikan bahwa ini akan jauh lebih lambat daripada menggunakan pernyataan kasus, seperti yang ditunjukkan oleh Paul, jangan menggunakannya dalam satu lingkaran.


8
2017-12-31 22:32