Làm thế nào để bạn biết nếu một chuỗi chứa một chuỗi khác trong POSIX sh?


135

Tôi muốn viết một kịch bản shell Unix sẽ thực hiện nhiều logic khác nhau nếu có một chuỗi bên trong một chuỗi khác. Ví dụ, nếu tôi đang ở trong một thư mục nhất định, hãy rẽ nhánh. Ai đó có thể vui lòng cho tôi biết làm thế nào để thực hiện điều này? Nếu có thể tôi muốn làm cho nó không phải là vỏ cụ thể (nghĩa là không chỉ bash) nhưng nếu không có cách nào khác tôi có thể làm với điều đó.

#!/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
Tôi nhận ra điều này đã cũ, nhưng đây là một vài điều cần lưu ý đối với khách truy cập trong tương lai: (1) Thông thường nên đặt trước các tên biến SNAKE_CASE cho các biến nội bộ và vỏ. (2) Cài đặt CURRENT_DIRlà dự phòng; bạn chỉ có thể sử dụng $PWD.
nyuszika7h

Câu trả lời:


161

Đây là một giải pháp khác. Điều này sử dụng mở rộng tham số chuỗi con POSIX , do đó, nó hoạt động trong Bash, Dash, KornShell (ksh), Z shell (zsh), v.v.

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

Một phiên bản chức năng với một số ví dụ:

# 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
Điều này không hoạt động đối với tôi nếu chuỗi con chứa dấu gạch chéo ngược. Như thường lệ, substring="$( printf '%q' "$2" )"tiết kiệm trong ngày.
Egor Tensin

Điều này phù hợp với chất nền quá. chuỗi = "aplha beta beta" chuỗi con = "beta". nó phù hợp với cả betaone một dbeta, tôi nghĩ là sai.
rajeev

1
Còn việc sử dụng ký tự đại diện thì sao? [[ $haystack == *"My needle"* ]]
Pablo A

3
Điều sai lầm là dấu ngoặc kép không phải là POSIX, đó là tiền đề của câu hỏi, @Pablo.
Rob Kennedy

1
Điều này không hoạt động với các ký tự đặc biệt như []. Xem câu trả lời của tôi stackoverflow.com/a/54490453/712666 .
Alex Skrypnyk

84

Vỏ POSIX nguyên chất:

#!/bin/sh
CURRENT_DIR=`pwd`

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

Các shell mở rộng như ksh hoặc bash có cơ chế kết hợp lạ mắt, nhưng kiểu cũ casemạnh mẽ đáng ngạc nhiên.


40

Đáng buồn thay, tôi không biết một cách để làm điều này trong sh. Tuy nhiên, bằng cách sử dụng bash (bắt đầu từ phiên bản 3.0.0, có lẽ là những gì bạn có), bạn có thể sử dụng toán tử = ~ như thế này:

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

Là một phần thưởng bổ sung (và / hoặc cảnh báo, nếu chuỗi của bạn có bất kỳ ký tự vui nhộn nào trong đó), = ~ chấp nhận biểu thức chính là toán hạng đúng nếu bạn bỏ dấu ngoặc kép.


3
Đừng trích dẫn regex, hoặc nó sẽ không hoạt động nói chung. Ví dụ, thử [[ test =~ "test.*" ]]so với [[ test =~ test.* ]].
l0b0

1
Chà, nó sẽ hoạt động tốt nếu bạn đang kiểm tra một chuỗi con, như trong câu hỏi ban đầu, nhưng nó sẽ không coi toán hạng đúng là một biểu thức chính quy. Tôi sẽ cập nhật câu trả lời của tôi để làm cho nó rõ ràng hơn.
John Hyland

3
Đây là Bash, không phải POSIX sh như câu hỏi.
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

2
Thay đổi grep -q "$2"để grep -q "$2" > /dev/nulltránh đầu ra không mong muốn.
Victor Sergienko

15

Đây là một liên kết đến các giải pháp khác nhau của vấn đề của bạn.

Đây là sở thích của tôi vì nó có ý nghĩa dễ đọc nhất đối với con người:

Phương pháp ký tự sao

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

Trên shtôi đã nhận được "toán hạng chưa biết" với điều này. Làm việc với Bash mặc dù.
halfer

13
[[không phải là 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


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

-> đầu ra: Chuỗi 1 chứa chuỗi 2


2

Xem trang hướng dẫn cho chương trình 'kiểm tra'. Nếu bạn chỉ đang kiểm tra sự tồn tại của một thư mục, bạn thường sẽ làm một cái gì đó như vậy:

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

Nếu bạn thực sự đang cố gắng khớp chuỗi, bạn cũng có thể sử dụng quy tắc mở rộng bash & ký tự đại diện:

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

Nếu bạn cần làm một cái gì đó phức tạp hơn, bạn sẽ cần sử dụng một chương trình khác như expr.


1
Dường như có một trích dẫn kép (") trong ví dụ thứ hai của bạn.
Alexis Wilke

2

Trong trường hợp đặc biệt mà bạn muốn tìm xem một từ có được chứa trong một văn bản dài hay không, bạn có thể lặp qua văn bản dài bằng một vòng lặp.

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

Đây là vỏ Bourne nguyên chất.


1

Nếu bạn muốn một phương thức chỉ ksh nhanh như "test", bạn có thể làm một cái gì đó như:

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

Nó hoạt động bằng cách xóa kim trong đống cỏ khô và sau đó so sánh độ dài chuỗi của đống cỏ cũ và mới.


1
Sẽ không thể trả lại kết quả test? Như trong return [ ${#haystack} -eq ${#1} ]?
Alexis Wilke

Vâng, đó là chính xác. Tôi sẽ để nó như thế này vì nó dễ hiểu hơn cho giáo dân. Nếu bạn sử dụng mã của mình, hãy sử dụng phương pháp @AlexisWilke.
JoeOfTex
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.