Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I'm writing a shell script for Linux, using Bash, to translate any video-file into a MP4. For that, I'm using avconv with libvorbis for audio.

Inside my script, I have a question for the user :

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

My "ABITRATE" string goes into the final avconv command-line.

But I would like to give the user the opportunity to answer that question with a value in Kb (Kilobit), and translate it into the scale that libvorbis uses. The "scale from -2 to 10" is this :

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

I would like to know how to check if my $REPLY is in a range of number. For example, I would like my script to do something like this :

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

Is this possible (I'm willing to say 'yes of course, shouldn't be hard' but I don't know the syntax to use) ?

share|improve this question
    
AFAIK, Vorbis isn't a valid audio codec in an MP4 file (you want to use AAC or possibly MP3)... –  evilsoup Mar 9 at 18:55
    
Thank you, it worked well on VLC but Totem doesn't want to read it. I'm switching to libvo_aacenc –  MrVaykadji Mar 9 at 19:57

4 Answers 4

up vote 3 down vote accepted

The [ command/shell builtin has comparison tests, so you can just do

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

where -ge means greater-or-equal-to (and so on). The -a is logical "and". The [ command is just a command, not special syntax (it's actually the same as test: check out man test), so it NEEDS the space after it. If you write [$REPLY it will try to find a command named [$REPLY and execute it, which won't work. The same goes for closing ].

Edit: to test if the number is integer (if that can happen in your code), first do the test

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

Of course all these bracket expressions return 0 (true) or 1 (false) and can be combined. Not only you can put everything in the same bracket, you can also do

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

or something similar.

share|improve this answer
    
Exactly what I was looking for, thank you ! Could I use instead simple comparison expression like >= ? –  MrVaykadji Mar 9 at 16:18
    
Bash allows many types of brackets for testing. You have these traditional [ brackets, which work as seen in man test. These are traditional and fool-proof. Then, you have a lot of bash builtins. You have [[ which are similar, but not exactly the same, as this one doesn't expand pathnames (there, < = > mean string comparisons, and integer comparisons are the same as in [). Both also have a lot of tests for file existence, permissions and so on. Then you have single ( and double (( used in @devnull's answer. Check out man bash under Compound Commands. –  orion Mar 9 at 16:26
1  
@MrVaykadji I highly recommend you also test whether the variable is a number, you might get unexpected results otherwise: foo='a'; [[ "$foo" -lt 32 ]] && echo yes –  terdon Mar 9 at 17:10

You could simply say:

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

Quoting from the manual:

((...))

(( expression ))

The arithmetic expression is evaluated according to the rules described below (see Shell Arithmetic). If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1. This is exactly equivalent to

let "expression"
share|improve this answer
    
I like the simplicity, but what are the (( ? I tried to use them in prompt and it seems to work like if [ ] ; then but I didn't knew that existed. –  MrVaykadji Mar 9 at 16:15
    
@MrVaykadji Added a reference from the manual. Let me know if it's not clear. –  devnull Mar 9 at 16:16
1  
@MrVaykadji Moreover, saying if [ condition ]; then foo; fi is equivalent to saying condition && foo. –  devnull Mar 9 at 16:19
    
Okay, nice ! I would like to accept both your aswers (Orion and you) if I could. Thanks a lot for all this, I learned a lot. –  MrVaykadji Mar 9 at 16:21
    
You may want to strip leading zeros if you use this. a=08; (( a > 1 )) will error since 08 is considered octal. you could also force decimal with 10#$REPLY. cmd && cmd isn't quite the same as if cmd; then ... Once you need a else part, chaining logical && and || can cause subtle bugs. –  llua Mar 9 at 16:23

You could do something like this:

#!/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
share|improve this answer

First, test whether the input is numeric. For example, using the regular expression match operator of bash conditional expressions:

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

To test numeric ranges, you have two possibilities:

  • the -gt operator of conditional expressions inside [ … ] or [[ … ]] (beware that the < and > operators do string comparison, not numeric value comparison, so [[ 10 < 9 ]] is true);
  • the usual arithmetic operators inside ((…)).

Thus:

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

(You may want to use different approximation rules, I don't know if the ones I chose are the best here.)

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.