2

在我的脚本中,我需要扩展一个间隔,例如:

input: 1,5-7

得到类似以下的东西:

output: 1,5,6,7

我在这里找到了其他解决方案,但它们涉及 python,我不能在我的脚本中使用它。

4

8 回答 8

2

Just Bash 4 Builtins 的解决方案

您可以使用Bash 范围扩展。例如,假设您已经解析了输入,您可以执行一系列连续的操作来将您的范围转换为逗号分隔的系列。例如:

value1=1
value2='5-7'
value2=${value2/-/..}
value2=`eval echo {$value2}`
echo "input: $value1,${value2// /,}"

所有关于 eval 危险的常见警告都适用,你最好用 Perl、Ruby、Python 或 AWK 解决这个问题。如果您不能或不会,那么您至少应该考虑在您的转换中包含一些管道工具,如trsed,以避免需要 eval。

于 2013-03-09T18:08:14.243 回答
2

尝试这样的事情:

#!/bin/bash

for f in ${1//,/ }; do
  if [[ $f =~ - ]]; then
    a+=( $(seq ${f%-*} 1 ${f#*-}) )
  else
    a+=( $f )
  fi  
done

a=${a[*]}
a=${a// /,}

echo $a

编辑:正如评论中提到的@Maxim_united,追加可能比一遍又一遍地重新创建数组更可取。

于 2013-03-09T18:12:57.077 回答
1

这也应该适用于多个范围。

#! /bin/bash
input="1,5-7,13-18,22"
result_str=""
for num in $(tr ',' ' ' <<< "$input"); do
    if [[ "$num" == *-* ]]; then
        res=$(seq -s ',' $(sed -n 's#\([0-9]\+\)-\([0-9]\+\).*#\1 \2#p' <<< "$num"))
    else
        res="$num"
    fi
    result_str="$result_str,$res"
done
echo ${result_str:1}

将产生以下输出:

1,5,6,7,13,14,15,16,17,18,22
于 2013-03-09T18:24:45.970 回答
0

使用序列表达式“{x..y}”的通用 bash 解决方案

#!/bin/bash
function doIt() {
  local inp="${@/,/ }"
  declare -a args=( $(echo ${inp/-/..}) )
  local item
  local sep
  for item in "${args[@]}"
  do
    case ${item} in
        *..*)  eval "for i in {${item}} ; do echo -n \${sep}\${i}; sep=, ; done";;
        *) echo -n ${sep}${item};;
    esac
    sep=,
  done
}
doIt "1,5-7"
  • 应该使用问题中示例之后的任何输入。也有多次出现x-y
  • 仅使用 bash 内置函数
于 2013-03-09T18:21:05.000 回答
0

使用来自@Ansgar Wiechers 和@CodeGnome 的想法:

input="1,5-7,13-18,22"
for s in ${input//,/ }
do
    if [[ $f =~ - ]]
    then
        a+=( $(eval echo {${s//-/..}}) )
    else
        a+=( $s )
    fi  
done
oldIFS=$IFS; IFS=$','; echo "${a[*]}"; IFS=$oldIFS

在 Bash 3 中工作

于 2015-04-30T15:27:09.480 回答
0
expand_commas()
{
    local arg
    local st en i

    set -- ${1//,/ }
    for arg
    do
        case $arg in
        [0-9]*-[0-9]*)
            st=${arg%-*}
            en=${arg#*-}
            for ((i = st; i <= en; i++))
            do
                echo $i
            done
        ;;
        *)
            echo $arg
        ;;
        esac
    done
}

用法:

result=$(expand_commas arg)

例如:

result=$(expand_commas 1,5-7,9-12,3)
echo $result

当然,您必须将分隔的单词重新转换为逗号。

输入错误时它有点脆弱,但它完全是 bash。

于 2013-03-09T18:02:48.777 回答
0

这是我的尝试:

input=1,5-7,10,17-20
IFS=, read -a chunks <<< "$input"

output=()
for chunk in "${chunks[@]}"
do
    IFS=- read -a args <<< "$chunk"
    if (( ${#args[@]} == 1 )) # single number                                   
    then
        output+=(${args[*]})
    else # range                                                                
        output+=($(seq "${args[@]}"))
    fi
done
joined=$(sed -e 's/ /,/g' <<< "${output[*]}")
echo $joined

基本上用逗号分开,然后解释每一段。然后在末尾加上逗号。

于 2013-03-09T18:09:58.937 回答
0

考虑到所有其他答案,我想出了这个解决方案,它不使用任何子外壳(但一次调用eval大括号扩展)或单独的进程:

# range list is assumed to be in $1 (e.g. 1-3,5,9-13)

# convert $1 to an array of ranges ("1-3" "5" "9-13")
IFS=,
local range=($1)
unset IFS

list=() # initialize result list
local r
for r in "${range[@]}"; do
    if [[ $r == *-* ]]; then
        # if the range is of the form "x-y",
        # * convert to a brace expression "{x..y}",
        # * using eval, this gets expanded to "x" "x+1" … "y" and
        # * append this to the list array
        eval list+=( {${r/-/..}} )
    else
        # otherwise, it is a simple number and can be appended to the array
        list+=($r)
    fi
done

# test output
echo ${list[@]}
于 2016-08-23T12:29:19.453 回答