如何检查 bash 中定义的任意数字,作为值、字符串或引用传递?

问题描述 投票:0回答:1

我一直在寻找一种扩展检查方法,涵盖各种有效的

numbers
,作为
value
string
或 var
reference
传递给方法,但大多数解决方案最多采用基本情况,例如纯整数或浮点数,或专用于格式化日期字符串(这不是我的要求范围)。

因此,我发布了一个解决方案的新问题,该问题验证传递的参数是否是常见的

decimal/int
、浮点(由
.
,
hex
分隔(以
0x 开头) 
,包含
[0-9a-fA-F]
)、
octal
(以
0
开头)、
scientific notation/exponential
(格式为
52e-14
-2*10^8
)或自定义基数(如
<base>#n
,基数范围为 2到 64,允许数字
[0-9a-fA-F]
)。

validation floating-point numbers hex scientific-notation
1个回答
0
投票

这是我的解决方案,包括大量的测试用例,验证传递的参数是否是常见的十进制/整数、浮点(由 . 或 ,, 十六进制分隔(以 0x 开头,包含 [0-9a-fA-F ])、八进制(从 0 开始)、科学记数法/指数(形式为 52e-14 或 -2*10^8)或自定义基数(如 #n,基数范围为 2 到 64,允许数字[0-9a-fA-F]).

1000 次迭代所花费的时间比 HERE 中最快的 isint_Case() 方法慢约 6-7 倍,但涵盖并组合了更多可能遇到的数字或数字字符串类型,如 GNU.org - Shell 算术中所述。

方法 numTest() 使用 case + 扩展模式匹配(需要 shopt -s extglob),成功时返回 0,测试失败时返回 1。如果需要,注释掉的 printf 语句可以将结果传递到 stdout,例如: G。用于将连续结果捕获到 var 中(由于信息输出重定向到 /dev/tty,因此应该仅适用于值)。还添加了一个注释掉的内置基于 RegEx 的解决方案(速度慢约 4 秒 à 1000):

#! /bin/bash

shopt -s extglob

# Function accepts int (decimal), float, scientific (exponential), octal and hex numbers passed directly as values, expandable vars or var name references
numTest() {

    # Check for and link val to var name reference 
    if declare -p "$1" &>/dev/null; then 
        local -n input="$1" #&>/dev/null
    else
        local var="$1"
        local -n input=var
    fi
    
    ## CASE PATTERN CHECK SOLUTION (~18s for 1000)
    local isNum
    case ${input#[-+]} in
        # Octal, hex, decimal / scientific/exponential, as parsable by awk '{ print +$1, +$2 }' <<<'12345678e-6 -323*10^34'
        @(+([0-9])|0[xX]+([0-9aAbBcCdDeEfF])|+([0-9])?(@([eE]|'^'|'*10^')?([+-])+([0-9]))) ) 
            # Uncomment to print succeeded test values
            #printf '%s' "IS INT: " >/dev/tty
            #printf '%s\n' "$input"
            return 0 ;;
        # Explicit base 2 - 64
        +([0-9])\#+([0-9aAbBcCdDeEfF]) ) 
            local -i base=${input%%#*}
            if [[ $base -ge 2 && $base -le 64 ]]; then
                # Uncomment to print succeeded test values
                #printf '%s' "IS INT: " >/dev/tty
                #printf '%s\n' "$input"
                return 0
            fi ;;
        # Float / scientific/exponential, as parsable by awk '{ print +$1, +$2 }' <<<'0.0314159e2 -323*10^34'
        @(*([0-9])[.,]+([0-9])?(@([eE]|'^'|'*10^')?([+-])+([0-9])))|'NaN' ) 
            # Uncomment to print succeeded test values
            #printf '%s' "IS FLOAT: " >/dev/tty
            #printf '%s\n' "$input"
            return 0
            ;; 
    esac
    
    # ## REGEX CHECK SOLUTION (~22s, ~4s slower for 1000)
    # if [[ $input =~ ^[+-]?([0-9]+|0[xX][0-9aAbBcCdDeEfF]+|[0-9]+(([eE]|'^'|'*10^')[+-]?[0-9]+)?)$ ]]; then
        ## printf '%s' "IS INT: " >/dev/tty
        ## printf '%s\n' "$input"
        # return 0
    # elif [[ $input =~ ^[+-]?[0-9]*[.,][0-9]+(([eE]|'^'|'*10^')[+-]?[0-9]+)?$ || $input == 'NaN' ]]; then 
        ## printf '%s' "IS FLOAT: " >/dev/tty
        ## printf '%s\n' "$input"
        # return 0
    # fi
    
    # Uncomment to print failed test values
    #printf '%s' "TEST_FAIL: " >/dev/tty
    #printf '%s\n' "$input"
    return 1
}

# Set to 1 to print test section captions
declare print=

## Declare test vars and array of test values

declare -i int_min
(( int_min=(1<<63) ))
declare -i int_max=$((int_min - 1))

## Success test var candidates

# Success test vars octal 

declare -i convertedOctal=073
declare -i convertedOctal2=-073
declare -i convertedOctal3=+073

# Success test vars hex

declare -ai successTestsHex=( x1450 0x9532 0xA0b32 0x0 -0x0 -0X0 +0x0 +0X0 +0x08842 +0X08842 -0x05431 -0X05431 )
declare -a successTestsHexStr=( 'x1450' '0x9532' '0xA0b32' '0x0' '-0x0' '-0X0' '+0x0' '+0X0' '+0x08842' '+0X08842' '-0x05431' '-0X05431' )

# "success" - parsed as null at var assignment -> 0
declare -ai successTestsPseudoHex=( -x0x0 -x0x0 +x0x0 )

# Auto-converted ints to hex
declare -ai successTestsConvertedOctal=( $convertedOctal $convertedOctal2 $convertedOctal3 )

# Success test vars int 

declare -ai successTestsInt=( 0 -0 +0 $int_max -$int_max +$int_max $int_min -$int_min +$int_min 1 -1 +1 9015 -9015 +9015 )

# Exponents (only possible as string)

declare -a successTestsIntStr=( '0' '-0' '+0' "$int_max" "-$int_max" "+$int_max" "$int_min" )
declare -a successTestsIntBaseStr=( '2#0' '2#1' '16#aF0' '16#AF0' '32#a' '32#A' '33#a' '33#A' '64#f' '64#F' '10#100' )
declare -a successTestsOctalStr=( '073' '-073' '+073' )
declare -a successTestsSciStr=( '0e0' '0E0' '087e-32' '087E-32' '-087e32' '-087E32' '-22048e-16' '-22048E-16' '+100e050' '+100E050' '+7e-25' '+7E-25' '+000e+00' '+000E+00' '-853e1' '-853E1' '-323^34' '-1^-3' '+166^8' '+89^-16' '-323*10^34' '-1*10^-3' '+166*10^8' '+89*10^-16' )

# Success test vars float 

declare -a successTestsFloatStr=( 'NaN' 0.0 +0.0 0,0 -0.0 -0,0 1.34 +1.34 1,34 -1.34 -1,34 .494 ,494 +,494 -.494 -,494 '571.0014' '571,0014' '-571.0014' '-571,0014' )
declare -a successTestsSciFloatStr=( '13.62e06' '13.62E06' '-13.62e06' '-13.62E06' '+13.62e06' '+13.62E06' '-3.23^34' '-0.1^-3' '+16.6^8' '+8.9^-16' '-3,23*10^34' '-0,1*10^-3' '+16,6*10^8' '+8,9*10^-16' )

## Fail tests 

# Fail test vars misc 

declare -a failTestsMiscStr=( '+' '-' ' ' '_' 'E' 'e' 'a' 'A' 'x' 'X' '.' '-.' ',' '-,' )

declare empty=''

# Fail test vars hex 

declare -a failTestsHexStr=( 'x0x0' '-x0x0' '+x0x0' '0x01x' '-0x01x' '+0x42x' '0x' 'x0' )
declare failHexStr="x0x0"
declare failHexStr1="-x0x0"
declare failHexStr2="+x0x0"

# Fail test vars int 

declare -a failTestsIntStr=( "-$int_min" "+$int_min" '#1' '2_' 'i3x' ' 1 ' '22 ' ' 333' ' -1 ' '-02 ' ' -033 ' '- 0444' '+0-535' '+-5350' '-+5350' '--5350' '++5350' '0-' '0+' '0ate234' '1993-' '78844355+' )
declare -a failTestsIntBaseStr=( '0#0' '1#1' '0#1' '2#-1' '65#0' '65#a' '2#g' '2#G' )

# Fail test vars float 

declare -a failTestsFloatStr=( 'gdXm0.18#' 'gdXm0,18#' '.0-' '.0+' '7.01-' '238.446+' '0.' '0,' '.0.' ',0,' 'df-93.87#' 'df-93,87#' ' -2.057,' ' 09.002 ' '-09.002 ' '+09,002 ' '1.01 ' '-1,01 ' ' +2.02' ' .2,02' ' -33.66 ' '-33,66 ' ' -5 .4 ' '-5 ,4' ' -0. 06' '-0, 06 ' 'e13.62e06' '13E.06' '-13.62Ee06' '-13.62eE06' '+13.62ee06' '+13.62EE06' )

## Run tests and measure elapsed time

declare start=$EPOCHREALTIME

[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS DIRECT NUMBER INPUT" 
for (( i=0; i<100; i++)); do

# Auto-converted octal -> decimal
numTest 0160
numTest -0160
numTest +0160
# Auto-converted (0x) hex -> decimal
numTest 0x3932
numTest 0xaF3e2
numTest -0x3932
numTest +0x3932

numTest 0
numTest -0
numTest +0
numTest $int_max 
numTest -$int_max 
numTest +$int_max 
numTest $int_min 
numTest 94316667
numTest -94316667
numTest +94316667

numTest 0e0
numTest 0E0
numTest 087e-32
numTest 087E-32
numTest -087e32
numTest -087E32
numTest -22048e-16
numTest -22048E-16
numTest +100e050
numTest +100E050
numTest +7e-25
numTest +7E-25
numTest +000e+00
numTest +000E+00
numTest -853e1
numTest -853E1

numTest '0'
numTest '-0'
numTest '+0'
numTest "$int_max"
numTest "-$int_max"
numTest "+$int_max" 
numTest "$int_min"
numTest '99'
numTest '-2377'
numTest '+356'

numTest NaN
numTest 0.0
numTest -0.0
numTest +0.0
numTest 0,0
numTest -0,0
numTest +0,0
numTest .3930
numTest -.3930
numTest +.3930
numTest ,3930
numTest -,3930
numTest +,3930

numTest 261087.0532
numTest -261087.0532
numTest +261087.0532
numTest 261087,0532
numTest -261087,0532
numTest +261087,0532

# Auto-converted hex 0x

[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS HEX"

_IFS="$IFS"; IFS=$'\n'
for param in "${successTestsHex[@]}"; do
    numTest param
done
for param in "${successTestsPseudoHex[@]}"; do
    numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS CONVERTED INT HEX"
for param in "${successTestsConvertedOctal[@]}"; do
    numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS INT"
for param in "${successTestsInt[@]}"; do
    numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS INT STR"
for param in "${successTestsIntStr[@]}"; do
    numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS INT BASE STR"
for param in "${successTestsIntBaseStr[@]}"; do
    numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS OCTAL STR"
for param in "${successTestsOctalStr[@]}"; do
    numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS SCI INT STR"
for param in "${successTestsSciStr[@]}"; do
    numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS FLOAT"
for param in "${successTestsFloatStr[@]}"; do
    numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS SCI FLOAT STR"
for param in "${successTestsSciFloatStr[@]}"; do
    numTest param
done
[[ $print ]] && echo -e "\nBEGIN FAIL TESTS MISC"
numTest empty
for param in "${failTestsMiscStr[@]}"; do
    numTest param
done
[[ $print ]] && echo -e "\nBEGIN FAIL TESTS HEX"
numTest $failHexStr
numTest failHexStr
numTest $failHexStr1
numTest failHexStr1
numTest $failHexStr2
numTest failHexStr2
for param in "${failTestsHexStr[@]}"; do
    numTest param
done
[[ $print ]] && echo -e "\nBEGIN FAIL TESTS INT"
for param in "${failTestsIntStr[@]}"; do
    numTest param
done
[[ $print ]] && echo -e "\nBEGIN FAIL TESTS INT BASE STR"
for param in "${failTestsIntBaseStr[@]}"; do
    numTest param
done
[[ $print ]] && echo -e "\nBEGIN FAIL TESTS FLOAT"
for param in "${failTestsFloatStr[@]}"; do
    numTest param
done
IFS="$_IFS"

[[ $print ]] && echo -e "\nBEGIN FAIL TESTS DIRECT NUMBER INPUT"

numTest
numTest ''
numTest '+'
numTest '+'
numTest '-'
numTest ' '
numTest '_'
numTest 'E'
numTest 'e'
numTest 'a'
numTest 'A'
numTest 'x'
numTest 'X'

numTest '0x'
numTest 'x0'
numTest 0x
numTest x0

numTest 0-
numTest 0+
numTest '0-'
numTest '0+'
numTest -$int_min 
numTest +$int_min
numTest "-$int_min"
numTest "+$int_min"
numTest 0ate234
numTest '1993-'
numTest 1993-
numTest '78844355+'
numTest 78844355+

numTest .0-
numTest .0+
numTest 7.01-
numTest 238.446+

done 

declare stop=$EPOCHREALTIME
declare startMsStr="${start##*.}"; "${startMsStr%%[!0]*}" 2>/dev/null 
startMsStr="${startMsStr#*$_}"
declare stopMsStr="${stop##*.}"; "${stopMsStr%%[!0]*}" 2>/dev/null 
stopMsStr="${stopMsStr#*$_}"
declare -i elapsedSeconds=$(( (${stop//.} - ${start//.}) / 1000000 ))
#declare -i elapsedMs=$( [[ $stopMsStr -gt $startMsStr ]] && echo $(( ( stopMsStr - startMsStr ) / 1000 )) || echo $(( ( startMsStr - stopMsStr ) / 1000 )) )
declare -i elapsedMs
if [[ $stopMsStr -gt $startMsStr ]]; then
    elapsedMs=$(( ( stopMsStr - startMsStr ) / 1000 )) 
else
    elapsedMs=$(( ( startMsStr - stopMsStr ) / 1000 ))
fi

echo -e "ELAPSED TIME: $elapsedSeconds.${elapsedMs}s"

shopt -u extglob

提供的测试应涵盖大多数已检查的成功和失败案例,同时应用值传递和引用传递。

对于错误的十六进制 STR 值,failTestsHexStr() 也有失败的情况,在 INT var 赋值时发生伪“成功”,而 successTestsPseudoHex() 则在 var 赋值时演示伪成功结果,这是由于初步失败 - null -> 0 评价。

在打印结果时,_的输入出现了一个小问题,没有打印值,而是打印了引用名称,但是报告失败是可以的。

© www.soinside.com 2019 - 2024. All rights reserved.