Перевірте, чи $ REPLY знаходиться в діапазоні чисел


30

Я пишу сценарій оболонки для Linux, використовуючи Bash, щоб перевести будь-який відеофайл у формат MP4. Для цього я використовую avconvз libvorbisаудіо.

Всередині мого сценарію у мене є запитання до користувача:

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

Мій рядок "ABITRATE" переходить у заключний avconvкомандний рядок.

Але я хотів би надати користувачеві можливість відповісти на це питання зі значенням у Кб (Кілобіт) та перекласти його у масштаб, який libvorbisвикористовує. "Шкала від -2 до 10" така:

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

Мені хотілося б дізнатися, як перевірити, чи відповідає моє $ ВІДПОВІДЬ у діапазоні. Наприклад, я хотів би, щоб мій сценарій робив щось подібне:

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

Чи можливо це (я готовий сказати «так, звичайно, не повинно бути важким», але я не знаю синтаксис, який слід використовувати)?


AFAIK, Vorbis не є дійсним аудіокодеком у файлі MP4 (ви хочете використовувати AAC або можливо MP3) ...
evilsoup

Дякую, він добре працював на VLC, але Тотем не хоче його читати. Я переходжу на libvo_aacenc
MrVaykadji

Відповіді:


30

[Вбудована команда / оболонка має порівняльні тести, так що ви можете просто зробити

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

де -geозначає більший-або-рівний (і так далі). -aЛогічно «і». [Команда просто команда, а не спеціальну синтаксис (насправді це те ж саме , як test: перевірити man test), тому вона потребує в просторі після нього. Якщо ви пишете, [$REPLYто спробуйте знайти команду з ім'ям [$REPLYта виконати її, яка не буде працювати. Те саме стосується закриття ].

Редагувати: щоб перевірити, чи число є цілим (якщо це може статися у вашому коді), спочатку зробіть тест

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

Зрозуміло, всі ці дужки вирази повертаються 0 (вірно) або 1 (помилково) і їх можна комбінувати. Ви не тільки можете помістити все в одну і ту ж дужку, ви також можете це зробити

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

чи щось подібне.


Саме те, що я шукав, дякую! Чи можу я замість цього використати простий порівняльний вираз >=?
MrVaykadji

Bash дозволяє багато типів дужок для тестування. У вас є ці традиційні [дужки, які працюють, як видно в man test. Це традиційні та безглузді. Тоді у вас дуже багато вбудованих баш. У вас [[є схожі, але не зовсім однакові, оскільки це не розширює назви шляхів (там <=> середнє порівняння рядків і цілі порівняння такі ж, як у [). Вони також мають багато тестів на існування файлів, дозволів тощо. Тоді ви маєте одинарне (та подвійне ((використання у відповіді @ devnull. Перевірте man bashпід Compound Commands.
Оріон

1
@MrVaykadji Я настійно рекомендую також перевірити, чи є змінною число, ви можете отримати несподівані результати в іншому випадку:foo='a'; [[ "$foo" -lt 32 ]] && echo yes
terdon

12

Ви можете просто сказати:

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

Цитуючи посібник :

((...))

(( expression ))

Арифметичний вираз оцінюється за правилами, описаними нижче (див. Арифметику оболонки ). Якщо значення виразу не дорівнює нулю, стан повернення дорівнює 0; інакше стан повернення дорівнює 1. Це точно рівнозначно

let "expression"

Мені подобається простота, але що таке ((? Я намагався їх швидко використовувати, і, здається, це працює, if [ ] ; thenале я не знав, що існує.
MrVaykadji

@MrVaykadji Додано посилання з посібника. Повідомте мене, якщо це не ясно.
devnull

1
@MrVaykadji Більше того, приказка if [ condition ]; then foo; fiрівнозначна вимові condition && foo.
devnull

Гаразд, приємно! Я хотів би прийняти обидва ваші сподівання (Оріон і ви), якщо зможу. Дуже дякую за все це, я багато чому навчився.
MrVaykadji

Якщо ви користуєтесь цим, ви можете зняти провідні нулі. a=08; (( a > 1 ))буде помилка, оскільки 08 вважається восьмеричним. ви також можете примусити десятковий знак із 10#$REPLY. cmd && cmdне зовсім те саме, що як if cmd; then ...тільки вам потрібна elseчастина, ланцюжок логічних &&і ||може спричинити тонкі помилки.
llua

4

Ви можете зробити щось подібне:

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

Спочатку перевірте, чи є вхід числовим. Наприклад, використовуючи оператор відповідності регулярних виразів bash умовних виразів :

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

Для перевірки числових діапазонів у вас є дві можливості:

  • -gtоператор умовних виразів всередині [ … ]або [[ … ]](остерігайтеся , що <і >оператори роблять порівняння рядків, а НЕ числове значення порівняння, так що [[ 10 < 9 ]]вірно);
  • звичайні арифметичні оператори всередині ((…)).

Таким чином:

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

(Ви можете скористатися різними правилами наближення, я не знаю, чи найкращі тут обрані нами.)


1

Щоб правильно визначити, чи є рядок (десятковим) числом, спершу потрібно визначити, що є десятковим цілим числом. Просте і в той же час цілком повне визначення таке:

Послідовність необов'язкового знака (+ або -) з подальшим не більше 18 (значущими) десятковими цифрами.

І ці кроки потрібні:

  1. Видаліть усі символи, які не є десятковими цифрами (після знака).
  2. Видаліть усі додаткові провідні нулі. Провідні нулі змусять оболонку повірити, що число знаходиться в восьмериці.
  3. Обмежте максимальний розмір цілого числа до 18 цифр. Нижче 2 ** 63-1 (максимум 64-бітове ціле число).

Лише один регулярний вираз зробить більшість із цього:

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

Код для обробки декількох чисел:

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

Який надрукує:

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

Після того, як число є чистим і чітким, єдиним пропущеним тестом є обмеження діапазону значень. Ця проста пара рядків зробить це:

(( 1  <= integer && integer <= 32 )) && REPLY="-2"
(( 33 <= integer && integer <= 48 )) && REPLY="-1"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.