如何在 Bash 中将 `4-7` 替换为 `4,5,6,7`
How to replace `4-7` into `4,5,6,7` in Bash
目标
我有一个长字符串 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]*\)/{..}/g')"
然后评估字符串以扩展大括号
b="$(eval echo $a)"
但是,当我 运行 最后一个命令时,扩展完成 "in a factorial way" (导致 RAM 使用量激增)。
问题
如何将字符串中 4-7
类型的模式替换为 4,5,6,7
?
版本
我在 Mac OS X 10.11.3
并使用 Terminal 2.6.1 (361.1)
这是因为您的代码并没有按照您的预期行事。仅考虑 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
如果您没有注意到两者之间的区别,那就是实际结果是两个大括号表达式的所有可能组合。您的实际案例有更多组合,导致 combinatorial explosion.
一个使用 GNU awk 的答案,它应该在大输入下表现更好:
#!/usr/bin/env gawk -f
{
while ( match([=10=], /([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
s="4092-4093,4095-4097,4104,4107,4111,4125-4127"
a="$(echo $s | sed 's/\([0-9]*\)-\([0-9]*\)/{..}/g' | tr "," " ")"
b=""
for i in ${a[@]}
do
add="$(eval echo $i)"
b="${b} ${add}"
done
echo $b
Perl 来拯救:
echo 4092-4093,4095-4097,4104,4107,4111,4125-4127 \
| perl -lane 's/-/../g;print join ",", eval'
在 Perl 中,范围是用 ..
运算符而不是破折号书写的。 运行 eval 将字符串扩展为实际列表。
目标
我有一个长字符串 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]*\)/{..}/g')"
然后评估字符串以扩展大括号
b="$(eval echo $a)"
但是,当我 运行 最后一个命令时,扩展完成 "in a factorial way" (导致 RAM 使用量激增)。
问题
如何将字符串中 4-7
类型的模式替换为 4,5,6,7
?
版本
我在 Mac OS X 10.11.3
并使用 Terminal 2.6.1 (361.1)
这是因为您的代码并没有按照您的预期行事。仅考虑 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
如果您没有注意到两者之间的区别,那就是实际结果是两个大括号表达式的所有可能组合。您的实际案例有更多组合,导致 combinatorial explosion.
一个使用 GNU awk 的答案,它应该在大输入下表现更好:
#!/usr/bin/env gawk -f
{
while ( match([=10=], /([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
s="4092-4093,4095-4097,4104,4107,4111,4125-4127"
a="$(echo $s | sed 's/\([0-9]*\)-\([0-9]*\)/{..}/g' | tr "," " ")"
b=""
for i in ${a[@]}
do
add="$(eval echo $i)"
b="${b} ${add}"
done
echo $b
Perl 来拯救:
echo 4092-4093,4095-4097,4104,4107,4111,4125-4127 \
| perl -lane 's/-/../g;print join ",", eval'
在 Perl 中,范围是用 ..
运算符而不是破折号书写的。 运行 eval 将字符串扩展为实际列表。