71

sed用于在运行时更新我的​​ JSON 配置文件。有时,当 JSON 文件中的模式不匹配时,sed仍会以返回码 0 退出。

sed返回 0 表示成功完成,但是如果没有找到正确的模式并更新文件,为什么返回 0 呢?有解决方法吗?

4

9 回答 9

73

正如@cnicutar 评论的那样,命令的返回码表示命令是否成功执行。与您在代码/脚本中实现的逻辑无关。

所以如果你有:

echo "foo"|sed '/bar/ s/a/b/'

sed 会返回0,但如果你写了一些语法/表达式错误,或者输入/文件不存在,sed 无法执行你的请求,sed 将返回 1。

解决方法

这实际上不是解决方法。sed 有q命令:(来自手册页):

 q [exit-code]

在这里,您可以根据需要定义退出代码。例如,如果不存在,'/foo/!{q100}; {s/f/b/}'将以代码 100 退出,否则执行替换 f->b 并以代码 0 退出。foo

匹配案例:

kent$  echo "foo" | sed  '/foo/!{q100}; {s/f/b/}'
boo
kent$  echo $?
0

不匹配案例:

kent$ echo "trash" | sed  '/foo/!{q100}; {s/f/b/}'
trash
kent$ echo $?
100

我希望这回答了你的问题。

编辑

我必须补充一点,上面的示例仅用于单行处理。我不知道你的具体要求。当你想得到exit 1。一行不匹配或整个文件。如果整个文件大小写不匹配,你可以考虑 awk,甚至grep在你的文本处理之前做一个......

于 2013-04-12T07:38:53.443 回答
56

这可能对您有用(GNU sed):

sed '/search-string/{s//replacement-string/;h};${x;/./{x;q0};x;q1}' file

如果search-string找到它,它将被替换为replacement-string并且在文件末尾sed将退出并0返回代码。如果没有进行替换,则返回码将为1.

更详细的解释:

在 sed 中,用户可以使用两个寄存器:将当前行加载到其中的模式空间 (PS)(减去换行符)和一个称为保持空间 (HS) 的备用寄存器,该寄存器最初是空的。

一般的想法是使用 HS 作为标志来指示是否发生了替换。如果 HS 在文件末尾仍然为空,则没有进行任何更改,否则发生了更改。

该命令/search-string/与 PS 中的任何内容匹配search-string,如果发现它包含search-string以下花括号之间的命令,则执行。

首先进行替换s//replacement-string/(sed 使用最后一个正则表达式,即search-string,如果左侧为空,s//replacement-string则与 相同s/search-string/replacement-string/),然后h命令复制 PS 并将其放入 HS。

sed 命令$用于识别文件的最后一行,然后发生以下情况。

首先该x命令交换两个寄存器,因此 HS 变为 PS,PS 变为 HS。

然后在 PS 中搜索任何字符/./.意味着匹配任何字符),记住 HS(现在是 PS)最初是空的,直到发生替换。如果条件为真,x则再次执行q0命令,该命令结束所有 sed 处理并将返回码设置为0. 否则x执行命令并将返回码设置为1.

注意虽然q退出 sed 处理它不会阻止 PS 被 sed 重新组装并按正常方式打印。

另一种选择:

sed '/search-string/!ba;s//replacement-string/;h;:a;$!b;p;x;/./Q;Q1' file

或者:

sed '/search-string/,${s//replacement-string/;b};$q1' file
于 2013-04-12T08:13:31.940 回答
31

这些答案都太复杂了。编写一些使用 grep 来确定要替换的东西是否存在然后使用 sed 替换它的 shell 脚本有什么问题?

grep -q $TARGET_STRING $file
if [ $? -eq 0 ]
then
    echo "$file contains the old site"
    sed -e "s|${TARGET_STRING}|${NEW_STRING}|g" ....
fi
于 2016-03-09T19:23:44.867 回答
4

对于1 行输入。为避免重复 /pattern/:

s替换成功时,用于t有条件地跳转到一个标签,例如x。否则使用q退出代码退出,例如100

's/pattern/replacement/;tx;q100;:x'

例子:

$ echo 1 > one
$ < one sed 's/1/replaced-it/;tx;q1;:x'
replaced-it
$ echo $?
0
$ < one sed 's/999/replaced-it/;tx;q100;:x'
1
$ echo $?
100

https://www.gnu.org/software/sed/manual/html_node/Branching-and-flow-control.html

于 2021-02-25T08:57:25.883 回答
2

下面是我们使用sed -rnor的模式sed -r

整个搜索和替换命令(“s/.../.../...”)是可选的。如果使用搜索和替换,为了速度并且已经匹配 $matchRe,我们使用尽可能快的 $searchRe 值,使用 . 其中字符不需要重新验证,并且 .{$len} 用于模式的固定长度部分。

none found 的返回值为 $notFoundExit。

/$matchRe/{s/$searchRe/$replacement/$options; Q}; q$notFoundExit

出于以下原因:

  • 无需浪费时间测试匹配和不匹配的案例
  • 无需浪费时间复制到缓冲区或从缓冲区复制
  • 没有多余的分支
  • 合理的灵活性

改变 Q 命令的大小写会改变行为,具体取决于退出的时间。涉及将布尔逻辑应用于多行输入的行为要求解决方案更加复杂。

于 2017-06-28T06:51:34.247 回答
2

我们有上面的答案,但我花了一些时间才弄清楚发生了什么。我试图为像我这样的 sed 基本用户提供一个简单的解释。

让我们考虑这个例子:

echo "foo" | sed  '/foo/!{q100}; {s/f/b/}'

这里我们有两个 sed 命令。第一个是'/foo/!{q100}'此命令实际检查模式匹配,100如果不匹配则返回存在代码。考虑以下示例,-n用于使输出静默,因此我们仅获得现有代码。

此示例 foo 匹配,因此退出代码返回为0

echo "foo" | sed -n '/foo/!{q100}'; echo $?
0

此示例输入是foo,我们尝试匹配,因此不返回boo匹配和退出代码100

echo "foo" | sed  -n '/boo/!{q100}'; echo $?
100

因此,如果我的要求只是检查模式匹配与否,我可以使用

echo "<input string>" | sed -n '/<pattern to match>/!{q<exit-code>}'

更多示例:

echo "20200206" | sed -n '/[0-9]*/!{q100}' && echo "Matched" || echo "No Match"
Matched

echo "20200206" | sed -n '/[0-9]{2}/!{q100}' && echo "Matched" || echo "No Match"
No Match

第二个命令是'{s/f/b/}'替换我多次使用的fin 。foob

于 2020-02-05T23:56:51.980 回答
2

对于任意数量的输入行:

sed --quiet 's/hello/HELLO/;t1;b2;:1;h;:2;p;${g;s/..*//;tok;q1;:ok}'

在匹配时填充保持空间,并在最后一行之后检查它。1如果文件中没有匹配项,则返回状态。

  • s/hello/HELLO- 替换检查
  • t11-如果替换成功则跳转到标签
  • b22-无条件跳转到标签
  • :1- 标签1
  • h- 复制模式以保留空间(替换成功时)
  • :2- 标签2
  • p- 无条件打印图案空间
  • ${ ... }- 匹配最后一行,评估里面的块
  • g- 将保持空间复制到模式空间(如果之前第一次替换成功,则非空)
  • s/..*//- 虚拟替换,设置分支标志
  • tok- 跳转到标签ok(如果虚拟替换在非空保持空间上成功)
  • q1- 以错误状态 1 退出
  • :ok- 标签ok
于 2021-04-05T08:07:07.627 回答
1

正如我们已经知道的那样,当 sed 匹配失败时,它只是简单地返回其输入字符串——没有发生错误。输入和输出字符串之间的差异确实意味着匹配,但匹配并不意味着字符串之间存在差异;毕竟 sed 可以简单地匹配所有输入字符。该缺陷是在以下示例中创建的

h=$(echo "$g" | sed 's/.*\(abc[[:digit:]]\).*/\1/g')    
if [ ! "$h" = "$g" ]; then    
  echo "1"   
else    
  echo "2"    
fi

whereg=Xabc1给出 1,而 settingg=abc1给出 2;然而这两个输入字符串都由 sed 匹配!因此,很难确定 sed 是否匹配。一个解法:

h=$(echo "fix${g}ed" | sed 's/.*\(abc[[:digit:]]\).*/\1/g')
if [ ! "$h" = "fix${g}ed" ]; then    
  echo "1"   
else    
  echo "2"    
fi

在这种情况下,如果 sed 匹配,则打印 1。

于 2018-02-02T15:11:13.530 回答
0

我曾想通过在找到匹配项时退出来截断文件(并排除匹配的行)。当可能重新运行在文件末尾添加行的进程时,这很方便。"Q;Q1" 不起作用,而只是 "Q1" 起作用,如下所示:

如果 sed -i '/text I want to find/Q1' file.txt 然后在文件末尾插入空行 + 新行 fi 只插入没有空行的新行

于 2015-09-11T15:20:07.167 回答