我是一名经验丰富的软件开发人员,但现在才回过头来学习 sed 技能。
text.txt
我有一个包含以下格式的行的文件:
blah blah blah
key-1:foo-bar
key-2:foo-baz
more blah blah
我想分离出键/值对 sushc 作为key-1
和foo-bar
并将它们发送给命令do-something
,但我想转换foo-bar
为foo_bar
!
要提取键/值对,目前我有这样的事情:
sed -nE 's/([^:]+):(.+)/\1 \2/p' test.txt
这将打印:
key-1 foo-bar
key-2 foo-baz
到目前为止一切顺利。现在,如果我想将其传递给do-something
,我想我可以做这样的事情并将其传送到xargs
或类似的东西:
sed -nE 's/([^:]+):(.+)/do-something -a \1 -b \2/p' test.txt | xargs #something or another
我还没有查看 xargs 部分。在查看之前,我想知道:我怎样才能-
用替换_
,但只能在第二个 sed 匹配组中,而不是第一个?
或者有没有更简单的 *nix 工具我可以使用?(我也欢迎有关 xargs 部分的建议do-something
。)谢谢。
答案1
GNUsed
允许你替换-
一行中第二次及之后出现的 。像这样:
<test.txt sed -nE '/[^:]+:/ {s/-/_/2g; s/:/ / p}'
对于您的示例数据,这种方法可能就足够了;但是,如果第二个-
在键中(key-3-4:foo
)或者第一个-
在值中(key5:bar-baz
),则它将会失败。
可以采用一种通用方法来识别密钥并保持其完好无损。我的想法是:
<test.txt sed -nE '/^[^:]+:/ {
h
s/:.*/ /
x
s/[^:]+://
s/-/_/g
H
g
s/\n//
p
}'
解释:
- 将一行加载到模式空间中,因为这就是它的
sed
作用。 /^[^:]+:/
– 匹配以一个或多个非冒号开头,后跟一个冒号的行;其余代码被分组,{}
这意味着对于匹配的行将执行整个组,而对于不匹配的行将被忽略。h
– 将模式空间复制到保存空间。s/:.*/ /
– 将冒号(由于初步匹配,因此保证存在)及其后的所有内容替换为空格字符。这将匹配模式空间中的第一个冒号,因为这是正则表达式的工作方式。结果,模式空间包含键和空格。x
– 交换保持空间和模式空间的内容。现在整行又回到了模式空间中;键和空间安全地保存在保持空间中。s/[^:]+://
– 删除模式空间中第一个冒号之前的所有内容。剩下的就是值。s/-/_/g
– 将所有出现的 替换-
为_
。H
– 将模式空间附加到保留空间。旧内容(键加空格)和附加字符串(更改后的值)之间将有一个换行符。g
– 将保持空间复制到模式空间。s/\n//
– 删除上述换行符。p
– 打印当前模式空间。
笔记:
- 保留空间从一行到另一行仍然存在,但是
h
每次我们需要重新使用它时都会覆盖它(),因此无需明确清除它。 - 问问自己是否想
g
加入s/-/_/g
。