我sed
用于在运行时更新我的 JSON 配置文件。有时,当 JSON 文件中的模式不匹配时,sed
仍会以返回码 0 退出。
sed
返回 0 表示成功完成,但是如果没有找到正确的模式并更新文件,为什么返回 0 呢?有解决方法吗?
正如@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
在你的文本处理之前做一个......
这可能对您有用(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
这些答案都太复杂了。编写一些使用 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
对于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
下面是我们使用sed -rn
or的模式sed -r
。
整个搜索和替换命令(“s/.../.../...”)是可选的。如果使用搜索和替换,为了速度并且已经匹配 $matchRe,我们使用尽可能快的 $searchRe 值,使用 . 其中字符不需要重新验证,并且 .{$len} 用于模式的固定长度部分。
none found 的返回值为 $notFoundExit。
/$matchRe/{s/$searchRe/$replacement/$options; Q}; q$notFoundExit
出于以下原因:
改变 Q 命令的大小写会改变行为,具体取决于退出的时间。涉及将布尔逻辑应用于多行输入的行为要求解决方案更加复杂。
我们有上面的答案,但我花了一些时间才弄清楚发生了什么。我试图为像我这样的 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/}'
替换我多次使用的f
in 。foo
b
对于任意数量的输入行:
sed --quiet 's/hello/HELLO/;t1;b2;:1;h;:2;p;${g;s/..*//;tok;q1;:ok}'
在匹配时填充保持空间,并在最后一行之后检查它。1
如果文件中没有匹配项,则返回状态。
s/hello/HELLO
- 替换检查t1
1
-如果替换成功则跳转到标签b2
2
-无条件跳转到标签:1
- 标签1
h
- 复制模式以保留空间(替换成功时):2
- 标签2
p
- 无条件打印图案空间${ ... }
- 匹配最后一行,评估里面的块g
- 将保持空间复制到模式空间(如果之前第一次替换成功,则非空)s/..*//
- 虚拟替换,设置分支标志tok
- 跳转到标签ok
(如果虚拟替换在非空保持空间上成功)q1
- 以错误状态 1 退出:ok
- 标签ok
正如我们已经知道的那样,当 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。
我曾想通过在找到匹配项时退出来截断文件(并排除匹配的行)。当可能重新运行在文件末尾添加行的进程时,这很方便。"Q;Q1" 不起作用,而只是 "Q1" 起作用,如下所示:
如果 sed -i '/text I want to find/Q1' file.txt 然后在文件末尾插入空行 + 新行 fi 只插入没有空行的新行