原始文件
claudio
antonio
claudio
michele
我只想将第一次出现的“claudio”更改为“claudia”,以便得到以下结果:
claudia
antonio
claudio
michele
我已经尝试过以下方法:
sed -e '1,/claudio/s/claudio/claudia/' nomi
上面的命令执行全局替换(它替换所有出现的“claudio”)。为什么?
答案1
如果您使用的是 GNU sed
,请尝试:
sed -e '0,/claudio/ s/claudio/claudia/' nomi
sed
不会开始检查结束范围的正则表达式,直到后开始该范围的线。
从man sed
(POSIX 联机帮助页,重点是我的):
具有两个地址的编辑命令应从与第一个地址匹配的第一个模式空间中选择包含范围下一个模式空间与第二个相匹配。
但该0
地址不是标准的,这是sed
任何其他实现都不支持的 GNU 扩展sed
。
使用awk
工作范围awk
更符合您的预期:
$ awk 'NR==1,/claudio/{sub(/claudio/, "claudia")} 1' nomi
claudia
antonio
claudio
michele
解释:
NR==1,/claudio/
这是一个从第 1 行开始到第一次出现 结束的范围
claudio
。sub(/claudio/, "claudia")
当我们在范围内时,将执行此替代命令。
1
这是 awk 的打印行的神秘简写。
答案2
新版本的 GNUsed
支持该-z
选项。
通常,sed 通过读取字符串直到行尾字符(换行符或回车符)来读取行。
GNU 版本的 sed 在 4.2.2 版本中添加了一个功能来使用“NULL”字符代替。如果您有使用 NULL 作为记录分隔符的文件,这会很有用。某些 GNU 实用程序可以生成使用 NULL 而不是新行的输出,例如“find . -print0”或“grep -lZ”。
sed
当您想要在不同的线路上工作时,可以使用此选项。
echo 'claudio
antonio
claudio
michele' | sed -z 's/claudio/claudia/'
回报
claudia
antonio
claudio
michele
答案3
总结
GNU 语法:
sed '/claudio/{s//claudia/;:p;n;bp}' file
或者甚至(仅使用一次要替换的单词:
sed '/\(claudi\)o/{s//\1a/;:p;n;bp}' file
或者,用 POSIX 语法:
sed -e '/claudio/{s//claudia/;:p' -e 'n;bp' -e '}' file
适用于任何 sed、进程仅有的查找第一个claudio
, 所需的行数尽可能多,即使claudio
位于第一行,并且由于仅使用一个正则表达式字符串而更短。
细节
仅更改一条线你需要选择只有一行。
使用1,/claudio/
(来自您的问题)选择:
- 从第一行开始(无条件)
- 到下一个包含字符串 的行
claudio
。
$ cat file
claudio 1
antonio 2
claudio 3
michele 4
$ sed -n '1,/claudio/{p}' file
claudio 1
antonio 2
claudio 3
选择任何包含 的行claudio
,使用:
$ sed -n `/claudio/{p}` file
claudio 1
claudio 3
并且仅选择第一的 claudio
在文件中,使用:
sed -n '/claudio/{p;q}' file
claudio 1
然后,您只能在该行上进行替换:
sed '/claudio/{s/claudio/claudia/;q}' file
claudia 1
这只会改变第一的行中出现正则表达式匹配,即使可能有多个第一的与正则表达式匹配的行。
当然,/claudio/
正则表达式可以简化为:
$ sed '/claudio/{s//claudia/;q}' file
claudia 1
然后,唯一缺少的是打印所有其他未修改的行:
sed '/claudio/{s//claudia/;:p;n;bp}' file
答案4
您可以使用awk
标志来了解替换是否已经完成。如果没有,请继续:
$ awk '!f && /claudio/ {$0="claudia"; f=1}1' file
claudia
antonio
claudio
michele