0

假设我们从这个字符串开始:

echo "1:apple:fruit.2:banana:fruit.3:cucumber:veggie.4:date:fruit.5:eggplant:veggie.">list.tmp

并希望得到这个结果:

1-apple:fruit
2-banana:fruit
3-cucumber:veggie
4-date:fruit
5-eggplant:veggie



为什么会这样:

sed -e 's/\./\n/g' -i list.tmp
sed -e 's/:/-/' list.tmp

但不是这个:

sed -e 's/\./\n/g' -e 's/:/-/' list.tmp



第二个命令产生了这个,显然在寻找每行上第一次出现的 ':' 时忽略了新的换行符。

1-apple:fruit
2:banana:fruit
3:cucumber:veggie
4:date:fruit
5:eggplant:veggie

使用输入的扩展版本:

echo "one:apple:fruit.two:banana:fruit.three:cucumber:veggie.four:date:fruit.five:eggplant:veggie.">list.tmp

我想得到这个结果:

one-apple:fruit
two-banana:fruit
three-cucumber:veggie
four-date:fruit
five-eggplant:veggie
4

2 回答 2

2

这可能对您有用(GNU sed):

sed -E 'y/./\n/;s/^([^:]*):/\1-/mg' file

将所有句点转换为换行符。

使用 GNU 特定m或多行标志,从模式空间中每一行的开头替换(即,^由冒号由非冒号字符和破折号组成-。这有效地将每行中的第一个冒号替换为破折号。

于 2020-04-05T00:04:14.187 回答
2

将关键评论转移到答案中。

原始数据

您忘记了双重公式g中第二个命令的修饰符。-e当第一个-e完成时,所有的行仍然在模式空间(sed 中的主要工作区域)中——它们不会变成 5 个单独读取的行。你读了一行;你还在处理它。请注意,您需要使用修改后的模式:

s/\([0-9]\):/\1-/g

结合这些,在 GNU sed(如问题标题中规定)中,您将获得:

sed -e 's/\./\n/g' -e 's/\([0-9]\):/\1-/g' list.tmp

请注意,POSIXsed和其他版本的第一个表达式sed中的换行符替换有不同的规则。-e

考虑使用awk

如果将工具从 更改sedawk是一种选择,您可以在 中更简单地执行此操作awk,如Ed Morton评论中所示。由于该解决方案不需要更改以处理修改后的数据,因此它显然具有优势 - 缺点是它不使用sed. 在“现实世界”中,您使用最好的工具来完成这项工作——在这个例子中,就是awk.

扩展数据

使用“扩展”输入,没有方便的单个数字,但您想将每行的第一个冒号更改为破折号,您必须更加努力:

sed -e 's/\./\n/g' \
    -e  's/^\([^:]*\):/\1-/' \
    -e 's/\(\n[^:]*\):/\1-/g' \
    list.tmp
  • 第一个-e不变。
  • 第二个在模式空间的开头查找一个非冒号序列,后跟一个冒号,并将其替换为非冒号序列和一个破折号。修饰符在g这里无关紧要。
  • 第三个-e查找换行符,后跟一系列非冒号,后跟冒号,并将其替换为换行符、非冒号序列和破折号。修饰符在g这里非常相关。

您可以将它们全部展平到一行上,但-e如果最后两个选项被布置在不同的行上,则更容易看到它们之间的相似之处。

您还可以使用该-E选项尝试 ERE(扩展正则表达式),并将两个单独的替换组合为一个:

{
echo "1:apple:fruit.2:banana:fruit.3:cucumber:veggie.4:date:fruit.5:eggplant:veggie."
echo "one:apple:fruit.two:banana:fruit.three:cucumber:veggie.four:date:fruit.five:eggplant:veggie."
} |
sed -E -e 's/\./\
/g' -e 's/((^|\n)[^:]+):/\1-/g'

这会产生:

1-apple:fruit
2-banana:fruit
3-cucumber:veggie
4-date:fruit
5-eggplant:veggie

one-apple:fruit
two-banana:fruit
three-cucumber:veggie
four-date:fruit
five-eggplant:veggie

如果您不想要额外的空行,请删除最后的换行符:

{
echo "1:apple:fruit.2:banana:fruit.3:cucumber:veggie.4:date:fruit.5:eggplant:veggie."
echo "one:apple:fruit.two:banana:fruit.three:cucumber:veggie.four:date:fruit.five:eggplant:veggie."
} |
sed -E -e 's/\./\
/g' -e 's/((^|\n)[^:]+):/\1-/g' -e 's/\n$//'

反斜杠换行符在 GNUsed和 POSIX(包括 BSD 和 macOS)中都能正常工作sed\n你可以用GNU重新替换它sed。该命令\n的替换部分s///在 BSD (macOS) 中不起作用sed。POSIXsed要求您使用反斜杠来转义替换文本中的文字换行符:

可以通过将 a 代入其中来拆分行<newline>。应用程序应通过在其前面添加一个 来避开<newline>替换中的<backslash>

GNU sed 更灵活。

此外(根据potong回答),还有一个 GNU 特定的修饰符m,您可以使用它在一次操作中进行多行匹配。

于 2020-04-04T22:44:30.137 回答