检查$ REPLY是否在数字范围内


30

我正在使用Bash编写Linux的外壳脚本,以将任何视频文件转换为MP4。为此,我使用avconvwith libvorbis进行音频。

在我的脚本中,我对用户有一个问题:

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

我的“ ABITRATE”字符串进入最终的avconv命令行。

但我想让用户有机会用Kb(千比特)的值来回答这个问题,并将其转换为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

这是否可能(我愿意说“是的,当然不应该很难”,但我不知道使用的语法)吗?


AFAIK,Vorbis在MP4文件中不是有效的音频编解码器(您想使用AAC或MP3)...
evilsoup 2014年

谢谢,它在VLC上运行良好,但是Totem不想阅读它。我要改用libvo_aacenc
MrVaykadji 2014年

Answers:


30

[内置的命令/ shell具有比较测试,因此您可以执行

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:check out 相同man test),因此它需要后面的空格。如果您编写[$REPLY该命令,它将尝试找到一个命名的命令[$REPLY并执行它,该命令将不起作用。关闭也是如此]

编辑:测试数字是否为整数(如果在代码中可能发生),请首先进行测试

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

当然,所有这些括号表达式都将返回0(true)或1(false),并且可以将它们组合在一起。您不仅可以将所有内容放在同一括号中,还可以

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

或类似的东西。


正是我要的东西,谢谢!我可以改用像这样的简单比较表达式>=吗?
MrVaykadji'3

Bash允许使用多种类型的支架进行测试。您有这些传统的[括号,可以在中查看man test。这些是传统的并且很简单。然后,您有很多bash内置函数。您具有[[相似但不完全相同的内容,因为它不会扩展路径名(其中,<=>表示平均字符串比较,而整数比较与中的相同[)。两者都对文件的存在,权限等进行了大量测试。然后(((在@devnull的答案中使用单打和双打。请man bash根据查看Compound Commands
Orion 2014年

1
@MrVaykadji我强烈建议您也测试变量是否为数字,否则可能会得到意外的结果:foo='a'; [[ "$foo" -lt 32 ]] && echo yes
terdon

12

您可以简单地说:

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

引用手册

((...))

(( expression ))

算术表达式是根据以下描述的规则求值的(请参见Shell Arithmetic)。如果表达式的值不为零,则返回状态为0;否则,返回值为0。否则返回状态为1。这完全等同于

let "expression"

我喜欢简单,但是什么((呢?我试图在提示中使用它们,它的工作方式似乎不错,if [ ] ; then但我不知道它的存在。
MrVaykadji 2014年

@MrVaykadji在手册中添加了参考。让我知道是否不清楚。
devnull 2014年

1
@MrVaykadji此外,说if [ condition ]; then foo; fi等同于说condition && foo
devnull 2014年

很好太棒了 !如果可以的话,我想接受你们的请求者(猎户座和您)。非常感谢所有这些,我学到了很多东西。
MrVaykadji 2014年

如果使用此功能,则可能要去除前导零。a=08; (( a > 1 ))由于08被认为是八进制的,因此将出错。您也可以使用强制小数10#$REPLYcmd && cmd是不太一样的if cmd; then ...。一旦你需要一个else部分,链接逻辑&&||可引起微妙的错误。
llua 2014年

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条件表达式的正则表达式match运算符:

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

要测试数字范围,您有两种可能性:

  • 所述-gt的操作员的条件表达式内部[ … ][[ … ]](注意的是,<>运营商做字符串比较,不是数字值比较,所以[[ 10 < 9 ]]为true);
  • 里面常用的算术运算((…))

从而:

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"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.