Trong Bash, làm thế nào tôi có thể kiểm tra nếu một chuỗi bắt đầu với một số giá trị?


745

Tôi muốn kiểm tra xem một chuỗi có bắt đầu bằng "nút" hay không, ví dụ "node001". Cái gì đó như

if [ $HOST == user* ]
  then
  echo yes
fi

Làm thế nào tôi có thể làm điều đó một cách chính xác?


Tôi cần thêm kết hợp các biểu thức để kiểm tra xem HOST có phải là "user1" hay bắt đầu bằng "nút"

if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi

> > > -bash: [: too many arguments

Làm thế nào tôi có thể làm điều đó một cách chính xác?


7
Đừng quá cám dỗ để kết hợp các biểu thức. Nó có thể trông xấu hơn khi có hai điều kiện riêng biệt, mặc dù bạn có thể đưa ra các thông báo lỗi tốt hơn và làm cho tập lệnh của bạn dễ gỡ lỗi hơn. Ngoài ra tôi sẽ tránh các tính năng bash. Công tắc là con đường để đi.
Hendry

Câu trả lời:


1073

Đoạn trích trên Hướng dẫn viết kịch bản Bash nâng cao nói:

# The == comparison operator behaves differently within a double-brackets
# test than within single brackets.

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

Vì vậy, bạn đã có nó gần như chính xác; bạn cần dấu ngoặc kép , không phải dấu ngoặc đơn.


Liên quan đến câu hỏi thứ hai của bạn, bạn có thể viết nó theo cách này:

HOST=user1
if  [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
    echo yes1
fi

HOST=node001
if [[ $HOST == user1 ]] || [[ $HOST == node* ]] ;
then
    echo yes2
fi

Mà sẽ vang

yes1
yes2

ifCú pháp của Bash khó làm quen với (IMO).


12
Đối với regex, ý bạn là [[$ a = ~ ^ z. *]]?
JStrahl

3
Vì vậy, có một sự khác biệt chức năng giữa [[ $a == z* ]][[ $a == "z*" ]]? Nói cách khác: họ làm việc khác nhau? Và cụ thể bạn có ý gì khi bạn nói "$ a bằng z *"?
Niels Bom

5
Bạn không cần dấu phân cách câu lệnh ";" nếu bạn đặt "sau đó" trên dòng riêng của mình
Yaza

6
Chỉ để hoàn chỉnh: Để kiểm tra xem một chuỗi [[ $a == *com ]]
KẾT THÚC

4
ABS là một sự lựa chọn đáng tiếc của các tài liệu tham khảo - đó là rất nhiều W3Schools bash, với đầy đủ nội dung lỗi thời và các ví dụ thực tiễn xấu; kênh #bash freenode đã cố gắng ngăn cản việc sử dụng nó ít nhất là kể từ năm 2008 . Bất kỳ cơ hội để bổ nhiệm tại BashFAQ # 31 ? (Tôi cũng đã đề xuất wiki của Bash-Hackers, nhưng bây giờ nó đã bị hỏng một chút).
Charles Duffy

207

Nếu bạn đang sử dụng phiên bản Bash gần đây (v3 +), tôi đề xuất toán tử so sánh regex Bash =~, ví dụ:

if [[ "$HOST" =~ ^user.* ]]; then
    echo "yes"
fi

Để khớp this or thattrong regex, sử dụng |, ví dụ,

if [[ "$HOST" =~ ^user.*|^host1 ]]; then
    echo "yes"
fi

Lưu ý - đây là cú pháp biểu thức chính quy 'phù hợp'.

  • user*có nghĩa là usevà không có nhiều lần xuất hiện của r, vì vậy useuserrrrsẽ khớp.
  • user.*phương tiện uservà số lần xuất hiện từ 0 trở lên của bất kỳ ký tự nào, do đó user1, userXsẽ khớp.
  • ^user.*có nghĩa là khớp với mẫu user.*ở đầu $ HOST.

Nếu bạn không quen với cú pháp biểu thức chính quy, hãy thử tham khảo tài nguyên này .


Cảm ơn, Brabster! Tôi đã thêm vào bài viết gốc một câu hỏi mới về cách kết hợp các biểu thức trong nếu cluase.
Tim

2
Thật đáng tiếc khi câu trả lời được chấp nhận không nói gì về cú pháp của biểu thức chính quy.
CarlosRos

20
FYI =~toán tử Bash chỉ thực hiện khớp biểu thức chính quy khi phía bên tay phải KHÔNG BẮT BUỘC. Nếu bạn trích dẫn phía bên tay phải "Bất kỳ phần nào của mẫu có thể được trích dẫn để buộc nó phải được khớp như một chuỗi." (1.) đảm bảo luôn đặt các biểu thức chính quy ở bên phải không được trích dẫn và (2.) nếu bạn lưu trữ biểu thức chính quy của mình trong một biến, đảm bảo KHÔNG trích dẫn phía bên phải khi bạn thực hiện mở rộng tham số.
Trevor Boyd Smith

145

Tôi luôn cố gắng gắn bó với POSIX shthay vì sử dụng các phần mở rộng Bash, vì một trong những điểm chính của kịch bản là tính di động (bên cạnh việc kết nối các chương trình, không thay thế chúng).

Trong shđó, có một cách dễ dàng để kiểm tra điều kiện "tiền tố".

case $HOST in node*)
    # Your code here
esac

Dựa vào độ tuổi, arcane và shufty sh (và Bash không phải là thuốc chữa bệnh: Nó phức tạp hơn, ít nhất quán hơn và ít di động hơn), tôi muốn chỉ ra một khía cạnh chức năng rất hay: Trong khi một số yếu tố cú pháp như caseđược tích hợp sẵn , các cấu trúc kết quả không khác gì bất kỳ công việc nào khác. Chúng có thể được sáng tác theo cùng một cách:

if case $HOST in node*) true;; *) false;; esac; then
    # Your code here
fi

Hoặc thậm chí ngắn hơn

if case $HOST in node*) ;; *) false;; esac; then
    # Your code here
fi

Hoặc thậm chí ngắn hơn (chỉ để trình bày !như một yếu tố ngôn ngữ - nhưng bây giờ đây là phong cách xấu)

if ! case $HOST in node*) false;; esac; then
    # Your code here
fi

Nếu bạn thích rõ ràng, hãy xây dựng yếu tố ngôn ngữ của riêng bạn:

beginswith() { case $2 in "$1"*) true;; *) false;; esac; }

Điều này thực sự không đẹp sao?

if beginswith node "$HOST"; then
    # Your code here
fi

Và vì shvề cơ bản chỉ là các công việc và danh sách chuỗi (và các quy trình nội bộ, trong đó các công việc được tạo ra), bây giờ chúng ta thậm chí có thể thực hiện một số lập trình chức năng nhẹ:

beginswith() { case $2 in "$1"*) true;; *) false;; esac; }
checkresult() { if [ $? = 0 ]; then echo TRUE; else echo FALSE; fi; }

all() {
    test=$1; shift
    for i in "$@"; do
        $test "$i" || return
    done
}

all "beginswith x" x xy xyz ; checkresult  # Prints TRUE
all "beginswith x" x xy abc ; checkresult  # Prints FALSE

Đây là thanh lịch. Không phải là tôi ủng hộ việc sử dụng shcho bất cứ điều gì nghiêm trọng - nó phá vỡ tất cả quá nhanh theo yêu cầu của thế giới thực (không có lambdas, vì vậy chúng tôi phải sử dụng chuỗi. Nhưng không thể gọi chức năng lồng với chuỗi, v.v.)


12
+1 Không chỉ có tính di động này, nó còn có thể đọc được, thành ngữ và thanh lịch (đối với tập lệnh shell). Nó cũng mở rộng tự nhiên đến nhiều mẫu; case $HOST in user01 | node* ) ...
tripleee

Có một tên cho loại định dạng mã này? if case $HOST in node*) true;; *) false;; esac; then Tôi đã nhìn thấy nó ở đây và ở đó, trong mắt tôi, nó trông có vẻ hơi khó chịu.
Niels Bom

@NielsBom Tôi không biết chính xác ý bạn là gì khi định dạng, nhưng quan điểm của tôi là mã shell rất có thể ghép được . Các caselệnh Becaues là các lệnh, chúng có thể đi vào bên trong if ... then.
Jo So

Tôi thậm chí không hiểu tại sao nó có thể ghép được, tôi không hiểu đủ kịch bản shell cho điều đó :-) Câu hỏi của tôi là về cách mã này sử dụng dấu ngoặc đơn không khớp và dấu chấm phẩy kép. Nó không giống bất kỳ kịch bản shell nào tôi từng thấy trước đây, nhưng tôi có thể được sử dụng để xem tập lệnh bash nhiều hơn tập lệnh sh để có thể là nó.
Niels Bom

Lưu ý: Nó phải là beginswith() { case "$2" in "$1"*) true;; *) false;; esac; }khác nếu $1có một nghĩa đen *hoặc ?nó có thể trả lời sai.
LLFishing

80

Bạn có thể chọn chỉ một phần của chuỗi bạn muốn kiểm tra:

if [ "${HOST:0:4}" = user ]

Đối với câu hỏi tiếp theo của bạn, bạn có thể sử dụng OR :

if [[ "$HOST" == user1 || "$HOST" == node* ]]

8
Bạn nên doublequote${HOST:0:4}
Jo So

@Jo Vậy: lý do là gì?
Peter Mortensen

@PeterMortensen, hãy thửHOST='a b'; if [ ${HOST:0:4} = user ] ; then echo YES ; fi
Jo So

60

Tôi thích các phương pháp khác đã được đăng, nhưng một số người thích sử dụng:

case "$HOST" in 
    user1|node*) 
            echo "yes";;
        *)
            echo "no";;
esac

Biên tập:

Tôi đã thêm người thay thế của bạn vào tuyên bố trường hợp ở trên

Trong phiên bản chỉnh sửa của bạn, bạn có quá nhiều dấu ngoặc. Nó sẽ giống như thế này:

if [[ $HOST == user1 || $HOST == node* ]];

Cảm ơn, Dennis! Tôi đã thêm vào bài viết gốc một câu hỏi mới về cách kết hợp các biểu thức trong nếu cluase.
Tim

11
"Một số người thích ...": cái này dễ mang theo hơn các phiên bản và vỏ.
carlosayam

Với các câu lệnh tình huống, bạn có thể bỏ qua các dấu ngoặc kép xung quanh biến vì không có sự phân tách từ nào xảy ra. Tôi biết nó vô nghĩa và không nhất quán nhưng tôi thích bỏ đi những trích dẫn ở đó để làm cho nó trở nên hấp dẫn hơn ở địa phương.
Jo So

Và trong trường hợp của tôi, tôi đã phải bỏ đi các trích dẫn trước đó): "/ *") không hoạt động, / *) đã làm. (Tôi đang tìm kiếm các chuỗi bắt đầu bằng /, tức là các đường dẫn tuyệt đối)
Josiah Yoder

36

Trong khi tôi thấy hầu hết các câu trả lời ở đây khá chính xác, nhiều trong số chúng có chứa Bashism không cần thiết. Mở rộng tham số POSIX cung cấp cho bạn tất cả những gì bạn cần:

[ "${host#user}" != "${host}" ]

[ "${host#node}" != "${host}" ]

${var#expr}tước tiền tố nhỏ nhất phù hợp exprtừ ${var}và trả về đó. Do đó nếu ${host}không không bắt đầu với user( node), ${host#user}( ${host#node}) là giống như ${host}.

exprcho phép fnmatch()ký tự đại diện, do đó ${host#node??}và bạn bè cũng làm việc.


2
Tôi cho rằng bashism [[ $host == user* ]]có thể là cần thiết, vì nó dễ đọc hơn nhiều [ "${host#user}" != "${host}" ]. Như đã cho rằng bạn kiểm soát môi trường nơi tập lệnh được thực thi (nhắm mục tiêu các phiên bản mới nhất bash), thì phiên bản trước là thích hợp hơn.
x-yuri

2
@ x-yuri Thành thật mà nói, tôi chỉ đơn giản là gói nó vào một has_prefix()chức năng và không bao giờ nhìn lại nó nữa.
dhke

30

#có ý nghĩa trong Bash, tôi đã đi đến giải pháp sau đây.

Ngoài ra, tôi thích tốt hơn để đóng gói các chuỗi với "" để vượt qua khoảng trắng, v.v.

A="#sdfs"
if [[ "$A" == "#"* ]];then
    echo "Skip comment line"
fi

Đây chính xác là những gì tôi cần. Cảm ơn!
Ionică Bizău

cảm ơn, tôi cũng đã tự hỏi làm thế nào để phù hợp với một chuỗi bắt đầu với blah:, có vẻ như đây là câu trả lời!
Anentropic

12

Thêm một chi tiết cú pháp nhỏ hơn một chút vào câu trả lời xếp hạng cao nhất của Mark Rushakoff.

Cách diễn đạt

$HOST == node*

Cũng có thể được viết là

$HOST == "node"*

Hiệu quả là như nhau. Chỉ cần chắc chắn rằng ký tự đại diện nằm ngoài văn bản được trích dẫn. Nếu ký tự đại diện nằm trong dấu ngoặc kép, nó sẽ được hiểu theo nghĩa đen (nghĩa là không phải là ký tự đại diện).


8

@OP, cho cả hai câu hỏi của bạn, bạn có thể sử dụng case / esac:

string="node001"
case "$string" in
  node*) echo "found";;
  * ) echo "no node";;
esac

Câu hỏi thứ hai

case "$HOST" in
 node*) echo "ok";;
 user) echo "ok";;
esac

case "$HOST" in
 node*|user) echo "ok";;
esac

Hoặc Bash 4.0

case "$HOST" in
 user) ;&
 node*) echo "ok";;
esac

Lưu ý rằng ;&chỉ có sẵn trong Bash> = 4.
Tạm dừng cho đến khi có thông báo mới.

6
if [ [[ $HOST == user1 ]] -o [[ $HOST == node* ]] ];
then
echo yes
fi

không làm việc, bởi vì tất cả [, [[testnhận ra cùng một ngữ pháp không đệ quy. Xem phần GIẢI THÍCH ĐIỀU KIỆN trên trang người đàn ông Bash của bạn.

Như một bên, SUSv3 nói

Lệnh điều kiện có nguồn gốc KornShell (dấu ngoặc kép [[]] ) đã bị xóa khỏi mô tả ngôn ngữ lệnh shell trong một đề xuất sớm. Những phản đối đã được nêu ra rằng vấn đề thực sự là sử dụng sai lệnh kiểm tra ( [ ) và đưa nó vào trình bao là cách sai để khắc phục sự cố. Thay vào đó, tài liệu thích hợp và một từ dành riêng shell mới ( ! ) Là đủ.

Kiểm tra đòi hỏi nhiều thử nghiệm hoạt động có thể được thực hiện ở cấp vỏ sử dụng lời gọi cá nhân của các thử nghiệm chỉ huy và vỏ logicals, thay vì sử dụng dễ bị lỗi -o cờ của thử nghiệm .

Bạn cần viết nó theo cách này, nhưng kiểm tra không hỗ trợ nó:

if [ $HOST == user1 -o $HOST == node* ];
then
echo yes
fi

kiểm tra sử dụng = cho sự bình đẳng chuỗi và quan trọng hơn là nó không hỗ trợ khớp mẫu.

case/ esaccó hỗ trợ tốt cho khớp mẫu:

case $HOST in
user1|node*) echo yes ;;
esac

Nó có thêm lợi ích mà nó không phụ thuộc vào Bash và cú pháp có thể mang theo được. Từ đặc tả Unix đơn , Ngôn ngữ lệnh Shell :

case word in
    [(]pattern1) compound-list;;
    [[(]pattern[ | pattern] ... ) compound-list;;] ...
    [[(]pattern[ | pattern] ... ) compound-list]
esac

1
[testđược xây dựng Bash cũng như các chương trình bên ngoài. Hãy thử type -a [.
Tạm dừng cho đến khi có thông báo mới.

Rất cám ơn vì đã giải thích các vấn đề với "hợp chất", @just ai đó - đã tìm kiếm chính xác cho một cái gì đó như thế! Chúc mừng! Lưu ý PS (không liên quan đến OP) : if [ -z $aa -or -z $bb ]; ... đưa ra " bash: [: -or: toán tử nhị phân dự kiến "; Tuy nhiên, if [ -z "$aa" -o -z "$bb" ] ; ...vượt qua.
sdaau

2

grep

Quên hiệu năng, đây là POSIX và trông đẹp hơn casecác giải pháp:

mystr="abcd"
if printf '%s' "$mystr" | grep -Eq '^ab'; then
  echo matches
fi

Giải trình:


2

Tôi đã điều chỉnh câu trả lời của @ markrushakoff để biến nó thành một chức năng có thể gọi được:

function yesNo {
  # Prompts user with $1, returns true if response starts with y or Y or is empty string
  read -e -p "
$1 [Y/n] " YN

  [[ "$YN" == y* || "$YN" == Y* || "$YN" == "" ]]
}

Sử dụng nó như thế này:

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] y
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] Y
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] yes
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n]
true

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] n
false

$ if yesNo "asfd"; then echo "true"; else echo "false"; fi

asfd [Y/n] ddddd
false

Đây là một phiên bản phức tạp hơn cung cấp một giá trị mặc định được chỉ định:

function toLowerCase {
  echo "$1" | tr '[:upper:]' '[:lower:]'
}

function yesNo {
  # $1: user prompt
  # $2: default value (assumed to be Y if not specified)
  # Prompts user with $1, using default value of $2, returns true if response starts with y or Y or is empty string

  local DEFAULT=yes
  if [ "$2" ]; then local DEFAULT="$( toLowerCase "$2" )"; fi
  if [[ "$DEFAULT" == y* ]]; then
    local PROMPT="[Y/n]"
  else
    local PROMPT="[y/N]"
  fi
  read -e -p "
$1 $PROMPT " YN

  YN="$( toLowerCase "$YN" )"
  { [ "$YN" == "" ] && [[ "$PROMPT" = *Y* ]]; } || [[ "$YN" = y* ]]
}

Sử dụng nó như thế này:

$ if yesNo "asfd" n; then echo "true"; else echo "false"; fi

asfd [y/N]
false

$ if yesNo "asfd" n; then echo "true"; else echo "false"; fi

asfd [y/N] y
true

$ if yesNo "asfd" y; then echo "true"; else echo "false"; fi

asfd [Y/n] n
false

-5

Một điều khác bạn có thể làm là cattìm ra những gì bạn đang lặp lại và đường ống vớiinline cut -c 1-1

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.