0

目标

我有一个长字符串s,它代表一系列用逗号和破折号分隔的数字(见下文)。当多个数字相互跟随时,两个极端数字被写入并用破折号分隔。例如,系列4,5,6,7写为4-7。我的目标是扩展此字符串以使所有数字都用逗号分隔(4-7应该成为4,5,6,7)。

我做了什么

这是字符串的示例

s="4092-4093,4095-4097,4104,4107,4111,4125-4127"

我想首先4-7通过{4..7}(使用 sed 反向引用)替换类型的模式

a="$(echo $s | sed 's/\([0-9]*\)-\([0-9]*\)/{\1..\2}/g')"

然后评估字符串以扩展大括号

b="$(eval echo $a)"

但是,当我运行最后一个命令时,扩展是“以阶乘方式”完成的(导致 RAM 使用量激增)。

问题

4-7如何在4,5,6,7我的字符串中替换那种模式?

版本

我在Mac OS X 10.11.3使用Terminal 2.6.1 (361.1)

4

4 回答 4

1

Perl 的救援:

echo 4092-4093,4095-4097,4104,4107,4111,4125-4127 \
| perl -lane 's/-/../g;print join ",", eval'

在 Perl 中,范围是用..运算符而不是破折号编写的。在其上运行eval会将字符串扩展为实际列表。

于 2016-08-15T23:42:35.067 回答
1

使用 GNU awk 的答案应该在大输入下表现更好:

#!/usr/bin/env gawk -f
{
    while ( match($0, /([0-9]+)-([0-9]+)/, arr) ) {
        s = arr[1]
        for (i=int(arr[1]) + 1; i<=int(arr[2]); i++) {
            s = s "," i
        }
        gsub(arr[1] "-" arr[2], s)
    }
    print
}

或者,在纯 bash 中(使用少量数据获得更好的性能):

s="4092-4093,4095-4097,4104,4107,4111,4125-4127"
re='([0-9]*)-([0-9]*)'
while [[ $s =~ $re ]]; do
  eval_str="printf -v replacement '%s,' {${BASH_REMATCH[1]}..${BASH_REMATCH[2]}}"
  eval "$eval_str"
  replacement=${replacement%,}
  s=${s//${BASH_REMATCH[0]}/$replacement}
done
于 2016-08-15T23:13:46.170 回答
1
s="4092-4093,4095-4097,4104,4107,4111,4125-4127"
a="$(echo $s | sed 's/\([0-9]*\)-\([0-9]*\)/{\1..\2}/g' | tr "," " ")"
b=""
for i in ${a[@]}
do
   add="$(eval echo $i)"
   b="${b} ${add}"
done    
echo $b
于 2016-08-15T23:14:25.097 回答
0

这是因为您的代码没有执行您显然认为的那样。只考虑s="4092-4093,4095-4097". 运行后sed会导致a={4092..4093},{4095..4097}. 在运行后会eval导致:

b=4092,4095 4092,4096 4092,4097 4093,4095 4093,4096 4093,4097

我猜你期待这样的事情:

b=4092,4093,4095,4096,4096

如果您没有注意到两者之间的区别,那就是实际结果是两个大括号表达式的所有可能组合。您的实际情况是有更多组合,导致组合爆炸

于 2016-08-15T23:06:21.397 回答