用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命令的执行本身是成功的。
然而,有时,在我们执行了涉及替换的命令之后——例如,sed或awk—— 我们想通过快速检查返回码来了解替换是否成功。
因此,我们将解决如何让 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] ”一样,awk有exit [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命令
我们已经了解到awk的sub() 和*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命令:
接下来,让我们弄清楚命令是如何工作的。
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 ”路径几乎相同的操作,但我们不附加模式空间来保存空间。这是因为我们只是在变电站成功后才在保留空间中设置标记。
如果我们比较sed和awk解决方案,awk解决方案更容易理解。
所以,当输入是多行的时候,我们推荐使用 awk的方案来控制返回值。