我遇到了一个很好的用法来sed
进行替换,并使用其成功状态作为打印该行的条件:
$ seq 3 | sed -n 's/2/B/ p'
B
我想知道这个简短的形式是否可以扩展以执行更多操作。喜欢,
- 仅当替换成功时才打印该行,但是
- 在打印之前,我需要做更多替换
这可能吗?我尝试了以下方法但失败了:
$ seq 3 | sed -n 's/2/B/ {p}'
sed: -e expression #1, char 8: unknown option to `s'
答案1
您可以用作/2/
更复杂命令的地址:
sed -n '/2/ { s/2/B/; /BB/d; p; }'
对于任何至少包含一个的行2
,这将替换第一次出现的2
with B
(在这种情况下,此替换也可以缩短为s//B/
空正则表达式表示“使用最近匹配的表达式”),然后丢弃该行,如果它现在包含子字符串BB
.然后该行将被输出(如果它没有被丢弃)。其他行根本不会输出,因为-n
.
你也可以使用
sed -e '/2/!d' -e 'other commands'
这将删除所有不包含的行2
,然后应用other commands
到其余行。
使用多个表达式(每个表达式都使用 给定-e
)是将一系列命令应用到输入流的标准方法sed
。大多数当前的sed
实现也理解用 分隔表达式;
。 GNUsed
不必在;
之前看到}
。
答案2
p
这是命令的标志sed
,它不是p
sed
命令。
要执行某些操作(除了p
打印生成的模式空间或w
将其写入文件之外,w
这是可以与命令一起使用的另一个标志s
),仅当进行了替换时,您才可以使用该t
命令(或T
GNU 中的命令sed
)如果有替换(如果没有 for -T
),则分支到某个标签(如果没有标签则分支到末尾),因此您可以执行以下操作:
GNU 特定:
sed -n 's/2/B/;T;=;p;#and other commands if there were substitutions'
就像:
substitute(the first occurrence of 2 with B in the pattern space)
if (no substitution was made so far)
goto(end)
print-pattern-space
print-input-line-number
: end
标准:
sed '
s/2/B/;t more
d
:more
p;=;#and other commands
'
或者您可以使用:
sed -n '/regexp/ { s//replacement/; p; =; }'
使用空正则表达式会重用前一个正则表达式,如ed
.
答案3
您可以使用“{ .... }”对命令进行分组。一般形式为:
/<regexp>/ {
cmd1
cmd2
...
}
这里的“cmd”是任何常规的 sed 命令,例如“s/.../.../”,还有“p”、“q”等。将其视为一条“规则”,适用于所有命令与正则表达式匹配的行。您还可以通过在正则表达式和左大括号之间使用感叹号来否定规则。在这种情况下,该规则将应用于所有行不是匹配正则表达式。
下面是一个示例,打印从脚本开头到第一个非注释行的所有仅注释行:
sed '/^[[:blank:]]*#/ !{
q
}'
所有以“#”开头的行都匹配规则,因此只是打印(我们没有使用“-n”选项来抑制它)。与正则表达式不匹配的第一行(实际上是所有行,但实际上只处理第一行)会触发简单退出 sed 的规则。
但还有另一种方法比使用规则更强大:您定义所谓的“标签”。使用类似于 GOTO 的命令分支到的符号位置。它是这样工作的:
1)无条件分支 sed 脚本的工作方式如下:读取第一行输入,然后将所有 sed 命令应用到它,一个接一个(如果该行更改,则进一步的命令将应用于此更改的行),直到到达最后一个命令。如果不使用“-n”选项,结果将打印到 .只有这样,才会读取下一行输入,并重新开始该过程,直到处理完最后一行输入。
有一种方法可以更改命令应用于当前行的顺序:
: [标签]
该命令本身不执行任何操作,但其他命令可以使用它来分支到此位置。您也许还记得 BASIC 等语言及其“goto”命令?这标记了后续“goto”(类似)命令可以跳转到的标签。分支到先前定义的标签的无条件 sed 命令是
b [标签]
“b”代表“分支”。对应于您使用“:”命令定义的内容。如果省略标签,则执行分支到脚本末尾。
这是一个例子:您得到一个程序,但不幸的是代码缩进是用制表符而不是空格完成的,您想更改它。显然,您只想更改任何代码前面的选项卡,而不是代码内的选项卡,因为可能需要这些选项卡。可惜的是,正则表达式中没有直接实现此目的的设备,因此您必须自己实现它。算法是:只要一条线是以下形式空格,后跟制表符将第一个制表符更改为 8 个空格,然后在同一行上重复,直到任何文本前面都没有制表符。然后才打印该行。这种类似循环的结构是使用“:”和“b”命令设置的。请注意,我在这里使用 \b (空白)和 \t (制表符)来使不同类型的空白可读。测试脚本时将它们替换为真正的空白/制表符:
sed ':start
/^\b*\t/ {
s/^\(\b*\)\t/\1\b\b\b\b\b\b\b\b/
b start
}'
2) 条件分支 现在,您知道如何设置标签并分支到它们,还有一个进一步的变化:您可以根据成功之前执行的 s/.../.../ 命令分支到这样的标签(这意味着:它改变了一些东西)或没有。执行此操作的命令是:
t [标签]
如果自读取最后一行以来或自上次执行 at 或 T 命令以来 s/../../- 命令之一成功替换,则分支到
T [标签]
这是“t”的否定对应词。它分支如果不s命令成功。
这是一个简单的例子,它是如何工作的。它没有做任何特别有用的事情,但它显示了原理:sed 脚本仅接受任何输入的第一行。如果第一个字符是“a”、“b”或“c”之一,则打印“YES”,否则打印“NO”。
sed -n 's/^[abc]/x/
t yes
b no
:no
s/^.*$/NO/p
q
:yes
s/^.*$/YES/p
q'
控制流程很简单:首先尝试进行替换,如果第一个字符是“a”、“b”或“c”,则将其更改为“x”。如果成功,分支命令“t yes”将分支到 lael“yes”,其中整行更改为“YES”并且 sed 退出(“q”命令)。如果此替换不成功,则传递“t”命令并执行“b”命令,跳转到“no”标签。
尝试各种输入(echo "..." | sed <script>
足够好)以查看其工作原理,然后将“t”命令更改为“T”并观察对结果的影响。
答案4
如果您需要做除此之外的任何事情s/old/new/
,那么您最好使用 awk 而不是 sed 来实现清晰度、稳健性、可移植性、可维护性等的某种组合:
$ seq 3 | awk 'sub(/2/,"B")'
B
$ seq 3 | awk 'sub(/2/,"B") { sub(/B/,"foo"); print }'
foo