我收到一条提取 IPv6 地址的命令:
/usr/bin/ip a | grep inet6 | grep -vE 'fe80|host' | sed -e 's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d'
有人可以帮我分解一下sed
替换吗?
sed 命令在没有;t;d
答案1
看来你想学点东西,我很欣赏。 symcbeansed
向您解释了该命令,但我想添加一些您可以从代码中学到的更多内容。你可以学会避免坏习惯。 (-;
- 整个命令通过两个
grep
s 和一个进行管道传输sed
,这几乎总是无意义的。在 中sed
,您可以地址通过在行前面加上正则表达式来替换行,这可以grep
动态执行功能,就像sed -n '/foo/s/pattern/replace/p'
仅在带有 . 的行上进行替换一样foo
。因此 '/inet6/!d' 可以删除所有不带 (!
)的行,然后您可以用 (或者使用扩展正则表达式选项: )inet6
替换第二grep
行。/fe80/d;/host/d
-E
/fe08|host/d
-e
如果只有一个命令(或由 串联的多个命令),则 是可选的;
。如果你去掉多余的东西,事情会变得更容易阅读。- 正如已经指出的,以 开始正则表达式
^.*
是无意义的。贪婪的人*
无论如何都会从一开始就匹配任何东西,所以不要用不必要的锚点来分散读者的注意力。 - 同样适用于
.*$
最后。拆下锚。 - 如果模式包含斜杠,请为
s
命令使用不同的分隔符。几乎任何字符都是允许的,那么为什么要通过用反斜杠填充表达式来使表达式更难以阅读呢?下划线是很好的选择,例如:s_.*inet6 \([^ ]*\)/.*_\1_
- 使用
\(…\)
和\1
可能很有用,但如果这只是提取中间部分,那么简单地分别删除开头和结尾可能会更全面:s/.*inet6 //;s_/.*__
- 根据
sed
定义,命令后面可选地跟随一个跳转标记,在本例中t
就是这样。;d
不常见,但合法。 GNU 版本sed
不允许在跳转标记中使用分号,但将其解释为命令分隔分号。在这种情况下,不会有跳转标记,这意味着“如果进行替换,则跳转到脚本末尾”。但其他sed
版本会抛出错误。让脚本不兼容这样的细节,实在是太恶心了!例如,可以通过单独的脚本来避免这种情况-e '…;t' -e d
。或者通过在新行中编写命令。 - 在本例中,其想法
t;d
是避免替换失败时输出混乱。一个好主意,但是有一个真正的工具可以实现这一点,即命令p
的标志s
:s_.*inet6 \([^ ]*\)/.*_\1_p;d
。如果进行了更换,p
请冲洗缓冲区。更易于阅读和便携。 - 尾随
d
命令可以替换为-n
抑制默认输出的选项,但这是一个品味问题。
最后我们可以比较命令
/usr/bin/ip a | grep inet6 | grep -vE 'fe80|host' | sed -e 's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d'
/usr/bin/ip a | sed '/inet6/!d; /fe80/d; /host/d; s/.*inet6 //; s_/.*__p; d'
或用 ERE-n
代替d
:
/usr/bin/ip a | sed -En '/inet6/!d; /fe80|host/d ;s/.*inet6 //; s_/.*__p'
高尔夫球手可能会写的代码
sed -En '/fe80|host/!s_.*inet6 ([^ ]*)/.*$_\1_p'
但这对我来说似乎不太可读。
答案2
我首先会稍微简化一下你的表达......
LINE=" inet6 fd86:73ea:ff6b:0:141b:ca40:741b:ec0c/64 scope global noprefixroute"
echo $LINE | sed -e' s/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d'
这-e意味着下一个参数是 sed 脚本。
's/pattern/replace/' 表示在输入中查找“pattern”并替换为“replace”。
这里的模式是/^.*inet6 \([^ ]*\)\/.*$/
'/' 标记模式的开始和结束。
^ 和 $ 字符始终分别与输入字符串的开头和结尾匹配。观察输入字符串总是有一个开始和结束 - 当您想要匹配或替换相对于这些位置的元素时,这些会变得有用。
.*
表示任何字符出现零次或多次。在上面的 $LINE 中,它匹配单个空格字符。
inet6
表示文字字符串“inet6”(带有尾随空格)。
括号\(pattern\)
告诉 sed 不仅匹配输入中的子模式,而且存储它以供以后使用。
均值\/
与文字“/”匹配(参见上文 - 如果没有前缀,“/”字符表示模式中的结构元素)。
.*$
只是意味着匹配任何剩余的字符直到行尾。
/
标记模式的结束。
\1
这就是替换。这里 1 指的是模式找到的第一个存储的匹配(但只有一个)。