$ REPLY'in sayı aralığında olup olmadığını kontrol edin


30

Herhangi bir video dosyasını MP4'e çevirmek için Bash kullanarak Linux için bir kabuk betiği yazıyorum. Bunun için, ben kullanıyorum avconvile libvorbisses için.

Komut dosyamın içinde, kullanıcı için bir sorum var:

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

"ABITRATE" dizgim son avconvkomut satırına giriyor .

Ancak, kullanıcıya bu soruyu Kb (Kilobit) cinsinden bir değerle cevaplama ve onu libvorbiskullanan ölçeğe çevirme fırsatı vermek istiyorum . "-2'den 10'a kadar olan ölçek" şudur:

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

$ REPLY’imin bir sayı aralığında olup olmadığını nasıl kontrol edeceğimi bilmek istiyorum. Örneğin, senaryomun böyle bir şey yapmasını istiyorum:

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

Bu mümkün mü ('evet tabi ki zor olmamalıyım' demeye istekliyim ama kullanacağım sözdizimini bilmiyorum)?


AFAIK, Vorbis bir MP4 dosyasında geçerli bir ses kodeği değil (AAC veya muhtemelen MP3 kullanmak istiyorsunuz) ...
evilsoup

Teşekkürler, VLC'de iyi çalıştı ama Totem okumak istemiyor. Libvo_aacenc
MrVaykadji

Yanıtlar:


30

[Sadece bunu yapabilirsiniz komut / kabuk yerleşik, karşılaştırma testleri vardır

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

burada -gedaha büyük veya eşit anlamına gelir (ve benzeri). -a"Ve" mantıklı. [Komut sadece bir komut değil, özel bir sözdizimi (aslında aynı şey olduğunu test: check out man testkendinden sonra boşluk İHTİYAÇ yüzden). Eğer yazarsanız işe yaramayacak [$REPLYisimli bir komut bulmaya [$REPLYve onu çalıştırmaya çalışacaktır. Aynı kapanış için de geçerli ].

Düzenleme: sayının tam sayı olup olmadığını test etmek için (kodunuzda olabilirse), önce testi yapın

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

Elbette bütün bu parantez ifadeleri 0 (doğru) veya 1 (yanlış) döndürür ve birleştirilebilir. Her şeyi aynı parantez içine almakla kalmaz, aynı zamanda

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

Veya benzeri.


Tam olarak aradığım şey, teşekkür ederim! Bunun yerine basit karşılaştırma ifadesini kullanabilir miyim >=?
MrVaykadji

Bash, test için birçok braket tipine izin verir. Görüldüğü gibi çalışan bu geleneksel [dirseklere sahipsiniz man test. Bunlar geleneksel ve aptaldır. O zaman, birçok bash yerleşkesi var. Buna [[benzer olan ancak tam olarak aynı olmayan bir durum var, çünkü bu durum yol adlarını genişletmiyor (orada, <=> ortalama dize karşılaştırmaları ve tamsayı karşılaştırmaları aynıydı [). Her ikisi de dosya mevcudiyeti, izinler vb. İçin birçok teste sahiptir. O zaman @ devnull'ın cevabında kullanılan tek (ve çift ((var. man bashAltında kontrol edin Compound Commands.
orion

1
@MrVaykadji Değişkenin bir sayı olup olmadığını da test etmenizi tavsiye ederim, aksi takdirde beklenmedik sonuçlar alabilirsiniz:foo='a'; [[ "$foo" -lt 32 ]] && echo yes
terdon

12

Basitçe söyleyebilirsin:

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

El kitabından alıntı :

((...))

(( expression ))

Aritmetik ifade aşağıda açıklanan kurallara göre değerlendirilir (bkz. Shell Arithmetic ). İfadenin değeri sıfır değilse, dönüş durumu 0'dır; Aksi halde iade durumu 1'dir. Bu tam olarak eşdeğerdir.

let "expression"

Sadeliği seviyorum, ama ne ((? Onları derhal kullanmaya çalıştım ve sanki benziyormuş gibi görünüyor if [ ] ; thenama bunun olduğunu bilmiyordum.
MrVaykadji

@MrVaykadji El kitabından bir referans eklendi. Net değilse bana bildirin.
devnull

1
@ MrVaykadji Ayrıca, söyleyerek if [ condition ]; then foo; fieşdeğerdir condition && foo.
devnull

Tamam güzel ! Mümkünse her iki kulenizi de (Orion ve siz) kabul etmek istiyorum. Bütün bunlar için çok teşekkürler, çok şey öğrendim.
MrVaykadji

Bunu kullanırsanız, baştaki sıfırları sıyrmak isteyebilirsiniz. a=08; (( a > 1 ))08'in sekizlik olduğu düşünülürse hata verir. ayrıca ondalık basmaya zorlayabilirsin 10#$REPLY. cmd && cmdaynı değil ise if cmd; then ...bir ihtiyaç kez elsemantıksal zincirleme parçayı &&ve ||ince hatalara yol açabilir.
llua,

4

Böyle bir şey yapabilirsin:

#!/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

2

İlk önce girişin sayısal olup olmadığını test edin. Örneğin, bash koşullu ifadelerin normal ifade eşleştirme işlecini kullanarak :

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

Sayısal aralıkları test etmek için iki seçeneğiniz vardır:

  • -gtoperatörü koşullu ifadeler[ … ]veya [[ … ]](o sakının <ve >operatörler dize karşılaştırma yapmak değil sayısal değer karşılaştırma, bu yüzden [[ 10 < 9 ]]doğrudur);
  • her zamanki aritmetik operatörler ((…)).

Böylece:

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

(Farklı yaklaşım kuralları kullanmak isteyebilirsiniz, seçtiğim kuralların burada en iyisi olup olmadığını bilmiyorum.)


1

Bir dizginin (ondalık) bir sayı olup olmadığını doğru bir şekilde belirlemek için önce ondalık bir tamsayı sayısının ne olduğunu tanımlamamız gerekir. Basit ama oldukça eksiksiz bir tanım:

İsteğe bağlı bir işaretin (+ veya -) ardından 18 (önemli) ondalık basamaktan oluşan bir sekansı.

Ve bu adımlara ihtiyaç var:

  1. Ondalık basamak olmayan tüm karakterleri kaldırın (işaretinden sonra).
  2. İsteğe bağlı tüm baştaki sıfırları çıkarın. Öndeki sıfırlar kabuğun sayının sekizlik olduğuna inanmasına neden olur.
  3. Tamsayının maksimum boyutunu 18 hane ile sınırlandırın. 2 altında ** 63-1 (maksimum 64 bit tamsayı).

Sadece bir regex, bunun çoğunu yapacak:

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

Birkaç numarayı işlemek için kod:

#!/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_

Hangi yazdıracak:

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

Bu sayı temiz ve net olduğunda, tek eksik olan test değer aralığını sınırlamaktır. Bu basit çift satır şunu yapacak:

(( 1  <= integer && integer <= 32 )) && REPLY="-2"
(( 33 <= integer && integer <= 48 )) && REPLY="-1"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.