Bagaimana cara menentukan apakah suatu string adalah substring dari yang lain di bash?

49

Saya ingin melihat apakah sebuah string berada di dalam bagian dari string lain.
misalnya:

'ab' in 'abc' -> true
'ab' in 'bcd' -> false

Bagaimana saya bisa melakukan ini dalam kondisi script bash?

Lucio
sumber

Jawaban:

27

Anda dapat menggunakan formulir di ${VAR/subs}mana VARberisi string yang lebih besar dan subsmerupakan substring yang Anda coba temukan:

my_string=abc
substring=ab
if [ "${my_string/$substring}" = "$my_string" ] ; then
  echo "${substring} is not in ${my_string}"
else
  echo "${substring} was found in ${my_string}"
fi

Ini berfungsi karena ${VAR/subs}sama dengan $VARtetapi dengan kemunculan pertama dari string yang subsdihapus, khususnya jika $VARtidak mengandung kata subsitu tidak akan dimodifikasi.

edwin
sumber
Saya pikir Anda harus mengubah urutan echopernyataan. Karena saya mendapatkanab is not in abc
Lucio
Kamu benar! : P
edwin
Mmm .. Tidak, skripnya salah. Seperti yang saya dapatkan ab was found in abc, tetapi jika saya menggunakan substring=zsaya dapatkanz was found in abc
Lucio
1
Sekarang saya mengerti ab is not in abc. Tapi z was found in abc. Ini lucu: D
Lucio
1
Duh! Gema tepat di awal ini! XD
edwin
47

[[ "bcd" =~ "ab" ]]
[[ "abc" =~ "ab" ]]

kurung untuk tes, dan karena kurung ganda, bisa jadi beberapa tes tambahan suka =~.

Jadi Anda bisa menggunakan formulir ini seperti

var1="ab"
var2="bcd"
if [[ "$var2" =~ "$var1" ]]; then
    echo "pass"
else
    echo "fail"
fi

Edit: dikoreksi "= ~", telah terbalik.

pendiam
sumber
1
Saya dapatkan faildengan parameter ini:var2="abcd"
Lucio
3
@ Lucio Yang benar adalah [[ $string =~ $substring ]]. Saya memperbarui jawabannya.
Eric Carvalho
12

Menggunakan pola nama file bash ( pola alias "glob")

substr=ab
[[ abc == *"$substr"* ]] && echo yes || echo no    # yes
[[ bcd == *"$substr"* ]] && echo yes || echo no    # no
glenn jackman
sumber
if [["$ JAVA_OPTS"! = "-XX: + UseCompressedOops" ]]; lalu ekspor JAVA_OPTS = "$ JAVA_OPTS -XX: + UseCompressedOops"; fi
Mike Slinn
10

Dua pendekatan berikut ini akan bekerja pada lingkungan yang kompatibel dengan POSIX, tidak hanya di bash:

substr=ab
for s in abc bcd; do
    if case ${s} in *"${substr}"*) true;; *) false;; esac; then
        printf %s\\n "'${s}' contains '${substr}'"
    else
        printf %s\\n "'${s}' does not contain '${substr}'"
    fi
done
substr=ab
for s in abc bcd; do
    if printf %s\\n "${s}" | grep -qF "${substr}"; then
        printf %s\\n "'${s}' contains '${substr}'"
    else
        printf %s\\n "'${s}' does not contain '${substr}'"
    fi
done

Kedua output di atas:

'abc' contains 'ab'
'bcd' does not contain 'ab'

Yang pertama memiliki keuntungan tidak menelurkan grepproses yang terpisah .

Perhatikan bahwa saya menggunakan printf %s\\n "${foo}"bukan echo "${foo}"karena echomungkin memotong ${foo}jika berisi garis miring terbalik.

Richard Hansen
sumber
Versi pertama berfungsi dengan baik untuk menemukan substring nama monitor dalam daftar xrandrnama monitor yang disimpan dalam variabel. +1 dan selamat datang di klub rep 1K :)
WinEunuuchs2Unix
6

pernyataan kasus shell

Ini adalah solusi paling portabel, akan bekerja bahkan pada cangkang Bourne lama dan cangkang Korn

#!/bin/bash
case "abcd" in
    *$1*) echo "It's a substring" ;;
    *) echo "Not a substring" ;;
esac

Contoh dijalankan:

$ ./case_substr.sh "ab"                                                                                           
It's a substring
$ ./case_substr.sh "whatever"                                                                                     
Not a substring

Perhatikan bahwa Anda tidak harus secara khusus menggunakan echoAnda dapat menggunakan exit 1dan exit 0untuk menandakan keberhasilan atau kegagalan.

Apa yang bisa kita lakukan juga, adalah membuat fungsi (yang dapat digunakan dalam skrip besar jika perlu) dengan nilai pengembalian spesifik (0 pada pertandingan, 1 pada tidak cocok):

$ ./substring_function.sh                                  
ab is substring

$ cat substring_function.sh                                
#!/bin/sh

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

main(){
   if is_substring "ab" "abcdefg"
   then
       echo "ab is substring"
   fi
}

main $@

grep

$ grep -q 'ab' <<< "abcd" && echo "it's a substring" || echo "not a substring"                                    
it's a substring

Pendekatan khusus ini berguna dengan pernyataan if-else di bash. Sebagian besar juga portabel

AWK

$ awk '$0~/ab/{print "it is a substring"}' <<< "abcd"                                                             
it is a substring

Python

$ python -c 'import sys;sys.stdout.write("it is a substring") if "ab" in sys.stdin.read() else exit(1)' <<< "abcd"
it is a substring

Rubi

$ ruby -e ' puts "is substring" if  ARGV[1].include? ARGV[0]'  "ab" "abcdef"                                             
is substring
Sergiy Kolodyazhnyy
sumber
+1 untuk melampaui semua orang. Saya perhatikan di sini dan di situs pertukaran stack lainnya tidak ada jawaban mengembalikan offset substring dalam string. Yang merupakan misi malam ini :)
WinEunuuchs2Unix
@ WinEunuuchs2Unix Akan melakukan itu di bash?
Sergiy Kolodyazhnyy
Ya dan Tidak. Saya sedang melakukan proyek Frankenstein di mana python mendapatkan semua metadata pesan gmail.com dan mem-bash menguraikannya dan menyajikan daftar GUI dengan menelusuri. Saya menemukan jawabannya di sini: stackoverflow.com/questions/5031764/…
WinEunuuchs2Unix
@ WinEunuuchs2Unix OK. Kedengarannya menarik. Saya pribadi lebih suka mengurai semuanya dengan Python. Ini memiliki jauh lebih banyak kemampuan untuk pemrosesan teks daripada bash saja.
Sergiy Kolodyazhnyy
Saya tahu preferensi Anda selama sekitar dua tahun dan saya menghargainya. Tapi saya hanya belajar Python dan membuat yad untuk bekerja di dalamnya sepertinya tidak praktis bagi saya. Belum lagi semua pemrosesan array yang saya sudah nyaman dengan di bash. Tapi setidaknya saya menulis skrip python pertama saya untuk menyedot semuanya dari google gmail.com ke dalam file flat Linux kan? :)
WinEunuuchs2Unix
5

Pikirkan [[dan ":

[[ $a == z* ]]   # True if $a starts with an "z" (pattern matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).

[ $a == z* ]     # File globbing and word splitting take place.
[ "$a" == "z*" ] # True if $a is equal to z* (literal matching).

Jadi seperti yang dikatakan @glenn_jackman, tetapi ingatlah bahwa jika Anda membungkus seluruh istilah kedua dalam tanda kutip ganda, itu akan mengalihkan tes ke pencocokan literal .

Sumber: http://tldp.org/LDP/abs/html/comparison-ops.html

Campa
sumber
4

Mirip dengan jawaban edwin, tetapi dengan peningkatan portabilitas untuk posix & ksh, dan sentuhan yang tidak terlalu berisik daripada jawaban Richard:

substring=ab

string=abc
if [ "$string" != "${string%$substring*}" ]; then
    echo "$substring IS in $string"
else
    echo "$substring is NOT in $string"
fi

string=bcd
if [ "$string" != "${string%$substring*}" ]; then
    echo "$string contains $substring"
else
    echo "$string does NOT contain $substring"
fi

Keluaran:

abc contains ab
bcd does NOT contain ab
Laubster
sumber