Проверьте, находится ли $ 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

Я хотел бы знать, как проверить, находится ли мой $ REPLY в диапазоне числа. Например, я хотел бы, чтобы мой скрипт делал что-то вроде этого:

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

Возможно ли это (я хочу сказать «да, конечно, не должно быть сложно», но я не знаю синтаксис для использования)?

MrVaykadji
источник
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: checkout 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 ...

или что-то подобное.

Орион
источник
Именно то, что я искал, спасибо! Могу ли я использовать вместо простого выражения сравнения, как >=?
Мр. Вайкаджи
Bash допускает множество типов скобок для тестирования. У вас есть эти традиционные [скобки, которые работают, как показано на рисунке man test. Это традиционные и надежные. Тогда у вас есть много встроенных в Bash. У вас [[есть похожие, но не точно такие же, поскольку этот не раскрывает пути (там <=> означает сравнение строк, а целочисленные сравнения такие же, как в [). Оба также имеют множество тестов на наличие файлов, разрешения и так далее. Тогда у вас есть одинарные (и двойные ((в ответе @ 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"
devnull
источник
Мне нравится простота, но каковы ((? Я пытался использовать их в кратчайшие сроки, и, кажется, работает как, if [ ] ; thenно я не знал, что существует.
Мр. Вайкаджи
@MrVaykadji Добавил ссылку из руководства. Дайте мне знать, если это не ясно.
devnull
1
@MrVaykadji Более того, поговорка if [ condition ]; then foo; fiэквивалентна поговорке condition && foo.
devnull
Ладно, хорошо ! Я хотел бы принять оба твоих ответа (Ориона и тебя), если бы мог. Большое спасибо за все это, я многому научился.
Мр. Вайкаджи
Вы можете удалить ведущие нули, если вы используете это. a=08; (( a > 1 ))будет ошибка, так как 08 считается восьмеричным. Вы также можете использовать десятичную с 10#$REPLY. cmd && cmdне совсем такой же , как if cmd; then ...только вы нуждаетесь в elseчасти, цепочки логического &&и ||может вызвать тонкие ошибки.
Луа
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"
Исаак
источник