Periksa apakah $ REPLY berada dalam kisaran angka

30

Saya sedang menulis skrip shell untuk Linux, menggunakan Bash, untuk menerjemahkan file video apa saja ke dalam MP4. Untuk itu, saya menggunakan avconvdengan libvorbisuntuk audio.

Di dalam skrip saya, saya punya pertanyaan untuk pengguna:

read -p "- Audio Quality [scale from -2 to 10] ? "
    if [ -n "$REPLY" ] ; then
    ABITRATE="-aq $REPLY"
    fi

String "ABITRATE" saya masuk ke baris avconvperintah terakhir .

Tapi saya ingin memberi pengguna kesempatan untuk menjawab pertanyaan itu dengan nilai dalam Kb (Kilobit), dan menerjemahkannya ke skala yang libvorbisdigunakan. "Skala dari -2 hingga 10" adalah ini:

Quality Kbit/s  Normalization
-----------------------------
 -2      ~32        y
 -1      ~48        y
  0      ~64        y
  1      ~80        y
  2      ~96        y
  3     ~112        y
  4     ~128        n
  5     ~160        n
  6     ~192        n
  7     ~224        n
  8     ~256        n
  9     ~320        n
 10     ~500        n

Saya ingin tahu cara memeriksa apakah $ REPLY saya dalam kisaran angka. Misalnya, saya ingin skrip saya melakukan sesuatu seperti ini:

if [ $REPLY is a number between 1 and 32 ] ; then 
 REPLY="-2"
elif [ $REPLY is a number between 33 and 48 ] ; then 
 REPLY="-1"
fi

Apakah ini mungkin (saya bersedia mengatakan 'ya tentu saja, seharusnya tidak sulit' tetapi saya tidak tahu sintaks yang digunakan)?

TuanVaykadji
sumber
AFAIK, Vorbis bukan codec audio yang valid dalam file MP4 (Anda ingin menggunakan AAC atau mungkin MP3) ...
evilsoup
Terima kasih, ini bekerja dengan baik pada VLC tetapi Totem tidak mau membacanya. Saya beralih ke libvo_aacenc
MrVaykadji

Jawaban:

30

The [perintah / shell builtin memiliki tes perbandingan, sehingga Anda hanya dapat melakukan

if [ "$REPLY" -ge 1 -a "$REPLY" -le 32 ]; then REPLY=-2;
elif [ "$REPLY" -ge 33 -a "$REPLY" -le 48 ]; then REPLY=-1; fi

di mana -geberarti lebih besar atau sama dengan (dan seterusnya). Ini -alogis "dan". The [perintah hanya perintah, tidak sintaks khusus (itu sebenarnya sama seperti test: memeriksa man test), sehingga KEBUTUHAN ruang setelah. Jika Anda menulisnya [$REPLYakan mencoba menemukan perintah bernama [$REPLYdan menjalankannya, yang tidak akan berhasil. Hal yang sama berlaku untuk penutupan ].

Sunting: untuk menguji apakah angka itu bilangan bulat (jika itu bisa terjadi dalam kode Anda), pertama-tama lakukan tes

if [[ "$REPLY" =~ ^[0-9]+$ ]]; then
   existing code
else echo "$REPLY is not an integer" >&2 && exit 1; fi

Tentu saja semua ekspresi kurung ini menghasilkan 0 (benar) atau 1 (salah) dan dapat digabungkan. Tidak hanya Anda dapat meletakkan semuanya di braket yang sama, Anda juga dapat melakukannya

if [[ "$REPLY" =~ ^[0-9]+$ ]] && [ "$REPLY" -ge 1 -a "$REPLY" -le 32 ]; then ...

atau yang serupa.

orion
sumber
Persis apa yang saya cari, terima kasih! Bisakah saya menggunakan ekspresi perbandingan yang sederhana >=?
MrVaykadji
Bash memungkinkan banyak jenis kurung untuk pengujian. Anda memiliki [tanda kurung tradisional ini , yang berfungsi seperti yang terlihat di man test. Ini tradisional dan bukti bodoh. Kemudian, Anda memiliki banyak bash bawaan. Anda memiliki [[yang serupa, tetapi tidak persis sama, karena yang ini tidak memperluas nama path (di sana, <=> perbandingan string rata-rata, dan perbandingan integer sama dengan di [). Keduanya juga memiliki banyak tes untuk keberadaan file, izin dan sebagainya. Maka Anda memiliki satu (dan dua kali ((digunakan dalam jawaban @ devnull. Lihat di man bashbawah Compound Commands.
orion
1
@MrVaykadji Saya sangat merekomendasikan Anda juga menguji apakah variabel adalah angka, Anda mungkin mendapatkan hasil yang tidak terduga jika tidak:foo='a'; [[ "$foo" -lt 32 ]] && echo yes
terdon
12

Anda bisa mengatakan:

((REPLY>=1 && REPLY<=32)) && REPLY=-2
((REPLY>=33 && REPLY<=48)) && REPLY=-1

Mengutip dari manual :

((...))

(( expression ))

Ekspresi aritmatika dievaluasi sesuai dengan aturan yang dijelaskan di bawah ini (lihat Aritmatika Shell ). Jika nilai ekspresi bukan nol, status pengembalian adalah 0; jika tidak, status pengembaliannya adalah 1. Ini persis sama dengan

let "expression"
devnull
sumber
Saya suka kesederhanaan, tetapi apa sajakah itu ((? Saya mencoba menggunakannya dengan cepat dan sepertinya berfungsi seperti if [ ] ; thentetapi saya tidak tahu itu ada.
MrVaykadji
@MrVaykadji Menambahkan referensi dari manual. Beritahu saya jika tidak jelas.
devnull
1
@MrVaykadji Selain itu, mengatakan if [ condition ]; then foo; fisetara dengan mengatakan condition && foo.
devnull
Oke, bagus ! Saya ingin menerima kedua aswers Anda (Orion dan Anda) jika saya bisa. Terima kasih banyak untuk semua ini, saya belajar banyak.
MrVaykadji
Anda mungkin ingin menghapus angka nol di depan jika Anda menggunakan ini. a=08; (( a > 1 ))akan kesalahan sejak 08 dianggap oktal. Anda juga bisa memaksa desimal dengan 10#$REPLY. cmd && cmdtidak sama dengan if cmd; then ...Sekali Anda membutuhkan elsebagian, merantai logis &&dan ||dapat menyebabkan bug halus.
llua
4

Anda dapat melakukan sesuatu seperti ini:

#!/usr/bin/env bash
read -p "- Audio Quality [scale from -2 to 10] ? "
if [ -n "$REPLY" ] ; then
    ABITRATE="-aq $REPLY"
fi

echo "You chose : $ABITRATE : $REPLY"
## If 0 < $REPLY < 33 and $REPLY is a number
if [[ "$REPLY" -gt 0 && "$REPLY" -lt 33 && "$REPLY" =~ '^[0-9]$' ]]
then
    echo "GOOD"
else
    echo "BAD"
fi
terdon
sumber
2

Pertama, uji apakah inputnya numerik. Misalnya, menggunakan operator pencocokan ekspresi reguler dari ekspresi kondisional bash :

if [[ $REPLY =~ -?[0-9]+ ]]; then
  echo "Invalid input (not numeric): $REPLY"
  exit 2
fi

Untuk menguji rentang angka, Anda memiliki dua kemungkinan:

  • yang -gtoperator ekspresi kondisional dalam [ … ]atau [[ … ]](berhati-hatilah bahwa <dan >operator melakukan perbandingan string, bukan numerik nilai perbandingan, sehingga [[ 10 < 9 ]]benar);
  • operator aritmatika biasa di dalam ((…)).

Demikian:

if ((REPLY >= -2 && REPLY <= 10)); then
  : # do nothing -- pass directly to libvorbis
elif ((REPLY <= 24)); then
  echo "Value outside supported range: $REPLY"
  exit 2
elif ((REPLY <= 135)); then
  REPLY=$(((REPLY+8) / 16 - 4))
elif ((REPLY <= 271)); then
  REPLY=$(((REPLY+16) / 32))
elif ((REPLY <= 400)); then
  REPLY=9
elif ((REPLY <= 707)); then
  REPLY=10
else
  echo "Value outside supported range: $REPLY"
  exit 2
fi

(Anda mungkin ingin menggunakan aturan pendekatan yang berbeda, saya tidak tahu apakah yang saya pilih adalah yang terbaik di sini.)

Gilles 'SANGAT berhenti menjadi jahat'
sumber
1

Untuk mendeteksi dengan benar jika suatu string adalah angka (desimal), pertama-tama kita perlu mendefinisikan apa itu bilangan bulat desimal. Definisi yang sederhana namun cukup lengkap adalah:

Urutan tanda opsional (+ atau -) diikuti oleh tidak lebih dari 18 (signifikan) angka desimal.

Dan langkah-langkah ini diperlukan:

  1. Hapus semua karakter yang bukan digit desimal (setelah tanda).
  2. Hapus semua nol memimpin opsional. Angka nol di depan akan menyebabkan shell percaya bahwa angka tersebut dalam oktal.
  3. Batasi ukuran maksimal bilangan bulat hingga 18 digit. Di bawah 2 ** 63-1 (maks. 64 bit integer).

Hanya satu regex yang akan melakukan sebagian besar itu:

re='^([+-])?0*([0-9]{1,18})$'
[[ $number =~ $re ]] && integer=${BASH_REMATCH[*]:1}

Kode untuk memproses beberapa angka adalah:

#!/bin/bash
DebugLevel=4     # 1:fatal 2:error 3:warn 4:info 5:debug 6:trace

SayMsg    (){   local a; a=$1; shift ;            # Log level
                [[ $a -le $DebugLevel ]] && printf '%s' "$@" $'\n' >&2 ;
            }
SayError  (){   a=$1; shift; printf '%s' "$@" $'\n' >&2; exit   "$a";   }

parseint  (){   local re # Parse the first argument as an integer or fail
                re='^([+-])?0*([0-9]{1,18})$'
                [[ $1 =~ $re ]] || { SayMsg 4 "Invalid number $1"; return 2; }
                integer=${BASH_REMATCH[1]}${BASH_REMATCH[2]}
                echo "integer=$integer"
             }

while read val; do
    parseint "$val"
    done <<-\_EOT_
    0
    1
    10
    100
    2345
    123456789012345678
    923456789012345678
    999999999999999999
    0000000012345
    +023
    -00045
    -76
    ""
    ''
    a
    abc
    1234567890123456789
    7.23
    -8.17
    1e3
    10+11
    _EOT_

Yang akan dicetak:

integer=0
integer=1
integer=10
integer=100
integer=2345
integer=123456789012345678
integer=923456789012345678
integer=999999999999999999
integer=12345
integer=+23
integer=-45
integer=-76
Invalid number ""
Invalid number ''
Invalid number 
Invalid number a
Invalid number abc
Invalid number 1234567890123456789
Invalid number 7.23
Invalid number -8.17
Invalid number 1e3
Invalid number 10+11

Setelah angka tersebut bersih dan jelas, satu-satunya tes yang hilang adalah membatasi rentang nilai. Beberapa baris sederhana ini akan melakukan itu:

(( 1  <= integer && integer <= 32 )) && REPLY="-2"
(( 33 <= integer && integer <= 48 )) && REPLY="-1"
Ishak
sumber