Contents

用SED和awk 替换的返回值

1. 概述

在 Linux 中,我们可以通过检查命令的返回值来判断命令是否执行成功。通常,如果它返回一个非零数字,我们认为命令失败。

有时,当我们使用 sed 或 awk进行文本替换时, 我们想知道我们是否通过返回码在那里成功完成了替换。

在本教程中,我们将学习如何根据替换结果返回不同的值。

2. 问题介绍

首先,让我们看一个例子:

$ sed 's/Java/Kotlin/' <<< 'Java is awesome!' 
Kotlin is awesome!
$ echo "return code is $?"
return code = 0

如上例所示,我们已成功使用sed命令替换单行字符串中的文本。此外,不出所料,我们得到了零作为返回值。

接下来,我们再看一个例子:

$ sed 's/Windows/Linux/' <<< 'Java is awesome!'
Java is awesome!
$ echo "return code is $?"
return code = 0

这一次,我们尝试使用sed命令将“ Windows ”替换为“ Linux ” 。而且,由于输入字符串不包含我们要替换的模式(“ Windows ”),该命令不必执行任何替换,因此我们的sed输出字符串与输入字符串相同。

但是,正如我们将从示例中的echo输出中注意到的那样, sed命令的返回值仍然为零。这是因为,虽然替换 ( s/…/…/ ) 没有改变任何东西,但sed命令的执行本身是成功的

然而,有时,在我们执行了涉及替换的命令之后——例如,sedawk—— 我们想通过快速检查返回码来了解替换是否成功。

因此,我们将解决如何让 sed和 awk返回一个非零值,比如100,以在输入与搜索模式不匹配时指示不成功的替换

我们将涵盖两种情况:单行输入和多行输入。

此外,我们将在本教程中使用 GNU awk和 GNU sed,因为它们广泛用于 Linux 操作系统。

3. 单行输入

在编写 shell 脚本时,我们经常通过对单行字符串进行“搜索和替换”来更改变量的值。

3.1. 使用sed命令

sed命令有一个 ’ q ’ 命令来退出sed脚本并停止处理进一步的输入。GNU sed支持’ q ’ 命令的*[exit-code]*参数。

因此,我们可以通过调用’ q[exit-code] ‘命令来自定义返回值,例如:

$ sed 'q 100' <<< 'Java is awesome!' 
Java is awesome!
$ echo "return code is $?"
return code is 100

接下来,让我们检查当给定的 Regex 匹配模式空间 时,我们将执行替换并返回退出代码0,否则,我们退出sed处理,退出代码为100

$ sed '/Java/!{q100}; {s/Java/Kotlin/}' <<< 'Java is awesome!'
Kotlin is awesome!
$ echo "return code is $?"
return code is 0
$ sed '/Windows/!{q100}; {s/Windows/Linux/}' <<< 'Java is awesome!' 
Java is awesome!
$ echo "return code is $?"
return code is 100

我们已经解决了这个问题,正如我们在上面的输出中看到的那样。

3.2. 改进 sed命令

首先,让我们重新审视我们的sed解决方案。

我们在命令中重复了两次正则表达式。一个用于匹配检查,另一个用于*s/../../*命令。

将此命令包含在 shell 脚本中可以为进一步维护带来一些额外的努力。一旦我们需要调整正则表达式,我们必须进行两次精确的更改。

一个直接的改进是将正则表达式保存在一个变量中。然后,我们可以用变量执行 sed 替换。

实际上,我们可以使用sed的 ’ t ’ 命令来删除没有额外变量的正则表达式重复项:

$ sed 's/Java/Kotlin/; ta; q 100; :a' <<< 'Java is awesome!' 
Kotlin is awesome!
$ echo "return code is $?"
return code is 0
$ sed 's/Windows/Linux/; ta; q 100; :a' <<< 'Java is awesome!'
Java is awesome!
$  echo "return code is $?"
return code is 100

**如果先前的*s/../../*已成功替换, sed的“ ta”命令可以将处理分支 到标签(在本例中为“ a ”) 。

在我们上面的示例中,如果替换成功,sed将跳转到标签“ :a ”,这是sed命令的结尾 。因此,该命令以零退出。

但是,如果s/../../不成功,“ ta ”命令将不执行任何操作。因此,将执行“ q 100 ”,导致sed以代码100*退出。

3.3. 使用awk命令

sed的“ q [exit-code] ”一样,awkexit [expression]命令来退出 带有返回值的awk脚本,例如“ exit 100 ”。

此外,awk 具有*用于文本替换的sub() 和*gsub()函数。此外,函数将返回成功替换的次数

因此,我们可以通过检查*sub()gsub()*函数的返回值来快速确定替换是否成功:

$ awk '{ v=sub(/Java/, "Kotlin"); print } v==0 { exit 100 }' <<< 'Java is awesome!'
Kotlin is awesome!
$ echo "return code is $?"
return code is 0
$ awk '{ v=sub(/Windows/, "Linux"); print } v==0 { exit 100 }' <<< 'Java is awesome!' 
Java is awesome!
$ echo "return code is $?"
return code is 100

如我们所见,  awk解决方案比 sed解决方案更直接。

4. 多行输入

我们也经常对多行输入应用替换。让我们创建一个示例文件:

$ cat input.txt 
Java is a programming language.
Java is awesome!
Hello World.

接下来,让我们看看如何根据替换结果来控制返回值。

这次我们先用awk来解决问题。

4.1. 使用 awk命令

我们已经了解到awksub() 和*gsub()*函数返回成功替换的次数。

当我们处理多行输入时,我们可以累加每一行输入的替换函数的返回值,以确定是否至少有一个替换成功

$ awk '{ v += sub(/Java/, "Kotlin"); print } END{ if(v==0) exit 100 }' input.txt 
Kotlin is a programming language.
Kotlin is awesome!
Hello World.
$ echo "return code is $?"
return code is 0
$ awk '{ v += sub(/Windows/, "Linux"); print } END{ if(v==0) exit 100 }' input.txt 
Java is a programming language.
Java is awesome!
Hello World.
$ echo "return code is $?"
return code is 100

上面的awk命令与单行解决方案非常相似。只有两个变化:

  • 使用*v += sub()而不是v = sub()*来累加每一行的返回值
  • 将“ v==0 ”检查移动到END 块以确定处理完所有输入行后的退出代码

正如我们所见,  awk命令非常容易理解。因此,我们首先解决awk解决方案。

sed也可以解决这个问题。让我们看看它是如何完成的。

4.2. 使用sed命令

sed不支持声明变量和执行数学计算。因此,我们必须以不同的方式解决问题。

我们先来看看解决方法:

$ sed -n 's/Java/Kotlin/; tOK; bNOK; :OK;H; :NOK;p;${g;/./!{q100}}' input.txt 
Kotlin is a programming language.
Kotlin is awesome!
Hello World.
$ echo "return code is $?"
return code is 0
$ sed -n 's/Windows/Linux/; tOK; bNOK; :OK;H; :NOK;p;${g;/./!{q100}}' input.txt
Java is a programming language.
Java is awesome!
Hello World.
$ echo "return code is $?"
return code is 100

在我们浏览命令之前,我们需要了解一些sed命令:

  • b label – 跳转到给定标签而不检查之前的*s/../../*是否成功
  • H – 将模式空间 附加到保持空间
  • g – 将保持空间复制到模式空间

接下来,让我们弄清楚命令是如何工作的。

4.3. 了解sed解决方案

一张“流程图”可以帮助我们更容易地理解它:

                                                ┌──────────────────────┐
                          ┌───success─:OK──────>┤H; p; ${g;/./!{q100}}                          │             ^       └──────────────────────┘
                          │             │
sed -n 's/Java/Kotlin/;  tOK;  bNOK;  :OK;H; :NOK; p;${g;/./!{q100}}' input.txt
                                 │             │
                                 │             v       ┌───────────────────┐
                                 └─not success─┴:NOK──>┤ p;${g;/./!{q100}}                                                       └───────────────────┘

如上图所示,替换后有两条路径。我们先来看一下成功案例。

H;’ 命令将路由处理以执行 ’ H; ; ${g;/./!{q100}} ‘:

  • H – 将当前模式空间附加到保持空间,因此保持空间不再为空。我们使用这个技巧来标记是否至少有一个成功的替换
  • p – 打印模式空间
  • ${..  – 如果当前行是输入的最后一行
  • g – 将保持空间复制到当前模式空间,以便我们可以检查是否至少有一个成功的替换
  • .. /./!{q100} *} –*如果保留空间为空,我们认为没有成功替换,所以返回:q100

NOK ”路径类似于“ OK ”路径。’ bNOK; ’ 命令将分支到命令:’ p; ${g;/./!{q100}} ‘.

因此,我们将执行与“ OK ”路径几乎相同的操作,但我们不附加模式空间来保存空间。这是因为我们只是在变电站成功后才在保留空间中设置标记

如果我们比较sedawk解决方案,awk解决方案更容易理解。

所以,当输入是多行的时候,我们推荐使用 awk的方案来控制返回值