我有一个输入文件,例如 ,file1
其显示如下:
WRITE=2
START=1
SMEAR=0
MIN=6
MAX=100
我的问题是搜索字符串SMEAR
,如果字符串存在,则用替换整行SMEAR=-5
。但是,如果SMEAR
不存在,则我需要插入新行SMEAR=-5
。最终该行SMEAR=-5
应该只出现一次。我可以使用 sed 替换字符串:
sed -i '/SMEAR/c\SMEAR=-5' file1
或者我可以将字符串作为新行插入
sed -i '10i SMEAR=-5' file1
但是我如何根据文件中字符串的存在来组合它们呢?
编辑:SMEAR
如果我可以将 的值作为变量 传递,也会很有帮助。
答案1
看起来行的顺序对于您的用例并不重要。鉴于此,我会ex
简单地使用:
- 删除
SMEAR
;的所有实例 - 插入你想要的行。
你可以这样做:
printf '%s\n' 'g/SMEAR/d' '$a' 'SMEAR=-5' . x | ex file.txt
第一个命令是g
全局d
删除与 regex 匹配的所有行/SMEAR/
。
下一个命令是a
在最后一行 ( $
) 之后追加该行SMEAR=-5
。要.
附加的文本结束。
该x
命令保存更改并退出。
每个命令都以换行符结束,使用printf '%s\n'
将它们发送到ex
。
另请参阅这个非常相似的解决方案这是我不久前在 vi/Vim 堆栈交换中写的。
要通过将更改的文件打印到命令行而不保存更改来测试更改,请将 替换为如下x
两个命令:%p 'q!'
printf '%s\n' 'g/SMEAR/d' '$a' 'SMEAR=-5' . %p 'q!' | ex file.txt
%
意思是“整个缓冲区”,这就是被p
打印的内容。
q!
意思是“退出,放弃改变”。
将更改保存到新的文件,将其替换%p
为w newfile.txt
:
printf '%s\n' 'g/SMEAR/d' '$a' 'SMEAR=-5' . 'w newfile.txt' 'q!' | ex file.txt
这w
会将修改后的缓冲区写入newfile.txt
.
或者,您可以在开始时执行此操作,进行备份,然后将更改的文件内容保存到原始位置,file.txt
如下所示:
printf '%s\n' 'w file.txt.bak' 'g/SMEAR/d' '$a' 'SMEAR=-5' . x | ex file.txt
编辑:实际上你不需要使用q!
;只需省略x
就足以避免保存更改。当ex
尝试读取更多输入时收到 EOF 时,它将退出,并且不会保存更改。
答案2
您可以这样尝试:如果一行匹配,只需将其复制到h
旧空间,然后s
替换该值。
在纬度$
线上x
更改保持空间和模式空间,然后检查后者是否为空。如果它不为空,则意味着替换已经完成,因此无需执行任何操作。如果它为空,则意味着未找到匹配项,因此将模式空间替换为SMEAR=-5
然后追加到保持缓冲区中的当前行。最后,ex
再次更改:
sed '/SMEAR/{h;s/.*/SMEAR=-5/};${x;/^$/{s//SMEAR=-5/;H};x}' infile
以上是gnu sed
语法。便携的:
sed '/SMEAR/{
h
s/.*/SMEAR=-5/
}
${
x
/^$/{
s//SMEAR=-5/
H
}
x
}' infile
替代方法ed
:
ed -s infile<<\IN || printf %s\\n SMEAR=-5 >> infile
/SMEAR.*/s//SMEAR=-5/
w
q
IN
如果没有行与模式匹配,ed
则会出错,因此printf...
将执行第二个命令 ( ) 将该行附加到文件中。否则ed
将就地编辑文件。
如果您将值SMEAR
保存在变量中,例如
var=-7
你会运行:
sed '/SMEAR/{h;s/.*/SMEAR='"$var"'/};${x;/^$/{s//SMEAR='"$var"'/;H};x}' infile
或者
ed -s infile<<IN || printf %s\\n "SMEAR=${var}" >> infile
/SMEAR.*/s//SMEAR=${var}/
w
q
IN
答案3
或者,实施通配符的想法
(删除包含 的任何/所有现有行SMEAR
,然后插入所需的行)使用石器时代工具:
(grep -v '^SMEAR=' file1; echo 'SMEAR=-5') > file1.tmp && cp file1.tmp file1 && rm file1.tmp
或者,如果您不关心保留 的属性(例如权限)和硬链接file1
,您可以这样做
(grep -v '^SMEAR=' file1; echo 'SMEAR=-5') > file1.tmp && mv file1.tmp file1
或者,如果你有sponge
,你可以这样做
(grep -v '^SMEAR=' file1; echo 'SMEAR=-5') | sponge file1