使用 sed 在 bash 中查找并替换其后继数字
Use sed to find and replace a number following by its successor in bash
我有一个字符串,其中包含多次出现的数字范围,它们之间用逗号分隔,例如
2-12,59-89,90-102,103-492,593-3990,3991-4930
现在我想删除所有直接相邻的范围并将它们从字符串中删除,即删除任何形式为 -(x),(x+1)
的内容,以获得如下内容:
2-12,59-492,593-4930
谁能想出一种方法来实现这个?老实说,我不能 post 我尝试过的任何东西,因为我所有的尝试都非常失败。在我看来,似乎不可能使用 sed 实际找到 -(x),(x+1)
形式的任何内容,因为这需要对找到的数字进行操作或将找到的数字与另一个数字进行比较,而另一个数字必须是当前命令的一部分搜索号码。
如果每个人都同意 sed 不是执行此操作的正确工具,我将采用另一种方式,但如果可能的话,我仍然很感兴趣。
awk 解决办法:
awk -F'-' '{ r=;
for (i=2; i<=NF; i++) {
split($i, a, ",");
r=sprintf("%s%s", r, a[2]-a[1]==1? "" : FS $i)
}
print r
}' file
-F'-'
- 将 -
(连字符)视为字段分隔符
r
- 结果字符串
split($i, a, ",")
- 通过分隔符 ,
将相邻的 range 边界拆分为数组 a
a[2]-a[1]==1
- 关键条件,反映 (x),(x+1)
输出:
2-12,59-492,593-4930
和awk
awk -F, -v RS="-" -v ORS="-" '!=+1' file
通过适当的分隔符设置,当第二个字段不是+1 时打印记录。
RS
是记录分隔符,ORS
是输出记录分隔符。
测试:
> awk -F, -v RS="-" -v ORS="-"
'!=+1' <<< "2-12,59-89,90-102,103-492,593-3990,3991-4930"
2-12,59-492,593-4930
这可能对你有用 (GNU sed):
sed -r ' s/^/\n/;:a;ta;s/\n([^-]*-)([0-9]*)(.*,)/\n\n\n/;Td;:b;s/(\n.*\n.*)9(_*\n)/_/;tb;s/(\n.*\n)(_*\n)//;s/$/\n0123456789/;s/(\n.*\n[0-9]*)([0-8])(_*\n.*)\n.*(.).*//;:z;tz;s/(\n.*\n[^_]*)_([^\n]*\n)//;tz;:c;tc;s/([0-9]*-)\n(.*)\n(.*)\n,()-/\n/;ta;s/\n(.*)\n.*\n,/,\n/;ta;:d;s/\n//g' file
此概念验证 sed 解决方案迭代递增并将一个范围的结尾与另一个范围的开头进行比较。如果比较结果为真,则将两者都删除并重复,否则移至下一个范围并重复,直到比较完所有范围。
我有一个字符串,其中包含多次出现的数字范围,它们之间用逗号分隔,例如
2-12,59-89,90-102,103-492,593-3990,3991-4930
现在我想删除所有直接相邻的范围并将它们从字符串中删除,即删除任何形式为 -(x),(x+1)
的内容,以获得如下内容:
2-12,59-492,593-4930
谁能想出一种方法来实现这个?老实说,我不能 post 我尝试过的任何东西,因为我所有的尝试都非常失败。在我看来,似乎不可能使用 sed 实际找到 -(x),(x+1)
形式的任何内容,因为这需要对找到的数字进行操作或将找到的数字与另一个数字进行比较,而另一个数字必须是当前命令的一部分搜索号码。
如果每个人都同意 sed 不是执行此操作的正确工具,我将采用另一种方式,但如果可能的话,我仍然很感兴趣。
awk 解决办法:
awk -F'-' '{ r=;
for (i=2; i<=NF; i++) {
split($i, a, ",");
r=sprintf("%s%s", r, a[2]-a[1]==1? "" : FS $i)
}
print r
}' file
-F'-'
- 将-
(连字符)视为字段分隔符r
- 结果字符串split($i, a, ",")
- 通过分隔符,
将相邻的 range 边界拆分为数组 a[2]-a[1]==1
- 关键条件,反映(x),(x+1)
a
输出:
2-12,59-492,593-4930
和awk
awk -F, -v RS="-" -v ORS="-" '!=+1' file
通过适当的分隔符设置,当第二个字段不是+1 时打印记录。
RS
是记录分隔符,ORS
是输出记录分隔符。
测试:
> awk -F, -v RS="-" -v ORS="-"
'!=+1' <<< "2-12,59-89,90-102,103-492,593-3990,3991-4930"
2-12,59-492,593-4930
这可能对你有用 (GNU sed):
sed -r ' s/^/\n/;:a;ta;s/\n([^-]*-)([0-9]*)(.*,)/\n\n\n/;Td;:b;s/(\n.*\n.*)9(_*\n)/_/;tb;s/(\n.*\n)(_*\n)//;s/$/\n0123456789/;s/(\n.*\n[0-9]*)([0-8])(_*\n.*)\n.*(.).*//;:z;tz;s/(\n.*\n[^_]*)_([^\n]*\n)//;tz;:c;tc;s/([0-9]*-)\n(.*)\n(.*)\n,()-/\n/;ta;s/\n(.*)\n.*\n,/,\n/;ta;:d;s/\n//g' file
此概念验证 sed 解决方案迭代递增并将一个范围的结尾与另一个范围的开头进行比较。如果比较结果为真,则将两者都删除并重复,否则移至下一个范围并重复,直到比较完所有范围。