Làm cách nào để kiểm tra nếu một biến là một số trong Bash?


599

Tôi chỉ không thể hiểu làm thế nào để đảm bảo một đối số được truyền cho tập lệnh của tôi có phải là số hay không.

Tất cả những gì tôi muốn làm là một cái gì đó như thế này:

test *isnumber* $1 && VAR=$1 || echo "need a number"

Có ai giúp đỡ không?


17
Bên cạnh đó - test && echo "foo" && exit 0 || echo "bar" && exit 1cách tiếp cận bạn đang sử dụng có thể có một số tác dụng phụ ngoài ý muốn - nếu tiếng vang không thành công (có lẽ là đầu ra là một FD đóng), exit 0sẽ bị bỏ qua và sau đó mã sẽ cố gắng thực hiện echo "bar". Nếu nó cũng thất bại, &&điều kiện sẽ thất bại và thậm chí nó sẽ không thực thi exit 1! Sử dụng các iftuyên bố thực tế thay vì &&/ ||ít bị tác dụng phụ không mong muốn.
Charles Duffy

@CharlesDuffy Đó là kiểu suy nghĩ thực sự thông minh mà hầu hết mọi người chỉ mắc phải khi họ phải theo dõi những con bọ lông ...! Tôi đã không bao giờ nghĩ rằng tiếng vang có thể trở lại thất bại.
Camilo Martin

6
Đến bữa tiệc muộn một chút, nhưng tôi biết về những nguy hiểm mà Charles đã viết, vì tôi cũng phải trải qua chúng cách đây khá lâu. Vì vậy, đây là một dòng chứng minh 100% (và dễ đọc) cho bạn: [[ $1 =~ "^[0-9]+$" ]] && { echo "number"; exit 0; } || { echo "not a number"; exit 1; }Dấu ngoặc nhọn chỉ ra rằng mọi thứ KHÔNG nên được thực hiện trong một lớp con (chắc chắn sẽ là cách đó với ()dấu ngoặc đơn được sử dụng thay thế). Hãy cẩn thận: Không bao giờ bỏ lỡ dấu chấm phẩy cuối cùng . Nếu không, bạn có thể gây ra bashviệc in ra các thông báo lỗi xấu nhất (và vô nghĩa nhất) ...
cú pháp

5
Nó không hoạt động trong Ubuntu, trừ khi bạn không xóa dấu ngoặc kép. Vì vậy, nó chỉ nên là[[ 12345 =~ ^[0-9]+$ ]] && echo OKKK || echo NOOO
Treviño

4
Bạn sẽ cần phải cụ thể hơn về ý nghĩa của "số" . Một số nguyên? Một số điểm cố định? Ký hiệu khoa học ("e")? Có một phạm vi bắt buộc (ví dụ: giá trị không dấu 64 bit) hoặc bạn có cho phép bất kỳ số nào có thể được viết không?
Toby Speight

Câu trả lời:


803

Một cách tiếp cận là sử dụng một biểu thức chính quy, như vậy:

re='^[0-9]+$'
if ! [[ $yournumber =~ $re ]] ; then
   echo "error: Not a number" >&2; exit 1
fi

Nếu giá trị không nhất thiết là số nguyên, hãy xem xét sửa đổi biểu thức chính xác; ví dụ:

^[0-9]+([.][0-9]+)?$

... hoặc, để xử lý các số có dấu:

^[+-]?[0-9]+([.][0-9]+)?$

7
+1 cho phương pháp này, nhưng hãy cẩn thận với số thập phân, thực hiện kiểm tra này với, ví dụ, lỗi "1.0" hoặc "1.0" in: Không phải là số ".
sourcerebels

15
Tôi tìm thấy '' exec> & 2; tiếng vang ... '' khá ngớ ngẩn. Chỉ '' vang ...> & 2 ''
lhunath

5
@Ben bạn có thực sự muốn xử lý nhiều hơn một dấu trừ không? Tôi sẽ làm điều đó ^-?thay vì ^-*trừ khi bạn thực sự làm việc để xử lý nhiều lần đảo ngược một cách chính xác.
Charles Duffy

4
@SandraSchlichting Làm cho tất cả đầu ra trong tương lai đi đến stderr. Không thực sự là một điểm cho nó ở đây, nơi chỉ có một tiếng vang, nhưng đó là thói quen tôi có xu hướng mắc phải trong trường hợp thông báo lỗi trải dài trên nhiều dòng.
Charles Duffy

29
Tôi không chắc tại sao biểu thức chính quy phải được lưu trong một biến, nhưng nếu nó vì mục đích tương thích thì tôi không nghĩ nó cần thiết. Bạn chỉ có thể áp dụng biểu thức trực tiếp : [[ $yournumber =~ ^[0-9]+$ ]].
konsolebox

284

Không có bashism (hoạt động ngay cả trong System V sh),

case $string in
    ''|*[!0-9]*) echo bad ;;
    *) echo good ;;
esac

Điều này từ chối các chuỗi rỗng và các chuỗi chứa các chữ số không, chấp nhận mọi thứ khác.

Số âm hoặc dấu phẩy động cần một số công việc bổ sung. Một ý tưởng là loại trừ -/ .trong mẫu "xấu" đầu tiên và thêm các mẫu "xấu" khác chứa các cách sử dụng không phù hợp của chúng ( ?*-*/ *.*.*)


20
+1 - đây là cách thành ngữ, di động trở lại vỏ Bourne ban đầu và có hỗ trợ tích hợp cho các ký tự đại diện kiểu toàn cầu. Nếu bạn đến từ một ngôn ngữ lập trình khác, nó trông có vẻ kỳ lạ, nhưng nó thanh lịch hơn nhiều so với việc đối phó với sự khó hiểu của các vấn đề trích dẫn khác nhau và các vấn đề tương thích ngược / ngược vô tận vớiif test ...
tripleee

6
Bạn có thể thay đổi dòng đầu tiên thành ${string#-}(không hoạt động trong các vỏ Bourne cổ, nhưng hoạt động trong bất kỳ vỏ POSIX nào) để chấp nhận các số nguyên âm.
Gilles 'SO- ngừng trở nên xấu xa'

4
Ngoài ra, điều này rất dễ dàng để mở rộng cho các float - chỉ cần thêm '.' | *.*.*vào các mẫu không được phép và thêm dấu chấm vào các ký tự được phép. Tương tự, bạn có thể cho phép một dấu hiệu tùy chọn trước đó, mặc dù sau đó tôi chỉ muốn case ${string#[-+]}bỏ qua dấu hiệu đó.
tripleee

Xem phần này để xử lý các số nguyên đã ký: stackoverflow.com/a/18620446/2013911
Niklas Peter

2
@Dor Các trích dẫn là không cần thiết, vì lệnh case không thực hiện phân tách từ và tạo tên đường dẫn trên từ đó. (Tuy nhiên, việc mở rộng trong các mẫu trường hợp có thể cần trích dẫn vì nó xác định xem các ký tự khớp mẫu là theo nghĩa đen hay đặc biệt.)
jilles

193

Giải pháp sau đây cũng có thể được sử dụng trong các shell cơ bản như Bourne mà không cần biểu thức chính quy. Về cơ bản, bất kỳ hoạt động đánh giá giá trị số nào sử dụng số không sẽ dẫn đến một lỗi sẽ được coi là sai trong shell:

"$var" -eq "$var"

như trong:

#!/bin/bash

var=a

if [ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null; then
  echo number
else
  echo not a number
fi

Bạn cũng có thể kiểm tra $? mã trả về của hoạt động rõ ràng hơn:

[ -n "$var" ] && [ "$var" -eq "$var" ] 2>/dev/null
if [ $? -ne 0 ]; then
   echo $var is not number
fi

Chuyển hướng lỗi tiêu chuẩn là có để ẩn thông báo "biểu thức số nguyên dự kiến" mà bash in ra trong trường hợp chúng ta không có số.

CAVEATS (nhờ các ý kiến ​​dưới đây):

  • Các số có dấu thập phân không được được xác định là "số" hợp lệ
  • Sử dụng [[ ]]thay vì [ ]sẽ luôn luôn đánh giátrue
  • Hầu hết các shell không Bash sẽ luôn đánh giá biểu thức này là true
  • Hành vi trong Bash không có giấy tờ và do đó có thể thay đổi mà không cần cảnh báo
  • Nếu giá trị bao gồm khoảng trắng sau số (ví dụ: "1 a") sẽ tạo ra lỗi, như bash: [[: 1 a: syntax error in expression (error token is "a")
  • Nếu giá trị giống với tên var (ví dụ i = "i"), sẽ tạo ra lỗi, như bash: [[: i: expression recursion level exceeded (error token is "i")

8
Tôi vẫn khuyến nghị điều này (nhưng với các biến được trích dẫn để cho phép các chuỗi rỗng), vì kết quả được đảm bảo có thể sử dụng như một số trong Bash, bất kể là gì.
l0b0

21
Hãy cẩn thận để sử dụng dấu ngoặc đơn; [[ a -eq a ]]ước tính thành đúng (cả hai đối số được chuyển đổi thành 0)
Tgr

3
Rất đẹp! Lưu ý điều này chỉ hoạt động cho một số nguyên, không phải bất kỳ số nào. Tôi cần kiểm tra một đối số duy nhất phải là số nguyên, vì vậy điều này hoạt động tốt:if ! [ $# -eq 1 -o "$1" -eq "$1" ] 2>/dev/null; then
haridsv

6
Tôi đặc biệt khuyên bạn nên chống lại phương pháp này vì số lượng vỏ không đáng kể mà phần [dựng sẵn sẽ đánh giá các đối số là số học. Điều đó đúng trong cả ksh93 và mksh. Hơn nữa, vì cả hai mảng hỗ trợ đó, có cơ hội dễ dàng để tiêm mã. Sử dụng một mô hình phù hợp thay thế.
ormaaj

3
@AlbertoZaccagni, trong các bản phát hành bash hiện tại, các giá trị này được diễn giải với các quy tắc ngữ cảnh số chỉ dành cho [[ ]]nhưng không dành cho [ ]. Điều đó nói rằng, hành vi này không được chỉ định bởi cả tiêu chuẩn POSIX cho testvà trong tài liệu riêng của bash; các phiên bản tương lai của bash có thể sửa đổi hành vi để khớp với ksh mà không phá vỡ bất kỳ lời hứa hành vi nào được ghi lại, do đó việc dựa vào hành vi hiện tại của nó vẫn không được đảm bảo an toàn.
Charles Duffy

43

Điều này kiểm tra nếu một số là một số nguyên không âm và cả hai đều độc lập với vỏ (tức là không có bashism) và chỉ sử dụng các phần tử dựng sẵn:

SAI.

Vì câu trả lời đầu tiên này (bên dưới) cho phép các số nguyên có ký tự trong đó miễn là câu đầu tiên không phải là đầu tiên trong biến.

[ -z "${num##[0-9]*}" ] && echo "is a number" || echo "is not a number";

CHÍNH XÁC .

Như jilles đã nhận xét và đề xuất trong câu trả lời của mình, đây là cách chính xác để làm điều đó bằng cách sử dụng các mẫu vỏ.

[ ! -z "${num##*[!0-9]*}" ] && echo "is a number" || echo "is not a number";

5
Điều này không hoạt động đúng, nó chấp nhận bất kỳ chuỗi bắt đầu bằng một chữ số. Lưu ý rằng WORD bằng $ {VAR ## WORD} và tương tự là mẫu shell, không phải là biểu thức chính quy.
jilles

2
Bạn có thể dịch biểu thức đó sang tiếng Anh không? Tôi thực sự muốn sử dụng nó, nhưng tôi không hiểu nó đủ để tin tưởng nó, ngay cả sau khi lướt qua trang bash man.
CivilFan

2
*[!0-9]*là một mẫu phù hợp với tất cả các chuỗi có ít nhất 1 ký tự không có chữ số. ${num##*[!0-9]*}là một "mở rộng tham số" trong đó chúng ta lấy nội dung của numbiến và loại bỏ chuỗi dài nhất phù hợp với mẫu. Nếu kết quả của việc mở rộng tham số không trống ( ! [ -z ${...} ]) thì đó là một số vì nó không chứa bất kỳ ký tự không có chữ số nào.
mrucci

Thật không may, điều này không thành công nếu có bất kỳ chữ số nào trong đối số, ngay cả khi đó không phải là số hợp lệ. Ví dụ: "tests1ple" hoặc "a2b".
studgeek

Cả hai đều thất bại với122s :-(
Hastur

40

Không ai đề xuất kết hợp mẫu mở rộng của bash :

[[ $1 == ?(-)+([0-9]) ]] && echo "$1 is an integer"

5
Glenn, tôi xóa shopt -s extglobkhỏi bài đăng của bạn (mà tôi đã nêu lên, đó là một trong những câu trả lời yêu thích của tôi ở đây), vì trong Cấu trúcđiều kiện bạn có thể đọc: Khi sử dụng ==!=toán tử, chuỗi bên phải của toán tử được coi là một mẫu và khớp theo các quy tắc được mô tả dưới đây trong Kết hợp mẫu , như thể extglobtùy chọn shell được bật. Tôi hy vọng bạn không phiền!
gniourf_gniourf

Trong bối cảnh như vậy, bạn không cần phải shopt extglob... đó là một điều tốt để biết!
gniourf_gniourf

Hoạt động tốt cho số nguyên đơn giản.

2
@Jdamian: bạn nói đúng, điều này đã được thêm vào Bash 4.1 (được phát hành vào cuối năm 2009, Bash Bash 3.2 đã được phát hành vào năm 2006, giờ đây nó là một phần mềm cổ, xin lỗi cho những người bị mắc kẹt trong quá khứ). Ngoài ra, bạn có thể lập luận rằng các extglobs được giới thiệu trong phiên bản 2.02 (phát hành năm 1998) và không hoạt động trong <2.02 phiên bản. Bây giờ nhận xét của bạn ở đây sẽ đóng vai trò như một lời cảnh báo về các phiên bản cũ hơn.
gniourf_gniourf

1
Các biến trong [[...]]không bị chia tách từ hoặc mở rộng toàn cầu.
glenn jackman

26

Tôi ngạc nhiên về các giải pháp phân tích trực tiếp các định dạng số trong shell. shell không phù hợp với điều này, là DSL để kiểm soát các tệp và quy trình. Có rất nhiều trình phân tích cú pháp số thấp hơn một chút, ví dụ:

isdecimal() {
  # filter octal/hex/ord()
  num=$(printf '%s' "$1" | sed "s/^0*\([1-9]\)/\1/; s/'/^/")

  test "$num" && printf '%f' "$num" >/dev/null 2>&1
}

Thay đổi '% f' thành bất kỳ định dạng cụ thể nào bạn yêu cầu.


4
isnumber(){ printf '%f' "$1" &>/dev/null && echo "this is a number" || echo "not a number"; }
Gilles Quenot

4
@sputnick phiên bản của bạn phá vỡ ngữ nghĩa giá trị trả về vốn có (và hữu ích) của hàm ban đầu. Vì vậy, thay vào đó, chỉ cần để nguyên chức năng và sử dụng nó:isnumber 23 && echo "this is a number" || echo "not a number"
michael

4
Không nên điều này cũng có 2>/dev/null, vì vậy mà isnumber "foo"không gây ô nhiễm stderr?
gioele

4
Để gọi các shell hiện đại như bash "DSL để kiểm soát các tệp và quy trình" là bỏ qua việc chúng được sử dụng nhiều hơn thế - một số distro đã xây dựng toàn bộ trình quản lý gói và giao diện web trên đó (xấu đến mức có thể). Các tệp hàng loạt phù hợp với mô tả của bạn, vì ngay cả việc thiết lập một biến cũng khó khăn.
Camilo Martin

7
Thật buồn cười khi bạn đang cố gắng trở nên thông minh bằng cách sao chép một số thành ngữ từ các ngôn ngữ khác. Thật không may, điều này không hoạt động trong vỏ. Shell rất đặc biệt và không có kiến ​​thức vững chắc về chúng, bạn có khả năng viết mã bị hỏng. Mã của bạn bị hỏng: isnumber "'a"sẽ trả về đúng. Điều này được ghi lại trong thông số POSIX nơi bạn sẽ đọc: Nếu ký tự đầu là một trích dẫn đơn hoặc trích dẫn kép, giá trị sẽ là giá trị số trong bộ mã cơ bản của ký tự theo dấu ngoặc đơn hoặc trích dẫn kép .
gniourf_gniourf

16

Tôi đã xem xét các câu trả lời và ... nhận ra rằng không ai nghĩ về số FLOAT (có dấu chấm)!

Sử dụng grep cũng rất tuyệt.
-E có nghĩa là regex mở rộng
-q có nghĩa là yên tĩnh (không lặp lại)
-qE là sự kết hợp của cả hai.

Để kiểm tra trực tiếp trong dòng lệnh:

$ echo "32" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is: 32

$ echo "3a2" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is empty (false)

$ echo ".5" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer .5

$ echo "3.2" | grep -E ^\-?[0-9]?\.?[0-9]+$  
# answer is 3.2

Sử dụng trong tập lệnh bash:

check=`echo "$1" | grep -E ^\-?[0-9]*\.?[0-9]+$`

if [ "$check" != '' ]; then    
  # it IS numeric
  echo "Yeap!"
else
  # it is NOT numeric.
  echo "nooop"
fi

Để khớp với số nguyên JUST, hãy sử dụng số nguyên này:

# change check line to:
check=`echo "$1" | grep -E ^\-?[0-9]+$`

Các giải pháp sử dụng awk by triple_r và tripleee hoạt động với float.
Ken Jackson

Cảm ơn vì điều này và điểm rất tốt! Nguyên nhân câu hỏi thực sự là làm thế nào để kiểm tra nếu nó là một số và không chỉ là một số nguyên.
Tanocation

Em cảm ơn anh quá. Hãy giúp đỡ nhau luôn.
Sergio Abreu

12

Chỉ cần theo dõi đến @mary. Nhưng vì tôi không có đủ đại diện nên không thể đăng bài này dưới dạng bình luận cho bài đăng đó. Dù sao, đây là những gì tôi đã sử dụng:

isnum() { awk -v a="$1" 'BEGIN {print (a == a + 0)}'; }

Hàm sẽ trả về "1" nếu đối số là số, nếu không sẽ trả về "0". Điều này làm việc cho số nguyên cũng như phao. Cách sử dụng là một cái gì đó như:

n=-2.05e+07
res=`isnum "$n"`
if [ "$res" == "1" ]; then
     echo "$n is a number"
else
     echo "$n is not a number"
fi

4
In một số ít hữu ích hơn so với việc đặt mã thoát. 'BEGIN { exit(1-(a==a+0)) }'là hơi khó để grok nhưng có thể được sử dụng trong một hàm trả về true hoặc false giống như [, grep -qvv
tripleee

9
test -z "${i//[0-9]}" && echo digits || echo no no no

${i//[0-9]}thay thế bất kỳ chữ số nào trong giá trị của $ibằng một chuỗi rỗng, xem man -P 'less +/parameter\/' bash.-zkiểm tra xem chuỗi kết quả có độ dài bằng không.

nếu bạn cũng muốn loại trừ trường hợp khi $itrống, bạn có thể sử dụng một trong các công trình sau:

test -n "$i" && test -z "${i//[0-9]}" && echo digits || echo not a number
[[ -n "$i" && -z "${i//[0-9]}" ]] && echo digits || echo not a number

Thumbs up đặc biệt là cho man -P 'less +/parameter\/' bashmột phần. Học điều gì đó mới mỗi ngày. :)
David

@sjas Bạn có thể dễ dàng thêm \-vào biểu thức chính quy để giải quyết vấn đề. Sử dụng [0-9\-\.\+]để chiếm tài khoản cho số float và số đã ký.
dùng2683246

@sjas ok, lỗi của tôi
user2683246

@sjasecho $i | python -c $'import sys\ntry:\n float(sys.stdin.read().rstrip())\nexcept:\n sys.exit(1)' && echo yes || echo no
user2683246

9

Đối với vấn đề của tôi, tôi chỉ cần đảm bảo rằng người dùng không vô tình nhập một số văn bản do đó tôi đã cố gắng giữ cho nó đơn giản và dễ đọc

isNumber() {
    (( $1 )) 2>/dev/null
}

Theo trang người đàn ông này khá nhiều làm những gì tôi muốn

Nếu giá trị của biểu thức là khác không, trạng thái trả về là 0

Để ngăn thông báo lỗi khó chịu cho chuỗi "có thể là số" Tôi bỏ qua đầu ra lỗi

$ (( 2s ))
bash: ((: 2s: value too great for base (error token is "2s")

Điều này giống như isInteger. Nhưng cảm ơn tuyệt vời!
Naheel

8

Câu hỏi cũ, nhưng tôi chỉ muốn giải quyết vấn đề của tôi. Cái này không yêu cầu bất kỳ thủ thuật vỏ lạ nào, hoặc dựa vào thứ gì đó đã tồn tại mãi mãi.

if [ -n "$(printf '%s\n' "$var" | sed 's/[0-9]//g')" ]; then
    echo 'is not numeric'
else
    echo 'is numeric'
fi

Về cơ bản, nó chỉ loại bỏ tất cả các chữ số khỏi đầu vào và nếu bạn để lại một chuỗi có độ dài khác không thì đó không phải là một số.


Điều này thất bại cho một sản phẩm nào var.
gniourf_gniourf

Hoặc cho các biến có dòng mới hoặc một cái gì đó như $'0\n\n\n1\n\n\n2\n\n\n3\n' .
gniourf_gniourf

7

Chưa thể bình luận vì vậy tôi sẽ thêm câu trả lời của riêng mình, đây là phần mở rộng cho câu trả lời của glenn jackman bằng cách sử dụng khớp mẫu bash.

Nhu cầu ban đầu của tôi là xác định số và phân biệt số nguyên và số float. Các định nghĩa hàm được khấu trừ vào:

function isInteger() {
    [[ ${1} == ?(-)+([0-9]) ]]
}

function isFloat() {
    [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}

Tôi đã sử dụng thử nghiệm đơn vị (với shUnit2) để xác thực các mẫu của tôi hoạt động như dự định:

oneTimeSetUp() {
    int_values="0 123 -0 -123"
    float_values="0.0 0. .0 -0.0 -0. -.0 \
        123.456 123. .456 -123.456 -123. -.456
        123.456E08 123.E08 .456E08 -123.456E08 -123.E08 -.456E08 \
        123.456E+08 123.E+08 .456E+08 -123.456E+08 -123.E+08 -.456E+08 \
        123.456E-08 123.E-08 .456E-08 -123.456E-08 -123.E-08 -.456E-08"
}

testIsIntegerIsFloat() {
    local value
    for value in ${int_values}
    do
        assertTrue "${value} should be tested as integer" "isInteger ${value}"
        assertFalse "${value} should not be tested as float" "isFloat ${value}"
    done

    for value in ${float_values}
    do
        assertTrue "${value} should be tested as float" "isFloat ${value}"
        assertFalse "${value} should not be tested as integer" "isInteger ${value}"
    done

}

Lưu ý: Mẫu isFloat có thể được sửa đổi để trở nên khoan dung hơn về dấu thập phân ( @(.,)) và ký hiệu E ( @(Ee)). Đơn vị của tôi kiểm tra chỉ kiểm tra các giá trị là số nguyên hoặc số float, nhưng không có bất kỳ đầu vào không hợp lệ nào.


Tôi xin lỗi, tôi không hiểu chức năng chỉnh sửa được sử dụng như thế nào.
3ronco

6

Tôi sẽ thử điều này:

printf "%g" "$var" &> /dev/null
if [[ $? == 0 ]] ; then
    echo "$var is a number."
else
    echo "$var is not a number."
fi

Lưu ý: điều này nhận ra nan và inf là số.


2
hoặc trùng lặp, hoặc có lẽ phù hợp hơn với tư cách là một nhận xét cho câu trả lời của pixelbeat ( %fdù sao sử dụng có lẽ tốt hơn)
michael

3
Thay vì kiểm tra mã trạng thái trước đó, tại sao bạn không đặt nó vào ifchính nó? Đó là những gì if...if printf "%g" "$var" &> /dev/null; then ...
Camilo Martin

3
Điều này có cảnh báo khác. Nó sẽ xác nhận chuỗi rỗng và chuỗi như thế nào 'a.
gniourf_gniourf

6

Một câu trả lời rõ ràng đã được đưa ra bởi @charles Dufy và những người khác. Một giải pháp bash tinh khiết sẽ được sử dụng như sau:

string="-12,345"
if [[ "$string" =~ ^-?[0-9]+[.,]?[0-9]*$ ]]
then
    echo $string is a number
else
    echo $string is not a number
fi

Mặc dù đối với các số thực, không bắt buộc phải có một số trước điểm cơ số .

Để cung cấp hỗ trợ kỹ lưỡng hơn về số nổi và ký hiệu khoa học (nhiều chương trình trong C / Fortran nếu không sẽ xuất theo cách này), một bổ sung hữu ích cho dòng này sẽ là như sau:

string="1.2345E-67"
if [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*[eE]?-?[0-9]+$ ]]
then
    echo $string is a number
else
    echo $string is not a number
fi

Do đó, dẫn đến một cách để phân biệt các loại số, nếu bạn đang tìm kiếm bất kỳ loại cụ thể nào:

string="-12,345"
if [[ "$string" =~ ^-?[0-9]+$ ]]
then
    echo $string is an integer
elif [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*$ ]]
then
    echo $string is a float
elif [[ "$string" =~ ^-?[0-9]*[.,]?[0-9]*[eE]-?[0-9]+$ ]]
then
    echo $string is a scientific number
else
    echo $string is not a number
fi

Lưu ý: Chúng tôi có thể liệt kê các yêu cầu cú pháp cho ký hiệu thập phân và khoa học, một là cho phép dấu phẩy dưới dạng điểm cơ số, cũng như ".". Sau đó chúng tôi sẽ khẳng định rằng chỉ có một điểm cơ số như vậy. Có thể có hai dấu +/- trong một float [Ee]. Tôi đã học được thêm một vài quy tắc từ công việc của Aulu và đã thử nghiệm các chuỗi xấu như '' '-' '-E-1' '0-0'. Dưới đây là các công cụ regex / chuỗi con / expr của tôi dường như đang giữ:

parse_num() {
 local r=`expr "$1" : '.*\([.,]\)' 2>/dev/null | tr -d '\n'` 
 nat='^[+-]?[0-9]+[.,]?$' \
 dot="${1%[.,]*}${r}${1##*[.,]}" \
 float='^[\+\-]?([.,0-9]+[Ee]?[-+]?|)[0-9]+$'
 [[ "$1" == $dot ]] && [[ "$1" =~ $float ]] || [[ "$1" =~ $nat ]]
} # usage: parse_num -123.456

6
[[ $1 =~ ^-?[0-9]+$ ]] && echo "number"

Đừng quên -bao gồm các số âm!


Phiên bản tối thiểu của bash là gì? Tôi vừa nhận được bash: toán tử nhị phân có điều kiện dự kiến ​​bash: lỗi cú pháp gần mã thông báo không mong đợi `= ~ '
Paul Hargreaves

1
@PaulHargreaves =~tồn tại ít nhất là từ bash 3.0.
Gilles 'SO- ngừng trở thành ác quỷ'

@PaulHargreaves có thể bạn đã gặp sự cố với toán hạng đầu tiên của mình, ví dụ: quá nhiều dấu ngoặc kép hoặc tương tự
Joshua Clayton

@JoshuaClayton Tôi đã hỏi về phiên bản này vì nó rất cũ trong hộp Solaris 7, cái mà chúng tôi vẫn có và nó không hỗ trợ = ~
Paul Hargreaves

6

Điều này có thể đạt được bằng cách sử dụng grep để xem liệu biến trong câu hỏi có khớp với biểu thức chính quy mở rộng không.

Kiểm tra số nguyên 1120:

yournumber=1120
if echo "$yournumber" | grep -qE '^[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi

Đầu ra: Valid number.

Kiểm tra không nguyên 1120a:

yournumber=1120a
if echo "$yournumber" | grep -qE '^[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi

Đầu ra: Error: not a number.


Giải trình

  • Các grep, các -Ecông tắc cho phép chúng ta sử dụng biểu thức chính quy mở rộng '^[0-9]+$'. Biểu thức chính quy này có nghĩa là biến chỉ nên []chứa các số từ 0-90 đến 9 từ ^đầu đến $cuối biến và nên có ít nhất+ một ký tự.
  • Các grep, các -qcông tắc yên tĩnh tắt bất kỳ đầu ra hay không mà nó tìm thấy bất cứ điều gì.
  • ifkiểm tra trạng thái thoát của grep. Trạng thái thoát 0có nghĩa là thành công và bất cứ điều gì lớn hơn có nghĩa là một lỗi. Các greplệnh có một trạng thái thoát của 0nếu nó tìm thấy một trận đấu và 1khi nó không;

Vì vậy, kết hợp tất cả lại với nhau, trong ifthử nghiệm, chúng tôi echobiến $yournumbervà đưa |nó vào grepcông -qtắc với âm thầm phù hợp với -Ebiểu thức biểu thức chính quy mở rộng '^[0-9]+$'. Trạng thái thoát của grepsẽ là 0nếu greptìm thấy thành công một trận đấu và 1nếu nó không. Nếu thành công để phù hợp, chúng tôi echo "Valid number.". Nếu nó không phù hợp, chúng tôi echo "Error: not a number.".


Đối với phao hoặc đôi

Chúng ta chỉ có thể thay đổi biểu thức thông thường từ '^[0-9]+$'sang '^[0-9]*\.?[0-9]+$'cho phao hoặc đôi.

Kiểm tra phao 1120.01:

yournumber=1120.01
if echo "$yournumber" | grep -qE '^[0-9]*\.?[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi

Đầu ra: Valid number.

Kiểm tra phao 11.20.01:

yournumber=11.20.01
if echo "$yournumber" | grep -qE '^[0-9]*\.?[0-9]+$'; then
    echo "Valid number."
else
    echo "Error: not a number."
fi

Đầu ra: Error: not a number.


Đối với tiêu cực

Để cho phép số nguyên âm, chỉ cần thay đổi biểu thức chính quy từ '^[0-9]+$'thành '^\-?[0-9]+$'.

Để cho phép nổi hoặc tăng gấp đôi âm, chỉ cần thay đổi biểu thức thông thường từ '^[0-9]*\.?[0-9]+$'sang '^\-?[0-9]*\.?[0-9]+$'.


Bạn nói đúng, @CharlesDuffy. Cảm ơn. Tôi dọn dẹp câu trả lời.
Joseph Shih

Phải bạn là một lần nữa, @CharlesDuffy. Đã sửa!
Joseph Shih

1
LGTM; Trả lời như đã chỉnh sửa có +1 của tôi. Điều duy nhất tôi làm khác đi vào thời điểm này chỉ là vấn đề quan điểm chứ không phải là tính chính xác (f / e, sử dụng [-]thay vì \-[.]thay vì \.dài dòng hơn một chút, nhưng điều đó có nghĩa là chuỗi của bạn không phải thay đổi nếu chúng ' được sử dụng trong bối cảnh mà dấu gạch chéo ngược được sử dụng).
Charles Duffy

4

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_03.html

Bạn cũng có thể sử dụng các lớp nhân vật của bash.

if [[ $VAR = *[[:digit:]]* ]]; then
 echo "$VAR is numeric"
else
 echo "$VAR is not numeric"
fi

Số sẽ bao gồm không gian, dấu thập phân và "e" hoặc "E" cho dấu phẩy động.

Nhưng, nếu bạn chỉ định số hex kiểu C, tức là "0xffff" hoặc "0XFFFF", [[: chữ số:]] trả về đúng. Một chút bẫy ở đây, bash cho phép bạn thực hiện một cái gì đó như "0xAZ00" và vẫn tính nó là một chữ số (đây không phải là từ một trình biên dịch kỳ lạ của trình biên dịch GCC cho phép bạn sử dụng ký hiệu 0x cho các cơ sở khác ngoài 16 ??? )

Bạn có thể muốn kiểm tra "0x" hoặc "0X" trước khi kiểm tra nếu đó là số nếu đầu vào của bạn hoàn toàn không tin cậy, trừ khi bạn muốn chấp nhận số hex. Điều đó sẽ được thực hiện bằng cách:

if [[ ${VARIABLE:1:2} = "0x" ]] || [[ ${VARIABLE:1:2} = "0X" ]]; then echo "$VAR is not numeric"; fi

17
[[ $VAR = *[[:digit:]]* ]]sẽ trả về true nếu biến chứa một số chứ không phải số nguyên.
glenn jackman

[[ "z3*&" = *[[:digit:]]* ]] && echo "numeric"bản in numeric. Đã thử nghiệm trong phiên bản bash 3.2.25(1)-release.
Jdamian

1
@ultraswadable, giải pháp của bạn phát hiện các chuỗi chứa, ít nhất, một chữ số được bao quanh (hoặc không) bởi bất kỳ ký tự nào khác. Tôi hạ bệ.
Jdamian

Do đó, cách tiếp cận rõ ràng chính xác là để đảo ngược điều này và sử dụng[[ -n $VAR && $VAR != *[^[:digit:]]* ]]
eschwartz

@eschwartz, giải pháp của bạn không hoạt động với số âm
Angel

4

Cách đơn giản nhất là kiểm tra xem nó có chứa các ký tự không có chữ số không. Bạn thay thế tất cả các ký tự chữ số không có gì và kiểm tra độ dài. Nếu có chiều dài thì đó không phải là một con số.

if [[ ! -n ${input//[0-9]/} ]]; then
    echo "Input Is A Number"
fi

2
Để xử lý số âm sẽ đòi hỏi một cách tiếp cận phức tạp hơn.
Andy

... Hoặc một dấu hiệu tích cực tùy chọn.
tripleee

@tripleee Tôi muốn xem cách tiếp cận của bạn nếu bạn biết cách thực hiện.
Andy

4

Vì tôi đã phải can thiệp vào điều này gần đây và giống như appoach của karttu với bài kiểm tra đơn vị nhiều nhất. Tôi đã sửa đổi mã và thêm một số giải pháp khác, hãy tự mình thử để xem kết quả:

#!/bin/bash

    # N={0,1,2,3,...} by syntaxerror
function isNaturalNumber()
{
 [[ ${1} =~ ^[0-9]+$ ]]
}
    # Z={...,-2,-1,0,1,2,...} by karttu
function isInteger() 
{
 [[ ${1} == ?(-)+([0-9]) ]]
}
    # Q={...,-½,-¼,0.0,¼,½,...} by karttu
function isFloat() 
{
 [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]
}
    # R={...,-1,-½,-¼,0.E+n,¼,½,1,...}
function isNumber()
{
 isNaturalNumber $1 || isInteger $1 || isFloat $1
}

bools=("TRUE" "FALSE")
int_values="0 123 -0 -123"
float_values="0.0 0. .0 -0.0 -0. -.0 \
    123.456 123. .456 -123.456 -123. -.456 \
    123.456E08 123.E08 .456E08 -123.456E08 -123.E08 -.456E08 \
    123.456E+08 123.E+08 .456E+08 -123.456E+08 -123.E+08 -.456E+08 \
    123.456E-08 123.E-08 .456E-08 -123.456E-08 -123.E-08 -.456E-08"
false_values="blah meh mooh blah5 67mooh a123bc"

for value in ${int_values} ${float_values} ${false_values}
do
    printf "  %5s=%-30s" $(isNaturalNumber $value) ${bools[$?]} $(printf "isNaturalNumber(%s)" $value)
    printf "%5s=%-24s" $(isInteger $value) ${bools[$?]} $(printf "isInteger(%s)" $value)
    printf "%5s=%-24s" $(isFloat $value) ${bools[$?]} $(printf "isFloat(%s)" $value)
    printf "%5s=%-24s\n" $(isNumber $value) ${bools[$?]} $(printf "isNumber(%s)" $value)
done

Vì vậy, isNumber () bao gồm dấu gạch ngang, dấu phẩy và ký hiệu số mũ và do đó trả về TRUE trên số nguyên & số float, mặt khác isFloat () trả về FALSE trên các giá trị nguyên và isInteger () cũng trả về FALSE trên số float. Để thuận tiện cho bạn, tất cả như một lớp lót:

isNaturalNumber() { [[ ${1} =~ ^[0-9]+$ ]]; }
isInteger() { [[ ${1} == ?(-)+([0-9]) ]]; }
isFloat() { [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]; }
isNumber() { isNaturalNumber $1 || isInteger $1 || isFloat $1; }

Cá nhân tôi sẽ loại bỏ functiontừ khóa vì nó không làm gì hữu ích. Ngoài ra, tôi không chắc về tính hữu ích của các giá trị trả về. Trừ khi có quy định khác, các hàm sẽ trả về trạng thái thoát của lệnh cuối cùng, vì vậy bạn không cần phải returntự mình làm bất cứ điều gì.
Tom Fenech

Đẹp, thực sự returnlà khó hiểu và làm cho nó ít đọc hơn. Sử dụng functiontừ khóa hay không là một câu hỏi về hương vị cá nhân ít nhất là tôi đã loại bỏ chúng khỏi một lớp lót để tiết kiệm không gian. cám ơn.
3ronco

Đừng quên rằng dấu chấm phẩy là cần thiết sau khi thử nghiệm cho các phiên bản một dòng.
Tom Fenech

2
isNumber sẽ trả về 'true' trên bất kỳ chuỗi nào có số trong đó.
DrStrangepork

@DrStrangepork Thật vậy, mảng false_values ​​của tôi thiếu trường hợp đó. Tôi sẽ xem xét nó. Cảm ơn đã gợi ý.
3ronco

4

Tôi sử dụng expr . Nó trả về giá trị khác không nếu bạn cố gắng thêm số 0 vào giá trị không phải là số:

if expr -- "$number" + 0 > /dev/null 2>&1
then
    echo "$number is a number"
else
    echo "$number isn't a number"
fi

Có thể sử dụng bc nếu bạn cần số nguyên, nhưng tôi không tin bccó hành vi tương tự. Thêm số 0 vào số không sẽ giúp bạn có số 0 và nó cũng trả về giá trị bằng 0. Có lẽ bạn có thể kết hợp bcexpr. Sử dụng bcđể thêm số không vào $number. Nếu câu trả lời là 0, sau đó thử exprxác minh đó $numberkhông phải là số không.


1
Điều này là khá xấu. Để làm cho nó tốt hơn một chút bạn nên sử dụng expr -- "$number" + 0; nhưng điều này vẫn sẽ giả vờ rằng 0 isn't a number. Từ man expr:Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null or 0,
gniourf_gniourf

3

Tôi thích câu trả lời của Alberto Zaccagni.

if [ "$var" -eq "$var" ] 2>/dev/null; then

Điều kiện tiên quyết quan trọng: - không có các lớp con sinh ra - không có trình phân tích cú pháp RE được gọi - hầu hết các ứng dụng shell không sử dụng số thực

Nhưng nếu $varphức tạp (ví dụ: truy cập mảng kết hợp) và nếu số đó sẽ là số nguyên không âm (hầu hết các trường hợp sử dụng), thì điều này có lẽ hiệu quả hơn?

if [ "$var" -ge 0 ] 2> /dev/null; then ..

2

Tôi sử dụng printf như các câu trả lời khác được đề cập, nếu bạn cung cấp chuỗi định dạng "% f" hoặc "% i" printf sẽ thực hiện việc kiểm tra cho bạn. Dễ dàng hơn so với phát minh lại các kiểm tra, cú pháp đơn giản và ngắn gọn và printf có mặt khắp nơi. Vì vậy, theo tôi, đó là một lựa chọn hợp lý - bạn cũng có thể sử dụng ý tưởng sau đây để kiểm tra một loạt các thứ, nó không chỉ hữu ích cho việc kiểm tra số.

declare  -r CHECK_FLOAT="%f"  
declare  -r CHECK_INTEGER="%i"  

 ## <arg 1> Number - Number to check  
 ## <arg 2> String - Number type to check  
 ## <arg 3> String - Error message  
function check_number() { 
  local NUMBER="${1}" 
  local NUMBER_TYPE="${2}" 
  local ERROR_MESG="${3}"
  local -i PASS=1 
  local -i FAIL=0   
  case "${NUMBER_TYPE}" in 
    "${CHECK_FLOAT}") 
        if ((! $(printf "${CHECK_FLOAT}" "${NUMBER}" &>/dev/random;echo $?))); then 
           echo "${PASS}"
        else 
           echo "${ERROR_MESG}" 1>&2
           echo "${FAIL}"
        fi 
        ;;                 
    "${CHECK_INTEGER}") 
        if ((! $(printf "${CHECK_INTEGER}" "${NUMBER}" &>/dev/random;echo $?))); then 
           echo "${PASS}"
        else 
           echo "${ERROR_MESG}" 1>&2
           echo "${FAIL}"
        fi 
        ;;                 
                     *) 
        echo "Invalid number type format: ${NUMBER_TYPE} to check_number()." 1>&2
        echo "${FAIL}"
        ;;                 
   esac
} 

>$ var=45

>$ (($(check_number $var "${CHECK_INTEGER}" "Error: Found $var - An integer is required."))) && { echo "$var+5" | bc; }


2

biểu hiện thông thường vs mở rộng tham số

Như câu trả lời của Charles Duffy công việc, nhưng chỉ sử dụngbiểu thức thông thường và tôi biết điều này là chậm, tôi muốn chỉ ra một cách khác, chỉ sử dụngmở rộng tham số :

Chỉ dành cho số nguyên dương:

is_num() { [ "$1" ] && [ -z "${1//[0-9]}" ] ;}

Đối với số nguyên:

is_num() { 
    local chk=${1#[+-]};
    [ "$chk" ] && [ -z "${chk//[0-9]}" ]
}

Sau đó cho nổi:

is_num() { 
    local chk=${1#[+-]};
    chk=${chk/.}
    [ "$chk" ] && [ -z "${chk//[0-9]}" ]
}

Để phù hợp với yêu cầu ban đầu:

set -- "foo bar"
is_num "$1" && VAR=$1 || echo "need a number"
need a number

set -- "+3.141592"
is_num "$1" && VAR=$1 || echo "need a number"

echo $VAR
+3.141592

Bây giờ một chút so sánh:

Có một kiểm tra tương tự dựa trên câu trả lời của Charles Duffy :

cdIs_num() { 
    local re='^[+-]?[0-9]+([.][0-9]+)?$';
    [[ $1 =~ $re ]]
}

Một số xét nghiệm:

if is_num foo;then echo It\'s a number ;else echo Not a number;fi
Not a number
if cdIs_num foo;then echo It\'s a number ;else echo Not a number;fi
Not a number
if is_num 25;then echo It\'s a number ;else echo Not a number;fi
It's a number
if cdIs_num 25;then echo It\'s a number ;else echo Not a number;fi
It's a number
if is_num 3+4;then echo It\'s a number ;else echo Not a number;fi
Not a number
if cdIs_num 3+4;then echo It\'s a number ;else echo Not a number;fi
Not a number
if is_num 3.1415;then echo It\'s a number ;else echo Not a number;fi
It's a number
if cdIs_num 3.1415;then echo It\'s a number ;else echo Not a number;fi
It's a number

Được rồi, ổn thôi. Bây giờ sẽ mất bao nhiêu thời gian (Trên mâm xôi pi của tôi):

time for i in {1..1000};do is_num +3.14159265;done
real    0m2.476s
user    0m1.235s
sys     0m0.000s

Sau đó với các biểu thức chính quy:

time for i in {1..1000};do cdIs_num +3.14159265;done
real    0m4.363s
user    0m2.168s
sys     0m0.000s

Dù sao, tôi đồng ý, tôi không thích sử dụng regex, khi tôi có thể sử dụng mở rộng tham số ... Việc lạm dụng RE sẽ khiến tập lệnh bash chậm hơn
F. Hauri

Trả lời chỉnh sửa: Không cần extglob(và nhanh hơn một chút)!
F. Hauri

@CharlesDuffy, Trên pi mâm xôi của tôi, 1000 lần lặp sẽ mất 2,5 giây với phiên bản của tôi và 4,4 giây với phiên bản của bạn!
F. Hauri

Không thể tranh luận với điều đó. 👍
Charles Duffy

1

Để bắt số âm:

if [[ $1 == ?(-)+([0-9.]) ]]
    then
    echo number
else
    echo not a number
fi

Ngoài ra, điều này đòi hỏi phải mở rộng toàn cầu để được kích hoạt trước tiên. Đây là tính năng chỉ dành cho Bash bị tắt theo mặc định.
tripleee

@tripleee mở rộng toàn cầu được kích hoạt tự động khi sử dụng == hoặc! = When the ‘==’ and ‘!=’ operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below in Pattern Matching, as if the extglob shell option were enabled. gnu.org/software/bash/manual/bashref.html#index-_005b_005b
Badr

@BadrElmer Cảm ơn đã cập nhật. Đây dường như là một hành vi mới không đúng trong Bash 3.2.57 (MacOS Mojave) của tôi. Tôi thấy nó hoạt động như bạn mô tả trong 4.4.
tripleee

1

Bạn có thể sử dụng "let" quá như thế này:

[ ~]$ var=1
[ ~]$ let $var && echo "It's a number" || echo "It's not a number"
It\'s a number
[ ~]$ var=01
[ ~]$ let $var && echo "It's a number" || echo "It's not a number"
It\'s a number
[ ~]$ var=toto
[ ~]$ let $var && echo "It's a number" || echo "It's not a number"
It\'s not a number
[ ~]$ 

Nhưng tôi thích sử dụng toán tử "3 ~" Bash 3+ như một số câu trả lời trong chuỗi này.


5
Điều này rất nguy hiểm. Đừng đánh giá số học không có giá trị trong vỏ. Nó phải được xác nhận một số cách khác đầu tiên.
ormaaj

1
printf '%b' "-123\nABC" | tr '[:space:]' '_' | grep -q '^-\?[[:digit:]]\+$' && echo "Integer." || echo "NOT integer."

Xóa -\?mẫu phù hợp trong grep nếu bạn không chấp nhận số nguyên âm.


2
Downvote vì thiếu lời giải thích. Cái này hoạt động ra sao? Nó trông phức tạp và dễ vỡ, và không rõ ràng những gì đầu vào chính xác nó sẽ chấp nhận. (Ví dụ: việc xóa các khoảng trắng có cần thiết không? Tại sao? Nó sẽ nói một số có các khoảng trắng được nhúng là một số hợp lệ, có thể không được mong muốn.)
tripleee

1

Đã làm điều tương tự ở đây với một biểu thức chính quy kiểm tra toàn bộ phần và phần thập phân, được phân tách bằng dấu chấm.

re="^[0-9]*[.]{0,1}[0-9]*$"

if [[ $1 =~ $re ]] 
then
   echo "is numeric"
else
  echo "Naahh, not numeric"
fi

Bạn có thể giải thích tại sao câu trả lời của bạn về cơ bản khác với các câu trả lời cũ khác, ví dụ: câu trả lời của Charles Duffy không? Chà, câu trả lời của bạn thực sự đã bị phá vỡ vì nó xác nhận một khoảng thời gian duy nhất.
gniourf_gniourf

không chắc chắn để hiểu khoảng thời gian duy nhất ở đây ... đó là một hoặc không có thời gian dự kiến ​​.... Nhưng đúng là không có gì khác biệt về cơ bản, chỉ cần tìm thấy regex dễ đọc hơn.
Jerome

cũng sử dụng * nên phù hợp với nhiều trường hợp thực tế hơn
Jerome

Vấn đề là bạn khớp với chuỗi trống a=''và chuỗi chỉ chứa một khoảng thời gian a='.'để mã của bạn bị hỏng đôi chút ...
gniourf_gniourf

0

Tôi sử dụng như sau (cho số nguyên):

## ##### constants
##
## __TRUE - true (0)
## __FALSE - false (1)
##
typeset -r __TRUE=0
typeset -r __FALSE=1

## --------------------------------------
## isNumber
## check if a value is an integer 
## usage: isNumber testValue 
## returns: ${__TRUE} - testValue is a number else not
##
function isNumber {
  typeset TESTVAR="$(echo "$1" | sed 's/[0-9]*//g' )"
  [ "${TESTVAR}"x = ""x ] && return ${__TRUE} || return ${__FALSE}
}

isNumber $1 
if [ $? -eq ${__TRUE} ] ; then
  print "is a number"
fi

Hầu như chính xác (bạn đang chấp nhận chuỗi trống) nhưng phức tạp đến mức khó hiểu.
Gilles 'SO- ngừng trở nên xấu xa'

2
Không chính xác: bạn đang chấp nhận -n, v.v. (vì echo) và bạn đang chấp nhận các biến có dòng mới (vì $(...)). Và nhân tiện, printđây không phải là một lệnh shell hợp lệ.
gniourf_gniourf

0

Tôi đã thử công thức của ultrasawblade vì nó có vẻ thực tế nhất đối với tôi và không thể làm cho nó hoạt động được. Cuối cùng, tôi đã nghĩ ra một cách khác, dựa trên những cách khác để thay thế tham số, lần này với sự thay thế regex:

[[ "${var//*([[:digit:]])}" ]]; && echo "$var is not numeric" || echo "$var is numeric"

Nó loại bỏ mọi ký tự: chữ số: lớp trong $ var và kiểm tra xem chúng ta có còn một chuỗi trống không, có nghĩa là bản gốc chỉ là số.

Những gì tôi thích về điều này là dấu chân nhỏ và tính linh hoạt của nó. Trong hình thức này, nó chỉ hoạt động cho các số nguyên cơ bản 10 không phân cách, mặc dù chắc chắn bạn có thể sử dụng khớp mẫu để phù hợp với nó cho các nhu cầu khác.


Đọc giải pháp của mrucci, nó trông gần giống như của tôi, nhưng sử dụng thay thế chuỗi thông thường thay vì "phong cách sed". Cả hai đều sử dụng cùng một quy tắc để khớp mẫu và là các giải pháp có thể hoán đổi cho nhau.
ata

sedlà POSIX, trong khi giải pháp của bạn là bash. Cả hai đều có công dụng của chúng
v010dya

0

Nhanh & bẩn: Tôi biết đó không phải là cách thanh lịch nhất, nhưng tôi thường chỉ thêm số 0 vào đó và kiểm tra kết quả. như vậy

function isInteger {
  [ $(($1+0)) != 0 ] && echo "$1 is a number" || echo "$1 is not a number"
 }

x=1;      isInteger $x
x="1";    isInteger $x
x="joe";  isInteger $x
x=0x16 ;  isInteger $x
x=-32674; isInteger $x   

$ (($ 1 + 0)) sẽ trả về 0 hoặc bom nếu $ 1 KHÔNG phải là số nguyên. ví dụ:

function zipIt  { # quick zip - unless the 1st parameter is a number
  ERROR="not a valid number. " 
  if [ $(($1+0)) != 0 ] ; then  # isInteger($1) 
      echo " backing up files changed in the last $1 days."
      OUT="zipIt-$1-day.tgz" 
      find . -mtime -$1 -type f -print0 | xargs -0 tar cvzf $OUT 
      return 1
  fi
    showError $ERROR
}

LƯU Ý: Tôi đoán rằng tôi chưa bao giờ nghĩ sẽ kiểm tra các phao hoặc các loại hỗn hợp sẽ làm cho toàn bộ bom script ... trong trường hợp của tôi, tôi không muốn nó đi xa hơn nữa. Tôi sẽ chơi xung quanh với giải pháp của mrucci và regex của Duffy - họ có vẻ mạnh mẽ nhất trong khuôn khổ bash ...


4
Điều này chấp nhận các biểu thức số học như 1+1, nhưng từ chối một số số nguyên dương với 0s hàng đầu (vì 08là hằng số bát phân không hợp lệ).
Gilles 'SO- ngừng trở nên xấu xa'

2
Điều này cũng có các vấn đề khác: 0không phải là một con số và nó có thể bị tiêm mã tùy ý, hãy thử nó : isInteger 'a[$(ls)]'. Ôi
gniourf_gniourf 14/2/2015

1
Và việc mở rộng không $((...))được trích dẫn, một số IFS=123sẽ thay đổi nó.
Isaac
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.