模式匹配逗号分隔数字作为字符串

时间:2012-10-25 09:20:19

标签: linux bash shell bash4

如何为此进行模式匹配:4,7,9 .... n - 以逗号分隔的数字字符串作为用户输入?我正在使用case语句并且我相信,例如使用模式匹配而不是正则表达式。这是用户得到的:

Do you want to delete any of these?
     [ 1 ]   launch-EOsgR4
     [ 2 ]   launch-SWZQdJ
     [ 3 ]   launch-tHAdIm
     [ 4 ]   launchd-235.z4KTVx
     [ 5 ]   launchd-257.nM2wOZ
     [ 6 ]   progress.log
     [ 7 ]   ssh-8pISGGnlZ5
----------------------------------------
Single: 4; Multiple: 2,3; Range: 4..7
a to delete all; n to cancel and exit
----------------------------------------
( [1][2][3][4][5][6][7] | a | n ): 

而且,如上所示,用户可以选择单个数字(简单),范围:6..9(也不是那么难)或多个:3,5,6(有点难以找到'单号'选项)。这就是我到目前为止所做的......

#!/usr/bin/env bash

NL=$(echo -e "\033[0;0m")
BD=$(echo -e "\033[0;1m")
ERR=$(printf "\n%-25s" "$(echo -e "\033[1;31m[ ERROR ]\033[0m")")
WRN=$(printf "\n%-25s" "$(echo -e "\033[1;33m[ WARN  ]\033[0m")")


ls /tmp | tail -n9 > list_of_file
s=$(printf "%-40s" "-")

function lstFile()
{
    local file=$1
    if [[ -s $file ]]
    then
        LINES=( $(cat $file) ); echo ""
        for ix in ${!LINES[@]}
        do
            printf "%-5s%-14s%s\n" "" "${BD}[ $(( ix+1 )) ]" "${LINES[$ix]}${NL}"
        done
   else
       exit 0
   fi

   LST=$(echo ${!LINES[@]}|awk -v ORS=']' '{for (i=1; i<=NF; i++) print "["($i+1)}')
}

function delOpt()
{
    echo "${s// /-}"
    echo -e "Single: 4; Multiple: 2,3; Range: 4..7"
    echo -e "${BD}a${NL} to delete all; ${BD}n${NL} to cancel and exit"
    echo "${s// /-}"
    echo -n "( $1 | a | n ): "
}

echo -e "\nDo you want to delete any of these?"
lstFile list_of_file

ANS=
until [[ "${ANS}" == "N" || "${ANS}" == "n" || "${ANS}" == "e" ]]
do
    delOpt $LST
    read ANS && echo ""

    ANS=$( tr '[:upper:]' '[:lower:]' <<< "$ANS" )
    [[ -n $(echo $ANS|grep -E -w "^[aen0-9,]{1,}") ]] && : || ANS="X"

case ${ANS} in
    [0-9]..[0-9] )

    for ix in $(eval echo \{$ANS\}); do
        LINE=${LINES[(( $ix-1 ))]}
        echo -e "Deleting:  ${BD}${LINE}${NL}"
        sed -i -c "/$LINE/d" list_of_file
    done
    unset LINE
    lstFile list_of_file
    ;;

    [0-9,]* )
    ANS=$(echo $ANS | awk -F' |,' '{for (i=1; i<=NF; i++) print $i}')
    for ix in $ANS; do
        if [[ $ix -gt ${#LINES[@]} ]]
        then
            echo  "${ERR}Out-of-range value: ${bd}$ix${NL}"
        else
            LINE=${LINES[(( $ix-1 ))]}
            echo -e "Deleting:  ${BD}${LINE}${NL}"
            sed -i -c "/$LINE/d" list_of_file > /dev/null 2>&1
        fi
    done
    unset LINE
    lstFile list_of_file
    ;;

    a )              
    for ix in ${LINES[@]}; do
        echo "Deleting:  ${BD}${ix}${NL}"
        sed -i -c "/$ix/d" list_of_file
    done
    exit 0
    ;;

    n|e ) exit 0
    ;;

    * ) echo "${WRN}Invalid entry! Should be digit or ${BD}a${NL} for All."
    printf "%-14s%s\n\n" "" "Otherwise, enter ${UL}n${NL}o or ${UL}e${NL}xit to quit"
    ;;
esac
done

工作正常(有点),但有一些竞争条件。 e.g。

2,d,7 - throws in: bad array subscript
6..10 - throws in: (( 6..11-1 )): syntax error: invalid arithmetic operator (error token is "..11-1 ))")
but, 
6..9  - throws in: first RE may not be empty

有没有办法有单独的选项来捕捉'单个'和'多个'数字输入?还有关于整体改进的任何建议吗?

任何帮助非常感谢。干杯!!

<小时/> 更新:31/10

它现在正在运作。感谢Alepac提出的建议 为了以防万一,如果其他人也在寻找类似的东西,我会把它放在这里。根据我的原始代码,此函数将根据用户输入从文件list_of_file中删除行。

function chkINPUT()
{
    local I_PUT=( "$@" )
    local MAX=${#LINES[@]}
    #IFS=', ' read -a SPLITTED <<< "$I_PUT"
    local SPLITTED=( $(echo "${I_PUT[@]}" | awk -F',| ' '{for (i=1; i<=NF; i++) print $i}') )

    for idx in "${!SPLITTED[@]}"
    do
        SPLTD=${SPLITTED[idx]}

        # Check if it's a range [4..7]
        if [[ "${SPLTD}" =~ ^[0-9]{1,2}\.\.[0-9]{1,2}$ ]]
        then
            for ix in $(eval echo \{$SPLTD\}); do
                if (( ${ix} <= $MAX )); then
                    LINE=${LINES[(( ix-1 ))]}
                    echo -e "Deleting:  ${BD}${LINE}${NL}"
                    sed -i -e "/$LINE/d" list_of_file 2>&1 > /dev/null
                    unset LINE
                else
                    echo -e "\t${ix} => Out of range"
                    break
                fi
            done

        # Check if it's a single input
        elif [[ "${SPLTD}" =~ ^[[:digit:]]+$ ]]
        then
            if (( ${SPLTD} <= $MAX )); then
                LINE=${LINES[(( SPLTD-1 ))]}
                echo -e "Deleting:  ${BD}${LINE}${NL}"
                sed -i -e "/$LINE/d" list_of_file 2>&1 > /dev/null
                unset LINE
            else
                echo  "${ERR}Out-of-range value: ${bd}$SPLTD${NL}"
            fi

        else
            echo  "${ERR}Invalid entry: ${bd}$SPLTD${NL}; must be an integer from the list!"
        fi
    done
}

然后,像这样使用它:

ANS= 
until [[ "${ANS}" == "N" || "${ANS}" == "n" || "${ANS}" == "e" ]] 
do
    delOpt $LST
    read ANS && echo ""
    ANS=$( tr '[:upper:]' '[:lower:]' <<< "$ANS" )

    case ${ANS} in
        [0-9]* )
        chkINPUT ${ANS}
        ;;    

希望它有所帮助。干杯!!

1 个答案:

答案 0 :(得分:2)

我认为最好的选择可能是将字符串拆分为数组,然后在数组上循环。接下来的问题可能是如何拆分字符串,这是一个选项:

#!/bin/sh
ANS="1,2,3..4"
echo ans = ${ANS}
IFS=', ' read -a SPLITTED <<< "$ANS"
for index in "${!SPLITTED[@]}"
do
    echo "$index ${SPLITTED[index]}"
done

结果

alepac@mypc:/tmp$ . test.sh 
ans = 1,2,3..4
0 1
1 2
2 3..4

通过这种方式你甚至可以像1,3..5,8,n一样正确地解析它

相关问题