Sebagai tambahan - test && echo "foo" && exit 0 || echo "bar" && exit 1pendekatan yang Anda gunakan mungkin memiliki beberapa efek samping yang tidak diinginkan - jika gema gagal (mungkin output ke FD tertutup), maka exit 0akan dilewati, dan kode kemudian akan mencoba echo "bar". Jika gagal juga, &&kondisinya akan gagal, dan itu bahkan tidak akan dieksekusi exit 1! Menggunakan ifpernyataan aktual daripada &&/ ||kurang rentan terhadap efek samping yang tidak terduga.
Charles Duffy
@CharlesDuffy Itulah jenis pemikiran yang sangat pintar yang hanya bisa dilakukan sebagian besar orang ketika mereka harus melacak bug berbulu ...! Saya tidak pernah berpikir gema dapat mengembalikan kegagalan.
Camilo Martin
6
Agak terlambat ke pesta, tapi aku tahu tentang bahaya yang ditulis Charles, karena aku harus melewati mereka beberapa waktu yang lalu juga. Jadi, inilah 100% pembuktian bodoh (dan mudah dibaca) untuk Anda: [[ $1 =~ "^[0-9]+$" ]] && { echo "number"; exit 0; } || { echo "not a number"; exit 1; }Kurung keriting menunjukkan bahwa segala sesuatu TIDAK boleh dieksekusi dalam sebuah subkulit (yang pastinya akan seperti itu dengan ()menggunakan tanda kurung sebagai gantinya). Peringatan: Jangan pernah melewatkan titik koma terakhir . Kalau tidak, Anda mungkin bashakan mencetak pesan kesalahan paling jelek (dan paling tidak berguna) ...
syntaxerror
5
Ini tidak berfungsi di Ubuntu, kecuali jika Anda tidak menghapus tanda kutip. Jadi seharusnya[[ 12345 =~ ^[0-9]+$ ]] && echo OKKK || echo NOOO
Treviño
4
Anda harus lebih spesifik tentang apa yang Anda maksud dengan "angka" . Bilangan bulat? Nomor titik tetap? Notasi ilmiah ("e")? Apakah ada rentang yang diperlukan (misalnya nilai 64-bit yang tidak ditandatangani), atau apakah Anda mengizinkan angka apa pun yang dapat ditulis?
Toby Speight
Jawaban:
803
Salah satu pendekatan adalah menggunakan ekspresi reguler, seperti:
re='^[0-9]+$'if![[ $yournumber =~ $re ]];then
echo "error: Not a number">&2; exit 1fi
Jika nilainya belum tentu bilangan bulat, pertimbangkan untuk mengubah regex dengan tepat; contohnya:
+1 untuk pendekatan ini, tetapi berhati-hatilah dengan desimal, lakukan tes ini dengan, misalnya, kesalahan "1,0" atau "1,0" mencetak ": Bukan angka".
sourcerebels
15
Saya menemukan '' exec> & 2; gaung ... 'agak konyol. Just '' echo ...> & 2 ''
lhunath
5
@ Ben, apakah Anda benar-benar ingin menangani lebih dari satu tanda minus? Saya akan membuatnya ^-?daripada ^-*kecuali Anda benar-benar melakukan pekerjaan untuk menangani beberapa inversi dengan benar.
Charles Duffy
4
@SandraSchlichting Membuat semua keluaran di masa mendatang pergi ke stderr. Tidak benar-benar menunjuk ke sini, di mana hanya ada satu gema, tapi itu kebiasaan saya cenderung masuk ke kasus di mana pesan kesalahan span beberapa baris.
Charles Duffy
29
Saya tidak yakin mengapa ekspresi reguler harus disimpan dalam variabel, tetapi jika itu demi kompatibilitas saya tidak berpikir itu perlu. Anda hanya bisa menerapkan ekspresi langsung: [[ $yournumber =~ ^[0-9]+$ ]].
konsolebox
284
Tanpa bashism (bekerja bahkan di System V sh),
case $string in''|*[!0-9]*) echo bad ;;*) echo good ;;esac
Ini menolak string kosong dan string berisi non-digit, menerima yang lainnya.
Angka negatif atau floating-point membutuhkan beberapa pekerjaan tambahan. Gagasannya adalah untuk mengecualikan -/ .dalam pola "buruk" pertama dan menambahkan lebih banyak pola "buruk" yang mengandung penggunaannya yang tidak tepat ( ?*-*/ *.*.*)
+1 - ini adalah cara portabel yang idiomatis untuk kembali ke cangkang Bourne asli, dan memiliki dukungan bawaan untuk wildcard gaya-global. Jika Anda berasal dari bahasa pemrograman lain, sepertinya menakutkan, tetapi jauh lebih elegan daripada mengatasi kerapuhan berbagai masalah penawaran dan masalah kompatibilitas mundur / menyamping yang tak berujung denganif test ...
tripleee
6
Anda dapat mengubah baris pertama ke ${string#-}(yang tidak bekerja di shell Bourne antik, tetapi bekerja di shell POSIX) untuk menerima bilangan bulat negatif.
Gilles 'SANGAT berhenti menjadi jahat'
4
Juga, ini mudah diperluas ke float - cukup tambahkan '.' | *.*.*ke pola yang tidak diizinkan, dan tambahkan titik ke karakter yang diizinkan. Demikian pula, Anda dapat mengizinkan tanda opsional sebelumnya, meskipun kemudian saya lebih suka case ${string#[-+]}mengabaikan tanda itu.
@Dor Kutipan tidak diperlukan, karena perintah case tidak melakukan pemisahan kata dan pembuatan pathname pada kata itu. (Namun, ekspansi dalam pola kasus mungkin perlu mengutip karena menentukan apakah karakter yang cocok dengan pola itu literal atau khusus.)
jilles
193
Solusi berikut juga dapat digunakan dalam cangkang dasar seperti Bourne tanpa perlu ekspresi reguler. Pada dasarnya setiap operasi evaluasi nilai numerik menggunakan non-angka akan menghasilkan kesalahan yang secara implisit akan dianggap salah dalam shell:
"$var"-eq "$var"
seperti dalam:
#!/bin/bash
var=a
if[-n "$var"]&&["$var"-eq "$var"]2>/dev/null;then
echo number
else
echo not a number
fi
Anda juga dapat menguji untuk $? kode pengembalian operasi yang lebih eksplisit:
[-n "$var"]&&["$var"-eq "$var"]2>/dev/null
if[ $?-ne 0];then
echo $var is not number
fi
Ada pengalihan kesalahan standar untuk menyembunyikan pesan "ekspresi integer yang diharapkan" yang dicetak bash seandainya kita tidak memiliki nomor.
CAVEATS (terima kasih untuk komentar di bawah):
Angka dengan titik desimal tidak diidentifikasi sebagai "angka" yang valid
Menggunakan [[ ]]alih-alih [ ]akan selalu dievaluasitrue
Sebagian besar cangkang non-Bash akan selalu mengevaluasi ekspresi ini sebagai true
Perilaku di Bash tidak berdokumen dan karenanya dapat berubah tanpa peringatan
Jika nilai termasuk spasi setelah angka (misalnya "1 a") menghasilkan kesalahan, seperti bash: [[: 1 a: syntax error in expression (error token is "a")
Jika nilainya sama dengan var-name (mis. I = "i"), menghasilkan kesalahan, seperti bash: [[: i: expression recursion level exceeded (error token is "i")
Saya masih merekomendasikan ini (tetapi dengan variabel yang dikutip untuk memungkinkan string kosong), karena hasilnya dijamin dapat digunakan sebagai angka di Bash, tidak peduli apa.
l0b0
21
Berhati-hatilah untuk menggunakan kurung tunggal; [[ a -eq a ]]bernilai true (kedua argumen dikonversikan ke nol)
Tgr
3
Sangat bagus! Perhatikan ini hanya berfungsi untuk bilangan bulat, bukan angka apa pun. Saya perlu memeriksa satu argumen yang harus berupa bilangan bulat, jadi ini bekerja dengan baik:if ! [ $# -eq 1 -o "$1" -eq "$1" ] 2>/dev/null; then
haridsv
6
Saya akan sangat menyarankan terhadap metode ini karena jumlah cangkang yang tidak signifikan yang [dibangun akan mengevaluasi argumen sebagai aritmatika. Itu benar dalam ksh93 dan mksh. Selanjutnya, karena kedua array dukungan tersebut, ada peluang mudah untuk injeksi kode. Gunakan pencocokan pola sebagai gantinya.
ormaaj
3
@AlbertoZaccagni, dalam rilis bash saat ini, nilai-nilai ini ditafsirkan dengan aturan konteks numerik hanya untuk [[ ]]tetapi tidak untuk [ ]. Yang mengatakan, perilaku ini tidak ditentukan oleh standar POSIX untuk testdan dalam dokumentasi bash sendiri; versi masa depan dari bash dapat memodifikasi perilaku agar sesuai dengan ksh tanpa melanggar janji perilaku yang didokumentasikan, jadi mengandalkan perilaku yang ada saat ini tidak dijamin aman.
Charles Duffy
43
Ini menguji apakah suatu bilangan merupakan bilangan bulat non-negatif dan keduanya tidak tergantung pada shell (yaitu tanpa bashisme) dan hanya menggunakan built-in shell:
SALAH.
Karena jawaban pertama ini (di bawah) memungkinkan untuk bilangan bulat dengan karakter di dalamnya selama yang pertama tidak pertama dalam variabel.
[-z "${num##[0-9]*}"]&& echo "is a number"|| echo "is not a number";
BENAR .
Sebagaimana jilles berkomentar dan menyarankan dalam jawabannya, ini adalah cara yang benar untuk melakukannya dengan menggunakan pola shell.
[!-z "${num##*[!0-9]*}"]&& echo "is a number"|| echo "is not a number";
Ini tidak berfungsi dengan baik, ia menerima string apa pun yang dimulai dengan angka. Perhatikan bahwa KATA dalam $ {VAR ## WORD} dan sejenisnya adalah pola shell, bukan ekspresi reguler.
jilles
2
Bisakah Anda menerjemahkan ungkapan itu ke dalam bahasa Inggris? Saya benar-benar ingin menggunakannya, tetapi saya tidak cukup memahaminya untuk mempercayainya, bahkan setelah membaca halaman manual bash.
CivFan
2
*[!0-9]*adalah pola yang cocok dengan semua string dengan setidaknya 1 karakter non-digit. ${num##*[!0-9]*}adalah "perluasan parameter" tempat kami mengambil konten numvariabel dan menghapus string terpanjang yang cocok dengan pola. Jika hasil ekspansi parameter tidak kosong ( ! [ -z ${...} ]) maka itu adalah angka karena tidak mengandung karakter non-digit.
mrucci
Sayangnya ini gagal jika ada angka dalam argumen, meskipun itu bukan angka yang valid. Misalnya "exam1ple" atau "a2b".
Glenn, saya menghapus shopt -s extglobdari posting Anda (yang saya undur, itu salah satu jawaban favorit saya di sini), karena dalam Conditional Constructs Anda dapat membaca: Ketika operator ==dan !=digunakan, string di sebelah kanan operator dianggap sebagai pola dan cocok sesuai dengan aturan yang dijelaskan di bawah ini dalam Pola Pencocokan , seolah-olah extglobopsi shell diaktifkan. Saya harap kamu tidak keberatan!
gniourf_gniourf
Dalam konteks seperti itu, Anda tidak perlu shopt extglob... itu hal yang baik untuk diketahui!
gniourf_gniourf
Bekerja dengan baik untuk bilangan bulat sederhana.
2
@Jdamian: Anda benar, ini ditambahkan di Bash 4.1 (yang dirilis pada akhir 2009 ... Bash 3.2 dirilis pada 2006 ... sekarang perangkat lunak antik, maaf untuk mereka yang terjebak di masa lalu). Juga, Anda bisa berpendapat bahwa extglobs mana yang diperkenalkan dalam versi 2.02 (dirilis pada tahun 1998), dan tidak berfungsi dalam versi <2.02 ... Sekarang komentar Anda di sini akan berfungsi sebagai peringatan tentang versi yang lebih lama.
gniourf_gniourf
1
Variabel di dalam [[...]]tidak tunduk pada pemisahan kata atau ekspansi glob.
glenn jackman
26
Saya terkejut dengan solusi yang langsung mem-parsing format angka di shell. shell tidak cocok untuk ini, karena DSL untuk mengendalikan file dan proses. Ada banyak pengurai yang sedikit lebih rendah ke bawah, misalnya:
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}
isnumber(){ printf '%f' "$1" &>/dev/null && echo "this is a number" || echo "not a number"; }
Gilles Quenot
4
@sputnick versi Anda memecah semantik nilai kembali yang melekat (dan berguna) dari fungsi asli. Jadi, alih-alih, tinggalkan saja fungsi apa adanya, dan gunakan:isnumber 23 && echo "this is a number" || echo "not a number"
michael
4
Bukankah ini seharusnya juga 2>/dev/null, sehingga isnumber "foo"tidak mencemari stderr?
gioele
4
Untuk memanggil shell modern seperti bash "DSL untuk mengendalikan file dan proses" mengabaikan bahwa mereka digunakan untuk lebih dari itu - beberapa distro telah membangun seluruh manajer paket dan antarmuka web di atasnya (seburuk mungkin). File batch sesuai dengan deskripsi Anda, karena pengaturan variabel sekalipun ada yang sulit.
Camilo Martin
7
Lucu bahwa Anda mencoba menjadi pintar dengan menyalin beberapa idiom dari bahasa lain. Sayangnya ini tidak bekerja di shell. Kerang sangat istimewa, dan tanpa pengetahuan yang kuat tentang mereka, Anda cenderung menulis kode yang rusak. Kode Anda rusak: isnumber "'a"akan kembali benar. Ini didokumentasikan dalam spesifikasi POSIX di mana Anda akan membaca: Jika karakter utama adalah kutipan-tunggal atau kutipan ganda, nilainya akan menjadi nilai numerik dalam kumpulan kode karakter yang mendasari mengikuti kutipan-tunggal atau kutipan ganda .
gniourf_gniourf
16
Saya melihat jawabannya dan ... menyadari bahwa tidak ada yang memikirkan angka FLOAT (dengan titik)!
Menggunakan grep juga bagus.
-E berarti extended regexp
-q berarti tenang (tidak menggema)
-qE adalah kombinasi keduanya.
Solusi menggunakan awk oleh triple_r dan tripleee berfungsi dengan float.
Ken Jackson
Terima kasih untuk ini dan poin yang sangat bagus! Karena pertanyaannya sebenarnya bagaimana memeriksa apakah itu angka dan bukan hanya bilangan bulat.
Tanasis
Saya juga berterima kasih kepada Tanasis! Mari kita selalu saling membantu.
Sergio Abreu
12
Cukup tindak lanjuti ke @mary. Tetapi karena saya tidak memiliki cukup perwakilan, tidak dapat memposting ini sebagai komentar untuk posting itu. Bagaimanapun, inilah yang saya gunakan:
isnum(){ awk -v a="$1"'BEGIN {print (a == a + 0)}';}
Fungsi akan mengembalikan "1" jika argumennya adalah angka, jika tidak akan mengembalikan "0". Ini berfungsi untuk bilangan bulat dan juga mengapung. Penggunaannya adalah seperti:
n=-2.05e+07
res=`isnum "$n"`if["$res"=="1"];then
echo "$n is a number"else
echo "$n is not a number"fi
Mencetak nomor kurang berguna daripada mengatur kode keluar. 'BEGIN { exit(1-(a==a+0)) }'sedikit sulit untuk grok tetapi dapat digunakan dalam fungsi yang mengembalikan benar atau salah seperti [, grep -q, dll
tripleee
9
test -z "${i//[0-9]}"&& echo digits || echo no no no
${i//[0-9]}mengganti digit apa pun dalam nilai $idengan string kosong, lihat man -P 'less +/parameter\/' bash. -zmemeriksa apakah string yang dihasilkan memiliki panjang nol.
jika Anda juga ingin mengecualikan kasing saat $ikosong, Anda dapat menggunakan salah satu konstruksi ini:
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
Acungan jempol terutama untuk man -P 'less +/parameter\/' bashbagian itu. Belajar sesuatu yang baru setiap hari. :)
David
@sjas Anda dapat dengan mudah menambahkan \-ekspresi reguler untuk mengatasi masalah ini. Gunakan [0-9\-\.\+]untuk menghitung pelampung dan nomor yang ditandatangani.
Untuk masalah saya, saya hanya perlu memastikan bahwa pengguna tidak sengaja memasukkan beberapa teks sehingga saya mencoba untuk membuatnya tetap sederhana dan mudah dibaca
isNumber(){(( $1 ))2>/dev/null
}
Menurut halaman manual ini cukup banyak melakukan apa yang saya inginkan
Jika nilai ekspresi bukan nol, status pengembalian adalah 0
Untuk mencegah pesan kesalahan yang tidak diinginkan untuk string yang "mungkin angka", saya mengabaikan output kesalahan
$ ((2s))
bash:((:2s: value too great for base (error token is "2s")
Ini lebih seperti isInteger. Tapi terima kasih luar biasa!
Naheel
8
Pertanyaan lama, tapi saya hanya ingin memperbaiki solusi saya. Yang ini tidak memerlukan trik shell aneh, atau mengandalkan sesuatu yang belum ada selamanya.
if[-n "$(printf '%s\n' "$var" | sed 's/[0-9]//g')"];then
echo 'is not numeric'else
echo 'is numeric'fi
Pada dasarnya itu hanya menghapus semua digit dari input, dan jika Anda pergi dengan string yang tidak nol, maka itu bukan angka.
Atau untuk variabel dengan mengikuti baris baru atau semacamnya $'0\n\n\n1\n\n\n2\n\n\n3\n'.
gniourf_gniourf
7
Tidak dapat berkomentar, jadi saya akan menambahkan jawaban saya sendiri, yang merupakan ekstensi untuk jawaban glenn jackman menggunakan pencocokan pola bash.
Kebutuhan awal saya adalah mengidentifikasi angka dan membedakan bilangan bulat dan pelampung. Definisi fungsi yang dikurangkan menjadi:
function isInteger(){[[ ${1}==?(-)+([0-9])]]}function isFloat(){[[ ${1}==?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9]))]]}
Saya menggunakan pengujian unit (dengan shUnit2) untuk memvalidasi pola saya berfungsi sebagaimana dimaksud:
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}"donefor 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}
Catatan: Pola isFloat dapat dimodifikasi agar lebih toleran tentang titik desimal ( @(.,)) dan simbol E ( @(Ee)). Unit saya menguji hanya nilai yang bilangan bulat atau mengambang, tetapi tidak ada input yang tidak valid.
baik duplikat, atau mungkin lebih cocok sebagai komentar, jawaban pixelbeat (menggunakan %fmungkin juga lebih baik)
michael
3
Alih-alih memeriksa kode status sebelumnya, mengapa tidak memasukkannya ifsendiri? Itu yang ifterjadi ...if printf "%g" "$var" &> /dev/null; then ...
Camilo Martin
3
Ini memiliki peringatan lain. Ini akan memvalidasi string kosong, dan string seperti 'a.
gniourf_gniourf
6
Jawaban yang jelas telah diberikan oleh @charles Dufy dan lainnya. Solusi bash murni akan menggunakan yang berikut:
string="-12,345"if[["$string"=~^-?[0-9]+[.,]?[0-9]*$ ]]then
echo $string is a number
else
echo $string is not a number
fi
Meskipun untuk bilangan real tidak wajib memiliki nomor sebelum titik radix .
Untuk memberikan dukungan yang lebih menyeluruh tentang angka mengambang dan notasi ilmiah (banyak program di C / Fortran atau yang akan mengekspor float dengan cara ini), tambahan yang bermanfaat untuk baris ini adalah sebagai berikut:
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
Jadi mengarah ke cara untuk membedakan jenis angka, jika Anda mencari jenis tertentu:
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
Catatan: Kita dapat membuat daftar persyaratan sintaksis untuk notasi desimal dan ilmiah, satu untuk memungkinkan koma sebagai titik radix, serta "." Kami kemudian akan menegaskan bahwa harus ada hanya satu titik radix tersebut. Mungkin ada dua tanda +/- dalam float [Ee]. Saya telah belajar beberapa aturan lagi dari karya Aulu, dan menguji terhadap string buruk seperti '' '-' '-E-1' '0-0'. Berikut adalah alat regex / substring / expr saya yang tampaknya bertahan:
Apa versi minimum dari bash? Saya baru saja mendapat bash: operator binary kondisional yang diharapkan bash: kesalahan sintaks dekat token yang tidak terduga `= ~ '
Paul Hargreaves
1
@PaulHargreaves =~ada setidaknya sejauh bash 3.0.
Gilles 'SANGAT berhenti menjadi jahat'
@PaulHargreaves Anda mungkin memiliki masalah dengan operan pertama Anda, mis. Terlalu banyak tanda kutip atau serupa
Joshua Clayton
@JoshuaClayton Saya bertanya tentang versi karena bash sangat tua pada kotak Solaris 7, yang masih kita miliki dan tidak mendukung = ~
Paul Hargreaves
6
Ini dapat dicapai dengan menggunakan grepuntuk melihat apakah variabel tersebut cocok dengan ekspresi reguler yang diperluas.
Bilangan bulat uji 1120:
yournumber=1120if echo "$yournumber"| grep -qE '^[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Keluaran: Valid number.
Tes bukan bilangan bulat 1120a:
yournumber=1120aif echo "$yournumber"| grep -qE '^[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Keluaran: Error: not a number.
Penjelasan
The grep, -Eswitch memungkinkan kita untuk menggunakan ekspresi reguler yang diperluas '^[0-9]+$'. Ekspresi reguler ini berarti variabel hanya boleh []berisi angka 0-9nol hingga sembilan dari ^awal hingga $akhir variabel dan harus memiliki setidaknya +satu karakter.
The grep, yang -qberalih tenang mematikan output apapun apakah atau tidak menemukan apa-apa.
ifmemeriksa status keluar dari grep. Status keluar 0berarti sukses dan yang lebih besar berarti kesalahan. The grepperintah memiliki status keluar dari 0jika menemukan kecocokan dan 1ketika tidak;
Jadi menyatukan semuanya, dalam ifpengujian, kita echovariabel $yournumberdan |pipa itu grepyang dengan -qswitch diam-diam cocok dengan -Eekspresi '^[0-9]+$'ekspresi reguler diperpanjang . Status keluar grepakan 0jika grepberhasil menemukan kecocokan dan 1jika tidak. Jika berhasil mencocokkan, kami echo "Valid number.". Jika gagal mencocokkan, kami echo "Error: not a number.".
Untuk Mengapung atau Ganda
Kami hanya dapat mengubah ekspresi reguler dari '^[0-9]+$'menjadi '^[0-9]*\.?[0-9]+$'untuk mengapung atau ganda.
Test float 1120.01:
yournumber=1120.01if echo "$yournumber"| grep -qE '^[0-9]*\.?[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Keluaran: Valid number.
Test float 11.20.01:
yournumber=11.20.01if echo "$yournumber"| grep -qE '^[0-9]*\.?[0-9]+$';then
echo "Valid number."else
echo "Error: not a number."fi
Keluaran: Error: not a number.
Untuk Negatif
Untuk mengizinkan bilangan bulat negatif, cukup ubah ekspresi reguler dari '^[0-9]+$'menjadi '^\-?[0-9]+$'.
Untuk memungkinkan mengapung atau menggandakan negatif, cukup ubah ekspresi reguler dari '^[0-9]*\.?[0-9]+$'menjadi '^\-?[0-9]*\.?[0-9]+$'.
Anda benar, @CharlesDuffy. Terima kasih. Saya membersihkan jawabannya.
Joseph Shih
Benar sekali lagi, @CharlesDuffy. Tetap!
Joseph Shih
1
LGTM; jawaban yang telah diedit memiliki +1 saya. Satu-satunya hal yang akan saya lakukan secara berbeda pada saat ini hanyalah masalah pendapat daripada kebenaran (f / e, menggunakan [-]bukan \-dan [.]bukannya \.sedikit lebih bertele-tele, tetapi itu berarti string Anda tidak harus berubah jika mereka ' digunakan dalam konteks di mana backslash dikonsumsi).
if[[ $VAR =*[[:digit:]]*]];then
echo "$VAR is numeric"else
echo "$VAR is not numeric"fi
Numerik akan menyertakan spasi, titik desimal, dan "e" atau "E" untuk titik mengambang.
Tetapi, jika Anda menentukan nomor heks C-style, yaitu "0xffff" atau "0XFFFF", [[: digit:]] mengembalikan true. Sedikit jebakan di sini, bash memungkinkan Anda melakukan sesuatu seperti "0xAZ00" dan masih menghitungnya sebagai digit (bukankah ini dari beberapa kekhasan aneh dari kompiler GCC yang memungkinkan Anda menggunakan notasi 0x untuk pangkalan selain dari 16 ??? )
Anda mungkin ingin menguji "0x" atau "0X" sebelum menguji apakah itu numerik jika input Anda benar-benar tidak dapat dipercaya, kecuali jika Anda ingin menerima nomor hex. Itu akan dicapai dengan:
if[[ ${VARIABLE:1:2}="0x"]]||[[ ${VARIABLE:1:2}="0X"]];then echo "$VAR is not numeric";fi
[[ $VAR = *[[:digit:]]* ]]akan mengembalikan true jika variabel berisi angka, bukan jika itu adalah bilangan bulat.
glenn jackman
[[ "z3*&" = *[[:digit:]]* ]] && echo "numeric"cetakan numeric. Diuji dalam versi bash 3.2.25(1)-release.
Jdamian
1
@ultraswadable, solusi Anda mendeteksi string yang berisi, setidaknya, satu digit dikelilingi (atau tidak) oleh karakter lain. Saya menurunkan suara.
Jdamian
Karenanya, pendekatan yang jelas benar adalah untuk membalikkan ini, dan gunakan[[ -n $VAR && $VAR != *[^[:digit:]]* ]]
eschwartz
@ Eschwartz, solusi Anda tidak bekerja dengan angka negatif
Angel
4
Cara paling sederhana adalah memeriksa apakah itu berisi karakter non-digit. Anda mengganti semua karakter digit dengan apa-apa dan memeriksa panjangnya. Jika ada panjang, itu bukan angka.
if[[!-n ${input//[0-9]/}]];then
echo "Input Is A Number"fi
Untuk menangani angka negatif akan membutuhkan pendekatan yang lebih rumit.
Andy
... Atau tanda positif opsional.
tripleee
@ tripleee saya ingin melihat pendekatan Anda jika Anda tahu cara melakukannya.
Andy
4
Karena saya harus mengutak-atik ini dan akhir-akhir ini menyukai pendekatan karttu dengan unit test yang paling. Saya merevisi kode dan menambahkan beberapa solusi lain juga, coba sendiri untuk melihat hasilnya:
Jadi isNumber () termasuk tanda hubung, koma dan notasi eksponensial dan karenanya mengembalikan BENAR pada bilangan bulat & mengapung di mana di sisi lain isFloat () mengembalikan FALSE pada nilai integer dan isInteger () juga mengembalikan FALSE pada float. Untuk kenyamanan Anda semua sebagai satu liners:
Secara pribadi saya akan menghapus functionkata kunci karena tidak melakukan apa pun yang bermanfaat. Juga, saya tidak yakin tentang kegunaan nilai pengembalian. Kecuali ditentukan lain, fungsi akan mengembalikan status keluar dari perintah terakhir, jadi Anda tidak perlu melakukan returnapa pun sendiri.
Tom Fenech
Bagus, memang returnmembingungkan dan membuatnya kurang mudah dibaca. Menggunakan functionkata kunci atau tidak lebih merupakan masalah selera pribadi setidaknya saya menghapusnya dari satu baris untuk menghemat ruang. Terima kasih.
3ronco
Jangan lupa bahwa tanda titik koma diperlukan setelah pengujian untuk versi satu-baris.
Tom Fenech
2
isNumber akan mengembalikan 'true' pada string apa pun yang memiliki nomor di dalamnya.
DrStrangepork
@DrStrangepork Memang, array false_values saya hilang kasus itu. Saya akan memeriksanya. Terima kasih atas petunjuknya.
3ronco
4
Saya menggunakan expr . Ini mengembalikan bukan nol jika Anda mencoba menambahkan nol ke nilai non-numerik:
if expr --"$number"+0>/dev/null 2>&1then
echo "$number is a number"else
echo "$number isn't a number"fi
Mungkin dimungkinkan untuk menggunakan bc jika Anda membutuhkan non-integer, tetapi saya tidak percaya bcmemiliki perilaku yang sama. Menambahkan nol ke non-angka membuat Anda nol dan mengembalikan nilai nol juga. Mungkin Anda bisa menggabungkan bcdan expr. Gunakan bcuntuk menambahkan nol $number. Jika jawabannya adalah 0, maka cobalah expruntuk memverifikasi bahwa $numberitu bukan nol.
Ini agak buruk. Untuk membuatnya sedikit lebih baik, Anda harus menggunakan expr -- "$number" + 0; namun ini masih akan berpura-pura 0 isn't a number. Dari man expr:Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null or 0,
gniourf_gniourf
3
Saya suka jawaban Alberto Zaccagni.
if["$var"-eq "$var"]2>/dev/null;then
Prasyarat penting: - tidak ada subkulit yang muncul - tidak ada parser RE yang dipanggil - sebagian besar aplikasi shell tidak menggunakan bilangan real
Tetapi jika $varkompleks (misalnya akses array asosiatif), dan jika angka tersebut akan menjadi bilangan bulat non-negatif (kebanyakan kasus penggunaan), maka ini mungkin lebih efisien?
Saya menggunakan printf sebagai jawaban lain yang disebutkan, jika Anda memberikan string format "% f" atau "% i" printf akan melakukan pengecekan untuk Anda. Lebih mudah daripada menciptakan kembali cek, sintaksinya sederhana dan pendek dan printf ada di mana-mana. Jadi itu pilihan yang layak menurut saya - Anda juga dapat menggunakan ide berikut untuk memeriksa berbagai hal, itu tidak hanya berguna untuk memeriksa angka.
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=1local-i FAIL=0case"${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; }
Sebagai jawaban Charles Duffy berhasil, tetapi hanya digunakanpestaekspresi reguler , dan saya tahu ini lambat, saya ingin menunjukkan cara lain, hanya menggunakanpestaekspansi parameter :
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
if is_num foo;then echo It\'s a number ;else echo Not a number;fiNot a number
if cdIs_num foo;then echo It\'s a number ;else echo Not a number;fiNot a number
if is_num 25;then echo It\'s a number ;else echo Not a number;fiIt'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;fiNot a number
if cdIs_num 3+4;then echo It\'s a number ;else echo Not a number;fiNot a number
if is_num 3.1415;then echo It\'s a number ;else echo Not a number;fiIt's a number
if cdIs_num 3.1415;then echo It\'s a number ;else echo Not a number;fi
It's a number
Baiklah tidak apa apa. Sekarang berapa banyak waktu yang diperlukan untuk semua ini (Pada raspberry pi saya):
time for i in{1..1000};do is_num +3.14159265;done
real 0m2.476s
user 0m1.235s
sys 0m0.000s
Kemudian dengan ekspresi reguler:
time for i in{1..1000};do cdIs_num +3.14159265;done
real 0m4.363s
user 0m2.168s
sys 0m0.000s
Saya setuju, bagaimanapun, saya lebih suka untuk tidak menggunakan regex, ketika saya bisa menggunakan ekspansi parameter ... Penyalahgunaan RE akan membuat skrip bash lebih lambat
F. Hauri
Jawaban diedit: Tidak perlu extglob(dan sedikit lebih cepat)!
F. Hauri
@CharlesDuffy, Pada pi raspberry saya, 1000 iterasi akan mengambil 2,5sec dengan versi saya dan 4,4sec dengan versi Anda!
F. Hauri
Tidak bisa berdebat dengan itu 👍
Charles Duffy
1
Untuk menangkap angka negatif:
if[[ $1 ==?(-)+([0-9.])]]then
echo number
else
echo not a number
fi
Juga, ini membutuhkan globbing diperpanjang untuk diaktifkan terlebih dahulu. Ini adalah fitur Bash-only yang dinonaktifkan secara default.
tripleee
@tripleee extended globbing diaktifkan secara otomatis saat menggunakan == atau! = 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 Elmers
@BadrElmers Terima kasih atas pembaruannya. Ini tampaknya menjadi perilaku baru yang tidak benar di Bash 3.2.57 saya (MacOS Mojave). Saya melihatnya berfungsi seperti yang Anda gambarkan dalam 4.4.
tripleee
1
Anda dapat menggunakan "biarkan" juga seperti ini:
[~]$ 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
[~]$
Tapi saya lebih suka menggunakan operator "= ~" Bash 3+ seperti beberapa jawaban di utas ini.
Downvote karena kurangnya penjelasan. Bagaimana cara kerjanya? Terlihat rumit dan rapuh, dan tidak jelas input apa yang akan diterima. (Misalnya, apakah menghilangkan spasi sangat penting? Mengapa? Ia akan mengatakan angka dengan ruang yang disematkan adalah angka yang valid, yang mungkin tidak diinginkan.)
tripleee
1
Melakukan hal yang sama di sini dengan ekspresi reguler yang menguji seluruh bagian dan bagian desimal, dipisahkan dengan titik.
Bisakah Anda menjelaskan mengapa jawaban Anda secara fundamental berbeda dari jawaban lama lainnya, misalnya, jawaban Charles Duffy? Yah, jawaban Anda sebenarnya rusak karena memvalidasi satu periode.
gniourf_gniourf
tidak yakin untuk memahami satu periode di sini ... itu adalah satu atau nol periode yang diharapkan .... Tapi benar tidak ada yang berbeda secara mendasar, hanya menemukan regex lebih mudah dibaca.
Jerome
juga menggunakan * harus cocok dengan lebih banyak kasus dunia nyata
Jerome
Masalahnya adalah Anda mencocokkan string kosong a=''dan string yang hanya berisi titik a='.'sehingga kode Anda agak rusak ...
gniourf_gniourf
0
Saya menggunakan yang berikut (untuk bilangan bulat):
## ##### 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
Hampir benar (Anda menerima string kosong) tetapi sangat rumit sampai-sampai kebingungan.
Gilles 'SANGAT berhenti menjadi jahat'
2
Salah: Anda menerima -n, dll. (Karena echo), dan Anda menerima variabel dengan mengikuti baris baru (karena $(...)). Dan omong-omong, printbukan perintah shell yang valid.
gniourf_gniourf
0
Saya mencoba resep ultrasawblade karena rasanya paling praktis bagi saya, dan tidak bisa membuatnya bekerja. Pada akhirnya saya menemukan cara lain, berdasarkan yang lain dalam penggantian parameter, kali ini dengan penggantian regex:
[["${var//*([[:digit:]])}"]];&& echo "$var is not numeric"|| echo "$var is numeric"
Ini menghapus setiap: digit: karakter kelas dalam $ var dan memeriksa apakah kita dibiarkan dengan string kosong, yang berarti bahwa aslinya hanya angka.
Yang saya sukai dari yang satu ini adalah footprint dan fleksibilitasnya yang kecil. Dalam bentuk ini hanya berfungsi untuk bilangan bulat basis 10 tanpa batas, meskipun tentunya Anda dapat menggunakan pencocokan pola yang sesuai dengan kebutuhan lain.
Membaca solusi mrucci, tampilannya hampir sama dengan milik saya, tetapi menggunakan penggantian string biasa alih-alih "gaya sed". Keduanya menggunakan aturan yang sama untuk pencocokan pola dan, AFAIK, solusi yang dapat dipertukarkan.
ata
sedadalah POSIX, sedangkan solusi Anda adalah bash. Keduanya memiliki kegunaannya
v010dya
0
Cepat & Kotor: Saya tahu ini bukan cara yang paling elegan, tapi saya biasanya hanya menambahkan nol dan menguji hasilnya. seperti itu:
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)) akan mengembalikan 0 atau bom jika $ 1 BUKAN bilangan bulat. sebagai contoh:
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
return1fi
showError $ERROR
}
CATATAN: Saya kira saya tidak pernah berpikir untuk memeriksa pelampung atau jenis campuran yang akan membuat seluruh skrip bom ... dalam kasus saya, saya tidak ingin itu berjalan lebih jauh. Saya akan bermain-main dengan solusi mrucci dan regex Duffy - mereka tampaknya paling kuat dalam kerangka bash ...
test && echo "foo" && exit 0 || echo "bar" && exit 1
pendekatan yang Anda gunakan mungkin memiliki beberapa efek samping yang tidak diinginkan - jika gema gagal (mungkin output ke FD tertutup), makaexit 0
akan dilewati, dan kode kemudian akan mencobaecho "bar"
. Jika gagal juga,&&
kondisinya akan gagal, dan itu bahkan tidak akan dieksekusiexit 1
! Menggunakanif
pernyataan aktual daripada&&
/||
kurang rentan terhadap efek samping yang tidak terduga.[[ $1 =~ "^[0-9]+$" ]] && { echo "number"; exit 0; } || { echo "not a number"; exit 1; }
Kurung keriting menunjukkan bahwa segala sesuatu TIDAK boleh dieksekusi dalam sebuah subkulit (yang pastinya akan seperti itu dengan()
menggunakan tanda kurung sebagai gantinya). Peringatan: Jangan pernah melewatkan titik koma terakhir . Kalau tidak, Anda mungkinbash
akan mencetak pesan kesalahan paling jelek (dan paling tidak berguna) ...[[ 12345 =~ ^[0-9]+$ ]] && echo OKKK || echo NOOO
Jawaban:
Salah satu pendekatan adalah menggunakan ekspresi reguler, seperti:
Jika nilainya belum tentu bilangan bulat, pertimbangkan untuk mengubah regex dengan tepat; contohnya:
... atau, untuk menangani angka dengan tanda:
sumber
^-?
daripada^-*
kecuali Anda benar-benar melakukan pekerjaan untuk menangani beberapa inversi dengan benar.[[ $yournumber =~ ^[0-9]+$ ]]
.Tanpa bashism (bekerja bahkan di System V sh),
Ini menolak string kosong dan string berisi non-digit, menerima yang lainnya.
Angka negatif atau floating-point membutuhkan beberapa pekerjaan tambahan. Gagasannya adalah untuk mengecualikan
-
/.
dalam pola "buruk" pertama dan menambahkan lebih banyak pola "buruk" yang mengandung penggunaannya yang tidak tepat (?*-*
/*.*.*
)sumber
if test ...
${string#-}
(yang tidak bekerja di shell Bourne antik, tetapi bekerja di shell POSIX) untuk menerima bilangan bulat negatif.'.' | *.*.*
ke pola yang tidak diizinkan, dan tambahkan titik ke karakter yang diizinkan. Demikian pula, Anda dapat mengizinkan tanda opsional sebelumnya, meskipun kemudian saya lebih sukacase ${string#[-+]}
mengabaikan tanda itu.Solusi berikut juga dapat digunakan dalam cangkang dasar seperti Bourne tanpa perlu ekspresi reguler. Pada dasarnya setiap operasi evaluasi nilai numerik menggunakan non-angka akan menghasilkan kesalahan yang secara implisit akan dianggap salah dalam shell:
seperti dalam:
Anda juga dapat menguji untuk $? kode pengembalian operasi yang lebih eksplisit:
Ada pengalihan kesalahan standar untuk menyembunyikan pesan "ekspresi integer yang diharapkan" yang dicetak bash seandainya kita tidak memiliki nomor.
CAVEATS (terima kasih untuk komentar di bawah):
[[ ]]
alih-alih[ ]
akan selalu dievaluasitrue
true
bash: [[: 1 a: syntax error in expression (error token is "a")
bash: [[: i: expression recursion level exceeded (error token is "i")
sumber
[[ a -eq a ]]
bernilai true (kedua argumen dikonversikan ke nol)if ! [ $# -eq 1 -o "$1" -eq "$1" ] 2>/dev/null; then
[
dibangun akan mengevaluasi argumen sebagai aritmatika. Itu benar dalam ksh93 dan mksh. Selanjutnya, karena kedua array dukungan tersebut, ada peluang mudah untuk injeksi kode. Gunakan pencocokan pola sebagai gantinya.[[ ]]
tetapi tidak untuk[ ]
. Yang mengatakan, perilaku ini tidak ditentukan oleh standar POSIX untuktest
dan dalam dokumentasi bash sendiri; versi masa depan dari bash dapat memodifikasi perilaku agar sesuai dengan ksh tanpa melanggar janji perilaku yang didokumentasikan, jadi mengandalkan perilaku yang ada saat ini tidak dijamin aman.Ini menguji apakah suatu bilangan merupakan bilangan bulat non-negatif dan keduanya tidak tergantung pada shell (yaitu tanpa bashisme) dan hanya menggunakan built-in shell:
SALAH.
Karena jawaban pertama ini (di bawah) memungkinkan untuk bilangan bulat dengan karakter di dalamnya selama yang pertama tidak pertama dalam variabel.
BENAR .
Sebagaimana jilles berkomentar dan menyarankan dalam jawabannya, ini adalah cara yang benar untuk melakukannya dengan menggunakan pola shell.
sumber
*[!0-9]*
adalah pola yang cocok dengan semua string dengan setidaknya 1 karakter non-digit.${num##*[!0-9]*}
adalah "perluasan parameter" tempat kami mengambil kontennum
variabel dan menghapus string terpanjang yang cocok dengan pola. Jika hasil ekspansi parameter tidak kosong (! [ -z ${...} ]
) maka itu adalah angka karena tidak mengandung karakter non-digit.122s
:-(
Tidak ada yang menyarankan pencocokan pola diperpanjang bash :
sumber
shopt -s extglob
dari posting Anda (yang saya undur, itu salah satu jawaban favorit saya di sini), karena dalam Conditional Constructs Anda dapat membaca: Ketika operator==
dan!=
digunakan, string di sebelah kanan operator dianggap sebagai pola dan cocok sesuai dengan aturan yang dijelaskan di bawah ini dalam Pola Pencocokan , seolah-olahextglob
opsi shell diaktifkan. Saya harap kamu tidak keberatan!shopt extglob
... itu hal yang baik untuk diketahui![[...]]
tidak tunduk pada pemisahan kata atau ekspansi glob.Saya terkejut dengan solusi yang langsung mem-parsing format angka di shell. shell tidak cocok untuk ini, karena DSL untuk mengendalikan file dan proses. Ada banyak pengurai yang sedikit lebih rendah ke bawah, misalnya:
Ubah '% f' ke format apa pun yang Anda butuhkan.
sumber
isnumber(){ printf '%f' "$1" &>/dev/null && echo "this is a number" || echo "not a number"; }
isnumber 23 && echo "this is a number" || echo "not a number"
2>/dev/null
, sehinggaisnumber "foo"
tidak mencemari stderr?isnumber "'a"
akan kembali benar. Ini didokumentasikan dalam spesifikasi POSIX di mana Anda akan membaca: Jika karakter utama adalah kutipan-tunggal atau kutipan ganda, nilainya akan menjadi nilai numerik dalam kumpulan kode karakter yang mendasari mengikuti kutipan-tunggal atau kutipan ganda .Saya melihat jawabannya dan ... menyadari bahwa tidak ada yang memikirkan angka FLOAT (dengan titik)!
Menggunakan grep juga bagus.
-E berarti extended regexp
-q berarti tenang (tidak menggema)
-qE adalah kombinasi keduanya.
Untuk menguji langsung di baris perintah:
Menggunakan dalam skrip bash:
Untuk mencocokkan JUST integer, gunakan ini:
sumber
Cukup tindak lanjuti ke @mary. Tetapi karena saya tidak memiliki cukup perwakilan, tidak dapat memposting ini sebagai komentar untuk posting itu. Bagaimanapun, inilah yang saya gunakan:
Fungsi akan mengembalikan "1" jika argumennya adalah angka, jika tidak akan mengembalikan "0". Ini berfungsi untuk bilangan bulat dan juga mengapung. Penggunaannya adalah seperti:
sumber
'BEGIN { exit(1-(a==a+0)) }'
sedikit sulit untuk grok tetapi dapat digunakan dalam fungsi yang mengembalikan benar atau salah seperti[
,grep -q
, dll${i//[0-9]}
mengganti digit apa pun dalam nilai$i
dengan string kosong, lihatman -P 'less +/parameter\/' bash
.-z
memeriksa apakah string yang dihasilkan memiliki panjang nol.jika Anda juga ingin mengecualikan kasing saat
$i
kosong, Anda dapat menggunakan salah satu konstruksi ini:sumber
man -P 'less +/parameter\/' bash
bagian itu. Belajar sesuatu yang baru setiap hari. :)\-
ekspresi reguler untuk mengatasi masalah ini. Gunakan[0-9\-\.\+]
untuk menghitung pelampung dan nomor yang ditandatangani.echo $i | python -c $'import sys\ntry:\n float(sys.stdin.read().rstrip())\nexcept:\n sys.exit(1)' && echo yes || echo no
Untuk masalah saya, saya hanya perlu memastikan bahwa pengguna tidak sengaja memasukkan beberapa teks sehingga saya mencoba untuk membuatnya tetap sederhana dan mudah dibaca
Menurut halaman manual ini cukup banyak melakukan apa yang saya inginkan
Untuk mencegah pesan kesalahan yang tidak diinginkan untuk string yang "mungkin angka", saya mengabaikan output kesalahan
sumber
Pertanyaan lama, tapi saya hanya ingin memperbaiki solusi saya. Yang ini tidak memerlukan trik shell aneh, atau mengandalkan sesuatu yang belum ada selamanya.
Pada dasarnya itu hanya menghapus semua digit dari input, dan jika Anda pergi dengan string yang tidak nol, maka itu bukan angka.
sumber
var
.$'0\n\n\n1\n\n\n2\n\n\n3\n'
.Tidak dapat berkomentar, jadi saya akan menambahkan jawaban saya sendiri, yang merupakan ekstensi untuk jawaban glenn jackman menggunakan pencocokan pola bash.
Kebutuhan awal saya adalah mengidentifikasi angka dan membedakan bilangan bulat dan pelampung. Definisi fungsi yang dikurangkan menjadi:
Saya menggunakan pengujian unit (dengan shUnit2) untuk memvalidasi pola saya berfungsi sebagaimana dimaksud:
Catatan: Pola isFloat dapat dimodifikasi agar lebih toleran tentang titik desimal (
@(.,)
) dan simbol E (@(Ee)
). Unit saya menguji hanya nilai yang bilangan bulat atau mengambang, tetapi tidak ada input yang tidak valid.sumber
Saya akan mencoba ini:
Catatan: ini mengenali nan dan inf sebagai angka.
sumber
%f
mungkin juga lebih baik)if
sendiri? Itu yangif
terjadi ...if printf "%g" "$var" &> /dev/null; then ...
'a
.Jawaban yang jelas telah diberikan oleh @charles Dufy dan lainnya. Solusi bash murni akan menggunakan yang berikut:
Meskipun untuk bilangan real tidak wajib memiliki nomor sebelum titik radix .
Untuk memberikan dukungan yang lebih menyeluruh tentang angka mengambang dan notasi ilmiah (banyak program di C / Fortran atau yang akan mengekspor float dengan cara ini), tambahan yang bermanfaat untuk baris ini adalah sebagai berikut:
Jadi mengarah ke cara untuk membedakan jenis angka, jika Anda mencari jenis tertentu:
Catatan: Kita dapat membuat daftar persyaratan sintaksis untuk notasi desimal dan ilmiah, satu untuk memungkinkan koma sebagai titik radix, serta "." Kami kemudian akan menegaskan bahwa harus ada hanya satu titik radix tersebut. Mungkin ada dua tanda +/- dalam float [Ee]. Saya telah belajar beberapa aturan lagi dari karya Aulu, dan menguji terhadap string buruk seperti '' '-' '-E-1' '0-0'. Berikut adalah alat regex / substring / expr saya yang tampaknya bertahan:
sumber
Jangan lupa
-
untuk memasukkan angka negatif!sumber
=~
ada setidaknya sejauh bash 3.0.Ini dapat dicapai dengan menggunakan
grep
untuk melihat apakah variabel tersebut cocok dengan ekspresi reguler yang diperluas.Bilangan bulat uji
1120
:Keluaran:
Valid number.
Tes bukan bilangan bulat
1120a
:Keluaran:
Error: not a number.
Penjelasan
grep
,-E
switch memungkinkan kita untuk menggunakan ekspresi reguler yang diperluas'^[0-9]+$'
. Ekspresi reguler ini berarti variabel hanya boleh[]
berisi angka0-9
nol hingga sembilan dari^
awal hingga$
akhir variabel dan harus memiliki setidaknya+
satu karakter.grep
, yang-q
beralih tenang mematikan output apapun apakah atau tidak menemukan apa-apa.if
memeriksa status keluar darigrep
. Status keluar0
berarti sukses dan yang lebih besar berarti kesalahan. Thegrep
perintah memiliki status keluar dari0
jika menemukan kecocokan dan1
ketika tidak;Jadi menyatukan semuanya, dalam
if
pengujian, kitaecho
variabel$yournumber
dan|
pipa itugrep
yang dengan-q
switch diam-diam cocok dengan-E
ekspresi'^[0-9]+$'
ekspresi reguler diperpanjang . Status keluargrep
akan0
jikagrep
berhasil menemukan kecocokan dan1
jika tidak. Jika berhasil mencocokkan, kamiecho "Valid number."
. Jika gagal mencocokkan, kamiecho "Error: not a number."
.Untuk Mengapung atau Ganda
Kami hanya dapat mengubah ekspresi reguler dari
'^[0-9]+$'
menjadi'^[0-9]*\.?[0-9]+$'
untuk mengapung atau ganda.Test float
1120.01
:Keluaran:
Valid number.
Test float
11.20.01
:Keluaran:
Error: not a number.
Untuk Negatif
Untuk mengizinkan bilangan bulat negatif, cukup ubah ekspresi reguler dari
'^[0-9]+$'
menjadi'^\-?[0-9]+$'
.Untuk memungkinkan mengapung atau menggandakan negatif, cukup ubah ekspresi reguler dari
'^[0-9]*\.?[0-9]+$'
menjadi'^\-?[0-9]*\.?[0-9]+$'
.sumber
[-]
bukan\-
dan[.]
bukannya\.
sedikit lebih bertele-tele, tetapi itu berarti string Anda tidak harus berubah jika mereka ' digunakan dalam konteks di mana backslash dikonsumsi).http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_03.html
Anda juga dapat menggunakan kelas karakter bash.
Numerik akan menyertakan spasi, titik desimal, dan "e" atau "E" untuk titik mengambang.
Tetapi, jika Anda menentukan nomor heks C-style, yaitu "0xffff" atau "0XFFFF", [[: digit:]] mengembalikan true. Sedikit jebakan di sini, bash memungkinkan Anda melakukan sesuatu seperti "0xAZ00" dan masih menghitungnya sebagai digit (bukankah ini dari beberapa kekhasan aneh dari kompiler GCC yang memungkinkan Anda menggunakan notasi 0x untuk pangkalan selain dari 16 ??? )
Anda mungkin ingin menguji "0x" atau "0X" sebelum menguji apakah itu numerik jika input Anda benar-benar tidak dapat dipercaya, kecuali jika Anda ingin menerima nomor hex. Itu akan dicapai dengan:
sumber
[[ $VAR = *[[:digit:]]* ]]
akan mengembalikan true jika variabel berisi angka, bukan jika itu adalah bilangan bulat.[[ "z3*&" = *[[:digit:]]* ]] && echo "numeric"
cetakannumeric
. Diuji dalam versi bash3.2.25(1)-release
.[[ -n $VAR && $VAR != *[^[:digit:]]* ]]
Cara paling sederhana adalah memeriksa apakah itu berisi karakter non-digit. Anda mengganti semua karakter digit dengan apa-apa dan memeriksa panjangnya. Jika ada panjang, itu bukan angka.
sumber
Karena saya harus mengutak-atik ini dan akhir-akhir ini menyukai pendekatan karttu dengan unit test yang paling. Saya merevisi kode dan menambahkan beberapa solusi lain juga, coba sendiri untuk melihat hasilnya:
Jadi isNumber () termasuk tanda hubung, koma dan notasi eksponensial dan karenanya mengembalikan BENAR pada bilangan bulat & mengapung di mana di sisi lain isFloat () mengembalikan FALSE pada nilai integer dan isInteger () juga mengembalikan FALSE pada float. Untuk kenyamanan Anda semua sebagai satu liners:
sumber
function
kata kunci karena tidak melakukan apa pun yang bermanfaat. Juga, saya tidak yakin tentang kegunaan nilai pengembalian. Kecuali ditentukan lain, fungsi akan mengembalikan status keluar dari perintah terakhir, jadi Anda tidak perlu melakukanreturn
apa pun sendiri.return
membingungkan dan membuatnya kurang mudah dibaca. Menggunakanfunction
kata kunci atau tidak lebih merupakan masalah selera pribadi setidaknya saya menghapusnya dari satu baris untuk menghemat ruang. Terima kasih.Saya menggunakan expr . Ini mengembalikan bukan nol jika Anda mencoba menambahkan nol ke nilai non-numerik:
Mungkin dimungkinkan untuk menggunakan bc jika Anda membutuhkan non-integer, tetapi saya tidak percaya
bc
memiliki perilaku yang sama. Menambahkan nol ke non-angka membuat Anda nol dan mengembalikan nilai nol juga. Mungkin Anda bisa menggabungkanbc
danexpr
. Gunakanbc
untuk menambahkan nol$number
. Jika jawabannya adalah0
, maka cobalahexpr
untuk memverifikasi bahwa$number
itu bukan nol.sumber
expr -- "$number" + 0
; namun ini masih akan berpura-pura0 isn't a number
. Dariman expr
:Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null or 0,
Saya suka jawaban Alberto Zaccagni.
Prasyarat penting: - tidak ada subkulit yang muncul - tidak ada parser RE yang dipanggil - sebagian besar aplikasi shell tidak menggunakan bilangan real
Tetapi jika
$var
kompleks (misalnya akses array asosiatif), dan jika angka tersebut akan menjadi bilangan bulat non-negatif (kebanyakan kasus penggunaan), maka ini mungkin lebih efisien?sumber
Saya menggunakan printf sebagai jawaban lain yang disebutkan, jika Anda memberikan string format "% f" atau "% i" printf akan melakukan pengecekan untuk Anda. Lebih mudah daripada menciptakan kembali cek, sintaksinya sederhana dan pendek dan printf ada di mana-mana. Jadi itu pilihan yang layak menurut saya - Anda juga dapat menggunakan ide berikut untuk memeriksa berbagai hal, itu tidak hanya berguna untuk memeriksa angka.
>$ var=45
>$ (($(check_number $var "${CHECK_INTEGER}" "Error: Found $var - An integer is required."))) && { echo "$var+5" | bc; }
sumber
pesta ekspresi reguler vspesta ekspansi parameter
Sebagai jawaban Charles Duffy berhasil, tetapi hanya digunakanpestaekspresi reguler , dan saya tahu ini lambat, saya ingin menunjukkan cara lain, hanya menggunakanpestaekspansi parameter :
Hanya untuk bilangan bulat positif:
Untuk bilangan bulat:
Kemudian untuk mengambang:
Untuk mencocokkan permintaan awal:
Sekarang sedikit perbandingan:
Ada cek yang sama berdasarkan jawaban Charles Duffy :
Beberapa tes:
Baiklah tidak apa apa. Sekarang berapa banyak waktu yang diperlukan untuk semua ini (Pada raspberry pi saya):
Kemudian dengan ekspresi reguler:
sumber
extglob
(dan sedikit lebih cepat)!Untuk menangkap angka negatif:
sumber
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_005bAnda dapat menggunakan "biarkan" juga seperti ini:
Tapi saya lebih suka menggunakan operator "= ~" Bash 3+ seperti beberapa jawaban di utas ini.
sumber
Hapus
-\?
pola pencocokan grep jika Anda tidak menerima bilangan bulat negatif.sumber
Melakukan hal yang sama di sini dengan ekspresi reguler yang menguji seluruh bagian dan bagian desimal, dipisahkan dengan titik.
sumber
.
a=''
dan string yang hanya berisi titika='.'
sehingga kode Anda agak rusak ...Saya menggunakan yang berikut (untuk bilangan bulat):
sumber
-n
, dll. (Karenaecho
), dan Anda menerima variabel dengan mengikuti baris baru (karena$(...)
). Dan omong-omong,print
bukan perintah shell yang valid.Saya mencoba resep ultrasawblade karena rasanya paling praktis bagi saya, dan tidak bisa membuatnya bekerja. Pada akhirnya saya menemukan cara lain, berdasarkan yang lain dalam penggantian parameter, kali ini dengan penggantian regex:
Ini menghapus setiap: digit: karakter kelas dalam $ var dan memeriksa apakah kita dibiarkan dengan string kosong, yang berarti bahwa aslinya hanya angka.
Yang saya sukai dari yang satu ini adalah footprint dan fleksibilitasnya yang kecil. Dalam bentuk ini hanya berfungsi untuk bilangan bulat basis 10 tanpa batas, meskipun tentunya Anda dapat menggunakan pencocokan pola yang sesuai dengan kebutuhan lain.
sumber
sed
adalah POSIX, sedangkan solusi Anda adalahbash
. Keduanya memiliki kegunaannyaCepat & Kotor: Saya tahu ini bukan cara yang paling elegan, tapi saya biasanya hanya menambahkan nol dan menguji hasilnya. seperti itu:
$ (($ 1 + 0)) akan mengembalikan 0 atau bom jika $ 1 BUKAN bilangan bulat. sebagai contoh:
CATATAN: Saya kira saya tidak pernah berpikir untuk memeriksa pelampung atau jenis campuran yang akan membuat seluruh skrip bom ... dalam kasus saya, saya tidak ingin itu berjalan lebih jauh. Saya akan bermain-main dengan solusi mrucci dan regex Duffy - mereka tampaknya paling kuat dalam kerangka bash ...
sumber
1+1
, tetapi menolak beberapa bilangan bulat positif dengan0
s (karena08
konstanta oktal tidak valid).0
bukan angka, dan tunduk pada kode injeksi sewenang-wenang, mencobanya:isInteger 'a[$(ls)]'
. Ups.$((...))
kutip, angkaIFS=123
akan mengubahnya.