Kiểm tra xem $ REPLY có nằm trong một dãy số không


30

Tôi đang viết một kịch bản shell cho Linux, sử dụng Bash, để dịch bất kỳ tệp video nào sang MP4. Vì thế, tôi đang sử dụng avconvvới libvorbisâm thanh.

Trong kịch bản của tôi, tôi có một câu hỏi cho người dùng:

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

Chuỗi "ABITRATE" của tôi đi vào dòng avconvlệnh cuối cùng .

Nhưng tôi muốn cho người dùng cơ hội trả lời câu hỏi đó với giá trị bằng Kb (Kilobit) và dịch nó sang thang đo libvorbissử dụng. "Thang điểm từ -2 đến 10" là đây:

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

Tôi muốn biết cách kiểm tra xem $ REPLY của tôi có nằm trong phạm vi số không. Ví dụ, tôi muốn kịch bản của mình làm một cái gì đó như thế này:

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

Điều này có thể không (tôi sẵn sàng nói 'tất nhiên, không nên khó' nhưng tôi không biết cú pháp để sử dụng)?


AFAIK, Vorbis không phải là codec âm thanh hợp lệ trong tệp MP4 (bạn muốn sử dụng AAC hoặc có thể MP3) ...
evilsoup

Cảm ơn bạn, nó hoạt động tốt trên VLC nhưng Totem không muốn đọc nó. Tôi đang chuyển sang libvo_aacenc
MrVaykadji

Câu trả lời:


30

Các [BUILTIN lệnh / vỏ có các bài kiểm tra so sánh, vì vậy bạn chỉ có thể làm

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

trong đó -gecó nghĩa là lớn hơn hoặc bằng (và như vậy). Hợp -alý "và". Các [lệnh chỉ là một lệnh, không cú pháp đặc biệt (nó thực sự giống như test: kiểm tra man test), vì vậy nó cần không gian sau nó. Nếu bạn viết [$REPLYnó sẽ cố gắng tìm một lệnh có tên [$REPLYvà thực thi nó, nó sẽ không hoạt động. Việc đóng cửa cũng vậy ].

Chỉnh sửa: để kiểm tra xem số đó có phải là số nguyên không (nếu điều đó có thể xảy ra trong mã của bạn), trước tiên hãy thực hiện kiểm tra

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

Tất nhiên tất cả các biểu thức ngoặc này trả về 0 (đúng) hoặc 1 (sai) và có thể được kết hợp. Bạn không chỉ có thể đặt mọi thứ trong cùng một khung, bạn cũng có thể làm

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

hoặc một cái gì đó tương tự.


Chính xác những gì tôi đang tìm kiếm, cảm ơn bạn! Tôi có thể sử dụng thay vì biểu thức so sánh đơn giản như thế >=nào?
MrVaykadji

Bash cho phép nhiều loại dấu ngoặc để thử nghiệm. Bạn có các [dấu ngoặc truyền thống , hoạt động như đã thấy trong man test. Đây là truyền thống và bằng chứng ngu ngốc. Sau đó, bạn có rất nhiều nội dung bash. Bạn có [[những cái tương tự, nhưng không hoàn toàn giống nhau, vì cái này không mở rộng tên đường dẫn (ở đó, <=> so sánh chuỗi trung bình và so sánh số nguyên giống như trong [). Cả hai cũng có rất nhiều bài kiểm tra về sự tồn tại của tập tin, quyền và như vậy. Sau đó, bạn có một (và hai ((được sử dụng trong câu trả lời của @ devnull. Kiểm tra man bashdưới Compound Commands.
orion

1
@MrVaykadji Tôi khuyên bạn cũng nên kiểm tra xem biến đó có phải là số không, bạn có thể nhận được kết quả không mong muốn nếu không:foo='a'; [[ "$foo" -lt 32 ]] && echo yes
terdon

12

Bạn có thể nói một cách đơn giản:

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

Trích dẫn từ hướng dẫn :

((...))

(( expression ))

Biểu thức số học được đánh giá theo các quy tắc được mô tả dưới đây (xem Shell Số học ). Nếu giá trị của biểu thức là khác không, trạng thái trả về là 0; mặt khác, trạng thái trả về là 1. Điều này hoàn toàn tương đương với

let "expression"

Tôi thích sự đơn giản, nhưng những gì là ((? Tôi đã cố gắng sử dụng chúng kịp thời và nó có vẻ hoạt động như thế if [ ] ; thennhưng tôi không biết rằng nó tồn tại.
MrVaykadji

@MrVaykadji Đã thêm một tài liệu tham khảo từ hướng dẫn. Hãy cho tôi biết nếu nó không rõ ràng.
devnull

1
@MrVaykadji Hơn nữa, nói if [ condition ]; then foo; fitương đương với nói condition && foo.
devnull

Được đẹp ! Tôi muốn chấp nhận cả aswers của bạn (Orion và bạn) nếu tôi có thể. Cảm ơn rất nhiều vì tất cả điều này, tôi đã học được rất nhiều.
MrVaykadji

Bạn có thể muốn tước số không hàng đầu nếu bạn sử dụng này. a=08; (( a > 1 ))sẽ lỗi vì 08 được coi là bát phân. bạn cũng có thể buộc thập phân với 10#$REPLY. cmd && cmdkhông hoàn toàn giống như if cmd; then ...Một khi bạn cần một elsephần, xâu chuỗi logic &&||có thể gây ra các lỗi tinh vi.
llua

4

Bạn có thể làm một cái gì đó như thế này:

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

Đầu tiên, kiểm tra xem đầu vào là số. Ví dụ: sử dụng toán tử khớp biểu thức chính quy của biểu thức điều kiện bash :

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

Để kiểm tra phạm vi số, bạn có hai khả năng:

  • các -gtnhà điều hành của biểu thức điều kiện bên trong [ … ]hoặc [[ … ]](hãy cẩn thận rằng <>khai thác làm chuỗi so sánh, không số so sánh giá trị, vì vậy [[ 10 < 9 ]]là true);
  • Các toán tử số học thông thường bên trong ((…)).

Như vậy:

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

(Bạn có thể muốn sử dụng các quy tắc gần đúng khác nhau. Tôi không biết liệu những quy tắc tôi chọn có tốt nhất ở đây không.)


1

Để phát hiện chính xác nếu một chuỗi là một số (thập phân) trước tiên chúng ta cần xác định đâu là số nguyên thập phân. Một định nghĩa đơn giản và khá đầy đủ là:

Một chuỗi các dấu tùy chọn (+ hoặc -) theo sau là không quá 18 chữ số thập phân (đáng kể).

Và các bước này là cần thiết:

  1. Xóa tất cả các ký tự không phải là chữ số thập phân (sau dấu).
  2. Loại bỏ tất cả các số không hàng đầu tùy chọn. Các số 0 đứng đầu sẽ khiến cho vỏ tin rằng số đó là số bát phân.
  3. Giới hạn kích thước tối đa của số nguyên đến 18 chữ số. Dưới 2 ** 63-1 (số nguyên tối đa 64 bit).

Chỉ cần một regex sẽ làm hầu hết điều đó:

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

Mã để xử lý một số số là:

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

Mà sẽ in:

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

Khi số đó rõ ràng và rõ ràng, thử nghiệm còn thiếu duy nhất là giới hạn phạm vi của các giá trị. Cặp dòng đơn giản này sẽ làm điều đó:

(( 1  <= integer && integer <= 32 )) && REPLY="-2"
(( 33 <= integer && integer <= 48 )) && REPLY="-1"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.