Saya mencoba melakukan sesuatu yang cukup umum: Parse input pengguna dalam skrip shell. Jika pengguna memberikan integer yang valid, skrip melakukan satu hal, dan jika tidak valid, skrip melakukan hal lain. Masalahnya adalah, saya belum menemukan cara yang mudah (dan cukup elegan) untuk melakukan ini - saya tidak ingin memilah-milahnya satu per satu.
Saya tahu ini pasti mudah tetapi saya tidak tahu caranya. Saya bisa melakukannya dalam selusin bahasa, tetapi tidak BASH!
Dalam penelitian saya, saya menemukan ini:
Dan ada jawaban di dalamnya yang berbicara tentang regex, tetapi sejauh yang saya tahu, itu adalah fungsi yang tersedia di C (antara lain). Tetap saja, itu sepertinya jawaban yang bagus jadi saya mencobanya dengan grep, tetapi grep tidak tahu apa yang harus dilakukan dengannya. Saya mencoba -P yang di kotak saya berarti memperlakukannya sebagai PERL regexp - nada. Dasbor E (-E) juga tidak berfungsi. Dan begitu pula -F.
Hanya untuk memperjelas, saya mencoba sesuatu seperti ini, mencari output apa pun - dari sana, saya akan meretas skrip untuk memanfaatkan apa pun yang saya dapatkan. (IOW, saya mengharapkan bahwa input yang tidak sesuai tidak menghasilkan apa-apa saat baris yang valid diulang.)
snafu=$(echo "$2" | grep -E "/^[-+]?(?:\.[0-9]+|(?:0|[1-9][0-9]*)(?:\.[0-9]*)?)$/")
if [ -z "$snafu" ] ;
then
echo "Not an integer - nothing back from the grep"
else
echo "Integer."
fi
Bisakah seseorang mengilustrasikan bagaimana ini paling mudah dilakukan?
Terus terang, ini adalah kekurangan TEST, menurut saya. Ini harus memiliki bendera seperti ini
if [ -I "string" ] ;
then
echo "String is a valid integer."
else
echo "String is not a valid integer."
fi
sumber
[
kompatibel lamatest
;[[
adalah hal baru Bash, dengan lebih banyak operasi dan aturan kutipan yang berbeda. Jika Anda sudah memutuskan untuk tetap menggunakan Bash, lakukan[[
(ini benar-benar jauh lebih bagus); jika Anda membutuhkan portabilitas ke shell lain, hindari[[
sepenuhnya.Jawaban:
^
menunjukkan awal dari pola input-
adalah "-" literal?
berarti "0 atau 1 dari sebelumnya (-
)"+
berarti "1 atau lebih dari sebelumnya ([0-9]
)"$
menunjukkan akhir dari pola inputJadi ekspresi reguler cocok dengan opsional
-
(untuk kasus angka negatif), diikuti dengan satu atau beberapa digit desimal.Referensi :
sumber
+
berarti "1 atau lebih dari sebelumnya", dan$
menunjukkan akhir dari pola input. Jadi ekspresi reguler cocok dengan opsional-
diikuti dengan satu atau beberapa digit desimal.[A-z]
tidak akan hanya memberikanA-Z
dana-z
tetapi juga\
,[
,]
,^
,_
, dan`
.d[g-i]{2}
bisa berakhir tidak hanya cocokdig
tetapi jugadish
dalam susunan yang disarankan oleh jawaban itu (di manash
digraf dianggap sebagai karakter tunggal, disusun setelahnyah
).Wow ... ada banyak solusi bagus di sini !! Dari semua solusi di atas, saya setuju dengan @nortally bahwa menggunakan
-eq
satu liner adalah yang paling keren.Saya menjalankan GNU bash, versi
4.1.5
(Debian). Saya juga telah memeriksa ini di ksh (SunSO 5.10).Ini adalah versi saya untuk memeriksa apakah
$1
itu bilangan bulat atau bukan:Pendekatan ini juga memperhitungkan bilangan negatif, yang beberapa solusi lain akan memiliki hasil negatif yang salah, dan itu akan memungkinkan awalan "+" (misalnya +30) yang jelas merupakan bilangan bulat.
Hasil:
Solusi yang diberikan oleh Ignacio Vazquez-Abrams juga sangat rapi (jika Anda suka regex) setelah dijelaskan. Namun, ini tidak menangani bilangan positif dengan
+
awalan, tetapi dapat dengan mudah diperbaiki seperti di bawah ini:sumber
Terlambat ke pesta di sini. Saya sangat terkejut tidak ada jawaban yang menyebutkan solusi yang paling sederhana, tercepat, dan paling portabel; yang
case
pernyataan.Pemangkasan tanda apa pun sebelum perbandingan terasa seperti sedikit retakan, tetapi itu membuat ekspresi untuk pernyataan kasus jauh lebih sederhana.
sumber
''|*[!0-9]*)
Saya suka solusi yang menggunakan
-eq
pengujian, karena pada dasarnya ini adalah satu baris.Solusi saya sendiri adalah menggunakan perluasan parameter untuk membuang semua angka dan melihat apakah masih ada yang tersisa. (Saya masih menggunakan 3.0, belum pernah menggunakan
[[
atauexpr
sebelumnya, tapi senang bertemu dengan mereka.)sumber
[ -z "${INPUT_STRING//[0-9]}" ]
solusi yang sangat bagus!-eq
solusi memiliki beberapa masalah; lihat di sini: stackoverflow.com/a/808740/1858225Untuk portabilitas ke pra-Bash 3.1 (saat
=~
tes diperkenalkan), gunakanexpr
.expr STRING : REGEX
mencari REGEX berlabuh di awal STRING, menggemakan grup pertama (atau panjang kecocokan, jika tidak ada) dan mengembalikan sukses / gagal. Ini adalah sintaks regex lama, oleh karena itu kelebihannya\
.-\?
berarti "mungkin-
",[0-9]\+
berarti "satu atau lebih digit", dan$
berarti "akhir string".Bash juga mendukung gumpalan yang diperpanjang, meskipun saya tidak ingat dari versi mana dan seterusnya.
@(-|)
berarti "-
atau tidak ada",[0-9]
berarti "digit", dan*([0-9])
berarti "nol atau lebih digit".sumber
awk
,~
adalah operator "pencocokan ekspresi reguler". Di Perl (seperti yang disalin dari C),~
telah digunakan untuk "pelengkap bit", jadi mereka menggunakannya=~
. Notasi ini kemudian disalin ke beberapa bahasa lain. (Perl 5.10 dan Perl 6 menyukai~~
lebih, tapi itu tidak berdampak di sini.) Saya kira Anda dapat melihatnya sebagai semacam perkiraan kesetaraan ...Berikut ini adalah pandangan lain (hanya menggunakan perintah test builtin dan kode kembaliannya):
sumber
$()
denganif
. Ini bekerja:if is_int "$input"
. Selain itu,$[]
formulir sudah tidak digunakan lagi. Gunakan$(())
sebagai gantinya. Di dalam keduanya, tanda dolar dapat dihilangkan: Tandaecho "Integer: $((input))"
kurung kurawal tidak diperlukan di mana pun dalam skrip Anda.test
tampaknya tidak mendukung ini.[[
tidak.[[ 16#aa -eq 16#aa ]] && echo integer
mencetak "integer".[[
mengembalikan positif palsu untuk metode ini; misalnya[[ f -eq f ]]
berhasil. Jadi harus menggunakantest
atau[
.Anda dapat menghapus non-digit dan melakukan perbandingan. Berikut skrip demo:
Seperti inilah keluaran pengujiannya:
sumber
${var//string}
dan${var#string}
dan di bagian bernama "Pencocokan Pola" untuk [^ [: digit:]] `(yang juga dibahas dalamman 7 regex
).match=${match#0*}
tidak tidak menghapus nol terkemuka, itu strip paling banyak satu nol. Menggunakan ekspansi ini hanya dapat dicapai dengan menggunakanextglob
viamatch=${match##+(0)}
.09
bukan bilangan bulat jika Anda menganggap bilangan bulat tidak memiliki nol di depannya. Tesnya adalah apakah input (09
) sama dengan versi yang sudah dibersihkan (9
- integer) dan tidak.Bagi saya, solusi paling sederhana adalah menggunakan variabel di dalam
(())
ekspresi, sebagai berikut:Tentu saja, solusi ini hanya valid jika nilai nol tidak masuk akal untuk aplikasi Anda. Itu benar dalam kasus saya, dan ini jauh lebih sederhana daripada solusi lainnya.
Seperti yang ditunjukkan di komentar, ini bisa membuat Anda terkena serangan eksekusi kode:
(( ))
Operator mengevaluasiVAR
, seperti yang dinyatakan di bagianArithmetic Evaluation
halaman manual bash (1) . Oleh karena itu, Anda tidak boleh menggunakan teknik ini ketika sumber kontenVAR
tidak pasti (Anda juga tidak boleh menggunakan bentuk ekspansi variabel APAPUN, tentunya).sumber
if (( var )); then echo "$var is an int."; fi
VAR='a[$(ls)]'; if ((VAR > 0)); then echo "$VAR is a positive integer"; fi
. Pada titik ini Anda senang saya tidak memasukkan perintah jahat sebagai gantinyals
. Karena OP menyebutkan input pengguna , saya sangat berharap Anda tidak menggunakan ini dengan input pengguna dalam kode produksi!agent007
atau dengan sed:
sumber
test -z "${string//[0-9]/}" && echo "integer" || echo "no integer"
... meskipun itu pada dasarnya menggandakan jawaban Dennis Williamsonif [[ -n "$(printf "%s" "${2}" | sed s/[0-9]//g)" ]]; then
Menambah jawaban dari Ignacio Vazquez-Abrams. Ini akan memungkinkan tanda + untuk mendahului bilangan bulat, dan itu akan memungkinkan sejumlah nol sebagai titik desimal. Misalnya, ini akan memungkinkan +45.00000000 dianggap sebagai bilangan bulat.
Namun, $ 1 harus diformat agar mengandung koma desimal. 45 tidak dianggap sebagai bilangan bulat di sini, tetapi 45.0 dianggap.
sumber
^[-+]?[0-9]
...?Untuk tertawa, saya kira-kira dengan cepat mengerjakan satu set fungsi untuk melakukan ini (is_string, is_int, is_float, adalah alpha string, atau lainnya) tetapi ada cara yang lebih efisien (lebih sedikit kode) untuk melakukan ini:
Jalankan melalui beberapa tes di sini, saya mendefinisikan bahwa -44 adalah int tetapi 44- bukan dll ..:
Keluaran:
CATATAN: Memimpin 0 dapat menyimpulkan sesuatu yang lain saat menambahkan angka seperti oktal sehingga akan lebih baik untuk menghapusnya jika Anda bermaksud memperlakukan '09' sebagai int (yang saya lakukan) (misalnya
expr 09 + 0
atau strip dengan sed)sumber